git difftool --dir-diff が便利すぎて泣きそうです

Git の 1.7.11 から git difftool コマンドに --dir-diff というオプションが追加されたのですが、これがライフ チェンジングだと思ったので紹介します。

--dir-diff 登場以前の git difftool は「ファイルごとに順番に差分を表示していく」ことしかできず、使い勝手はいまいちでした。それが、--dir-diff オプションの登場で状況が一変したわけです。

こんな感じの使い心地だよ

ある Git レポジトリーで dir1/a.txtdir2/c.txt を編集したとしましょう。

この状態で git difftool --dir-diff または git difftool -d を実行してみると・・・。

はい、差分のあるファイルが一覧で表示されました。

(difftool に WinMerge を設定して、メニューから [ツリー表示] を有効にしたときの表示例です。設定方法は後述します)

個別のファイルの diff を見る

a.txt を選択すると、新しいタブが開いて、a.txt の差分が表示されます。

カラフルに色分けされているし、ショートカットキーで前後の差分に移動できるので便利です (WinMerge の場合は Alt + ↑ と Alt + ↓)。

当然、タブを移動して差分一覧に戻れば、c.txt の差分も表示できます。

コマンドラインの git diff に比べて、

  • 確認するファイルを選びやすい
  • 差分間の移動がキーボードでやりやすい
  • ファイル全体が表示されているので、差分から前後に好きなだけたどっていける

といったところが嬉しいですね。

おっと、ここまでなら、GUI な Git クライアントでも同じようなことはできますね。便利なのはこの次です。

ファイル編集も!

なんと、右側のファイルを編集して保存すると、ワーキング ディレクトリーに反映されるのです (ただし、右側のファイルがワーキング ディレクトリーのファイルと同じ内容のときのみ)。

これがとてつもなく便利です。

差分を見ながら、「この差分は不要」とか「typo 発見」とか気づいたときに、その場で修正ができちゃうわけです。

比較対象は無限大

git difftool で比較対象を指定する方法は git diff とまったく同じです。

たとえば、git difftool -d master...topic として、トピックブランチでの変更点をまとめて閲覧したりもできるわけです。git difftool -d --cached としてインデックスとの差分を確認できるわけです。任意のコミット間の差分も確認できるわけです。タグがうってあれば、特定のバージョン間の差分も確認できるわけです

うれしいですね。

裏側で起こっていること

git difftool -d を実行したときに、裏側で何が起こっているのでしょうか。

内部的に git diff を呼び出して、出てきたファイルを一時ディレクトリーにチェックアウトしています。

たとえば、先ほどの例でいうと

Temp/git-difftool.VoxJJ/
    left/
        dir1/
            a.txt
        dir2/
            c.txt
    right/
        dir1/
            a.txt
        dir2/
            c.txt

といった構造になります。中身が同一なファイルはチェックアウトされないので、大きなレポジトリーでも安心です。

さらに、Mac や Linux では、right のファイルがワーキング ディレクトリーと同じなら、「ワーキング ディレクトリーへのシンボリックリンク」になっています。その結果、right のファイルを書き換えると、即時にワーキング ディレクトリーに反映されるわけです。

Windows の場合は、difftool を終了したときに、ワーキング ディレクトリーに一時ファイルを書き戻す動作になっています。ちょっと不便なので、シンボリックリンクを使うように改造したいところです。(追記) 改造しました!! Windows でも git difftool --dir-diff でシンボリックリンクを使う方法

使えるようにするまでの準備

便利なのは分かってもらえたと思うので、使えるように準備してみましょう!

最新の git-difftool を取得しておく

先ほど書いた通り、git difftool --dir-diff は 1.7.11 から追加されています。ただ、最近になっていくつか大きなバグが修正されているので、なるべく新しいヤツ (Linux なら 1.8.3、Windows なら 1.8.3.2 ) を利用するのがオススメです。

Git 全体を更新するのが面倒な場合は、git-difftool だけを

から落としてきて、libexec/git-core/git-difftool に上書きすれば、よほど古いバージョンじゃなければ動く・・・と思います。

difftool の設定: WinMerge 篇

Windows で WinMerge を使う場合は、.gitconfig に次のように書きます (Git for Windows の場合)。

[diff]
    tool = winmerge
[difftool winmerge]
    path = C:/Program Files (x86)/WinMerge/winmergeu.exe
    cmd = \"C:/Program Files (x86)/WinMerge/winmergeu.exe\" -r -u \"$LOCAL\" \"$REMOTE\"

インストール先が異なる場合は適宜修正してください。

コマンドライン オプションの意味は次の通りです。

  • -r: 再帰的に比較する
  • -u: 最近開いた一覧に追加しない

人によっては -e を追加して ESC で WinMerge を閉じられるようにしているようですが、複数タブを開いてるときに ESC を押して WinMerge が閉じてしまうと困るので私は設定していません。

difftool の設定: meld 篇

Mac や Linux で meld を利用する場合は、.gitconfig に次のように書きます。

[diff]
    tool = meld

簡単ですね。

Git 本体に meld のコマンドラインオプションの情報が入ってるので、これで動くようです。

(手元に Mac がないので動作確認はできませんでしたが、git - View differences of branches with meld? - Stack Overflow に同じ手順が書いてあって、プラス評価がついてるので正しいはずです)

Pro Git には P4Merge を使う手順 が書いてあるんだけど、P4Merge はディレクトリーの比較に対応してないので、--dir-diff には活用できません。

エイリアスを設定する

このあたりは好みですが、.gitconfig でエイリアスを設定しておくと便利でしょう。

[alias]
    d = difftool -d
    dc = difftool -d --cached
    dp = difftool -d HEAD~

git d <branch> とか git d HEAD のように入力するだけで、difftool が --dir-diff で立ち上がるようになります。

まとめ

difftool --dir-diff で快適な Git 生活を!