Git
WinMerge
WSL

WSL の Git で diff / merge に WinMerge を使うための設定

WSL の Git で WinMerge を diff / merge ツールとして使用する方法のメモです。基本的な考え方は Cygwin の場合と同じですが,ファイルシステムの問題やパスの取り扱いの違いに対応するため,以下のような変更・追加をしてやる必要があります。


  • WinMerge のパスを WSL から見たものにする。

  • パスの変換には cygpath ではなく wslpath を使用する。


  • $TMPDIR を設定して一時ファイルの出力先を Windows のファイルシステム(DrvFs)上のディレクトリにする。


  • --dir-diff には --no-symlinks を併用する。


外部 diff ツールとしての設定


Git Config

WSL の Git で WinMerge を diff ツールとして使うには .gitconfig に以下の内容を追加します。Git から渡される変数($MERGED など)や WinMerge に指定しているオプションの意味は以前に投稿した記事を参照してください。


.gitconfig

[diff]

tool = WinMerge

[difftool]
prompt = false

[difftool "WinMerge"]
cmd = '/mnt/c/Program Files/WinMerge/WinMergeU.exe' -e -r -u -x -wl -wr -dl \"a/$MERGED\" -dr \"b/$MERGED\" \"$(wslpath -am \"$LOCAL\")\" \"$(wslpath -am \"$REMOTE\")\"
trustExitCode = false


この設定で git difftool を引数なしで実行すると,左側ペインにインデックス(差分適用前)の内容が,右側ペインにワーキングツリー(差分適用後)の内容が表示されます。

001.png

Cygwin で WinMerge を使う場合との違いは以下の通りです。


  • WinMerge の実行ファイルのパスが WSL のものになっている。

  • パスの変換に cygpath ではなく wslpath を使用している。

WSL から見た UNIX 形式のパスを Windows 形式のパスに変換するには wslpath コマンドを使います。使い方は cygpath とほとんど同じです。ここではバックスラッシュの代わりにスラッシュを使う -m オプションと結果を絶対パスで返す -a オプションを指定しています。wslpath-h--help を付けても使い方が表示されないのですが,Build 17046 の Release Notes に簡単な説明があります。


$TMPDIR の設定

git difftool は,比較対象のファイルを /tmp にチェックアウトして,その一時ファイルのパスを外部ツールに渡します。チェックアウト先の /tmp は WSL のファイルシステム(VolFs)上にあるため,Windows ネイティブアプリである WinMerge からは直接アクセスできませんし,wslpath もパスの変換に失敗してしまいます。

$ git difftool

wslpath: /tmp/XXXXXX_sample.txt: Result not representable

002.png

WinMerge からアクセスできるように,一時ファイルのチェックアウト先を Windows のファイルシステム(DrvFs)上のディレクトリに変更してやる必要があります。

一時ファイルの出力先は $TMPDIR 環境変数で指定することができます。.bash_profile などに以下の記述を追加してください。バックスペースや CR の処理を挟むので少し長くなっていますが,Windows の環境変数 %TMP% の値を読みだして WSL 形式のパスに変換してから $TMPDIR に設定しているだけです。


.bash_profile

export TMPDIR=$(cmd.exe /C 'echo %TMP%' | sed -e 's/\\/\\\\/g' | tr -d '\r' | xargs wslpath -au)


あらかじめ Windows の環境変数として %TMPDIR% を定義しておいて,Build 17063 で導入された %WSLENV% を使って WSL に引き継いでもよいと思います。たとえば以下のように Windows の環境変数を設定します。%WSLENV%/u フラグは Windows から WSL へ環境変数を引き継ぐことを,/p フラグは環境変数の値はパスであるため変換が必要なことをそれぞれ意味しています。

環境変数

TMPDIR
%TEMP%

WSLENV
TMPDIR/up


ディレクトリ比較の設定

git difftool には --dir-diff というオプションがあり,ディレクトリ比較をすることができます。通常,git difftool はファイルを 1 つずつ順番に開いて差分を表示していきますが,--dir-diff オプションを付けるとディレクトリ比較となり,変更のあるファイルがツリー形式で一覧表示されるようになります。もちろん,必要であれば個別のファイルを開いて差分を確認することもできます。

003.png

ディレクトリ比較を実行すると,一時ディレクトリに leftright というディレクトリが作成され,変更のあったファイルがチェックアウトされます。このとき,right の内容がワーキングツリーと一致していれば,ファイルのコピーではなくシンボリックリンクが作られます。

この仕様はワーキングツリーのファイルをその場で修正できるため便利なのですが,WSL から WinMerge を使う場合には問題が起こります。WSL で作成されたシンボリックリンクは Windows ネイティブアプリの WinMerge からは見えないため,ファイルが見つからずエラーになってしまうのです。

004.png

この問題を解決するためには,--dir-diff と同時に --no-symlinks を指定して,シンボリックリンクを作るのではなく,ファイルをコピーするよう Git に指示します。以下のように alias に登録してしまうとよいと思います。


.gitconfig

[alias]

dd = "difftool --dir-diff --no-symlinks"
ddc = "difftool --dir-diff --cached --no-symlinks"

なお,Windows 環境では --no-symlinks がデフォルトになっているため,Git for Windows や Cygwin の Git ではこの問題は起こりません。


外部 merge ツールとしての設定

merge ツールの設定は以下の通りです。実行ファイルのパスが WSL のものになっている以外は Cygwin での設定とまったく同じです。


.gitconfig

[merge]

tool = WinMerge

[mergetool]
prompt = false
keepBackup = false

[mergetool "WinMerge"]
cmd = '/mnt/c/Program Files/WinMerge/WinMergeU.exe' \"$MERGED\"
trustExitCode = false


競合マーカーをもとに内容を分割して,左側ペインに --theirs の内容(マージするブランチ)を,右側ペインに --ours の内容(マージを受け入れるブランチ)を表示します。

005.png


参考