Vimでgitのログをきれいに表示する

この記事はVim Advent Calendar 2012の161日目の記事です。
160日目はrbtnnさんによるEffective NeoBundle -- autoload関数を理解しNeoBundleを使いこなすための8の方法 --でした。

Inspired by ujihisa's VAC

本記事は、Vim Advent Calendar 2012 143日目にujihisaさんが書いた記事 撮った動画にインスパイアされて書いた記事です。ujihisa++

ujihisaさんの動画はこちら

ujihisaさんの動画を見て、GitLogViewer便利と思って早速設定してみました。 たしかに便利なんですが、コミットメッセージが表示されないので いちいち折畳を開かないと何のコミットなのかわからなくてちょっとめんどうだと思いました。 tigのメイン画面みたいに表示できたらもっと便利なのになーと思って ちょこちょこいじってたらそれっぽいものができました。 なので、本来今日上げようと思っていた記事を放り投げてこのネタで VACを書くことにしました。 本来今日あげようと思っていた記事は別の機会に載せます。

ちなみにtigって何?という方のために一応説明しておくと、 CUI上でGitのログとかdiffとかを見れるGitブラウザってやつですね。 紹介記事はWebに色々上がっているので詳しく知りたい方はググっていただきたいですが、 例えば以下の記事を見たらなんとなくイメージが掴めるかと思います。
CUI な Git ブラウザ tig を入れてみた

設定を載せる前に、実際の表示される画面の画像を載せておきます。

f:id:deris:20130510001655p:plain

画面の上部ウィンドウがgitのログを折りたたんで表示したものです。 流石にマージのグラフ表示とか色付けとかできてませんが、tigのメイン画面そっくりですね。 Date,Author,Commit Titleの順で並んでます。

折畳なのでzoなどで開けば以下の画像のように各コミットの中身が見れます。diffが見れて便利。 折畳を閉じる時はzcですね。

f:id:deris:20130510001703p:plain

以下の設定をすることで表示できます。元ネタのujihisaさんの設定もそうですが、動作にはvimprocが必要です。

" Inspired by ujihisa's vimrc
function! s:GitLogViewer()
  " vnewだとコミットメッセージが切れてしまうのでnew
  new
  VimProcRead git log -u 'ORIG_HEAD..HEAD'
  set filetype=git-log.git-diff
  setlocal foldmethod=expr
  setlocal foldexpr=getline(v:lnum)=~'^commit'?'>1':getline(v:lnum+1)=~'^commit'?'<1':'='
  setlocal foldtext=FoldTextOfGitLog()
endfunction
command! GitLogViewer call s:GitLogViewer()

" git log表示時の折りたたみ用
function! FoldTextOfGitLog()
  let month_map = {
    \ 'Jan' : '01',
    \ 'Feb' : '02',
    \ 'Mar' : '03',
    \ 'Apr' : '04',
    \ 'May' : '05',
    \ 'Jun' : '06',
    \ 'Jul' : '07',
    \ 'Aug' : '08',
    \ 'Sep' : '09',
    \ 'Oct' : '10',
    \ 'Nov' : '11',
    \ 'Dec' : '12',
    \ }

  if getline(v:foldstart) !~ '^commit'
    return getline(v:foldstart)
  endif

  if getline(v:foldstart + 1) =~ '^Author:'
    let author_lnum = v:foldstart + 1
  elseif getline(v:foldstart + 2) =~ '^Author:'
    " commitの次の行がMerge:の場合があるので
    let author_lnum = v:foldstart + 2
  else
    " commitの下2行がどちらもAuthor:で始まらなければ諦めて終了
    return getline(v:foldstart)
  endif

  let date_lnum = author_lnum + 1
  let message_lnum = date_lnum + 2

  let author = matchstr(getline(author_lnum), '^Author: \zs.*\ze <.\{-}>')
  let date = matchlist(getline(date_lnum), ' \(\a\{3}\) \(\d\{1,2}\) \(\d\{2}:\d\{2}:\d\{2}\) \(\d\{4}\)')
  let message = getline(message_lnum)

  let month = date[1]
  let day = printf('%02s', date[2])
  let time = date[3]
  let year = date[4]

  let datestr = join([year, month_map[month], day], '-')

  return join([datestr, time, author, message], ' ')
endfunction

一応gistにもあげておきました。

この設定を書いて、おもむろにGitLogViewerコマンドを実行すると上の画像のように 別ウィンドウでgitのログが表示されます。
ちなみにvimのカレントディレクトリ(:pwdで表示されるディレクトリ)でgit logコマンドを 実行するので、当たり前ですが、カレントディレクトリはgitで管理している ルートディレクトリ(.gitがあるディレクトリ)以下である必要があります。 必要に応じて:lcd,:cdなどで移動しておきましょう。
自動でプロジェクトルートディレクトリに移動してくれるvim-rooterなんて pluginもあるので必要に応じて入れておくといいと思います。

あと、ujihisaさんの動画でご紹介の通り、motemenさんのgit-vimという pluginを入れておくとGitLogViewerで表示されたウィンドウでの差分などの表示が 良い感じに色付けして表示されるのでこちらも必要に応じて入れておきましょう。 (というか元々は、git-vimで設定している FileTypegit-logをGitLogViewerで設定しているだけなんですけどね)

私は上記設定の他にautocmd FileType git-logでmotemenさんのgit-vimGitLogコマンドの出力も折畳で表示するようにしていますが、オプションによっては うまく表示されない可能性があるので、その設定は載せてません。

他にやりたいこと

大分きれいに表示されて自分的には満足なんですが、出来れば以下も設定したいなぁ。

  • tigみたいにブランチの位置も表示
  • 色付け(折畳表示なのでたぶん無理)

やるかどうかは未定。

おわり

折畳は普段あまり便利だと思ったことはないんですが、今回のように表示専用で 使う分にはスッキリ表示できて便利ですね。

折畳の設定などの普段使わない設定や、今回紹介したpluginなど新たな発見が あるのでvimrc読書会に参加することをおすすめします。
毎週土曜日23時からやってます。
ちなみに、5/11(土)は私のvimrc(後半)です。gkbr

それではVim Advent Calendar 2012の161日目を終わります。

明日のVim Advent Calendar 2012はtyruさんです。

追記(2013/5/10)

  • 今回載せた設定でGitLogViewerを実行した場合、本文中の画像と異なり、 ログが表示されるウィンドウの先頭行に空行が出ると思います。 本文中に載せた画像は自分が別で設定したmotemenさんのGitLogコマンドで 出力したもの折畳表示したものだったため微妙な差異が出てしまいました。すいません。
  • 本文中に補足するのを忘れていましたが、記事に載せた設定はgit log -u 'ORIG_HEAD..HEAD'としてます。 つまり、ORIG_HEADとHEADの間に何もなければ何も表示されません。必要に応じて書きかえてください。