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 に指定しているオプションの意味は以前に投稿した記事を参照してください。
[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
を引数なしで実行すると,左側ペインにインデックス(差分適用前)の内容が,右側ペインにワーキングツリー(差分適用後)の内容が表示されます。
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
WinMerge からアクセスできるように,一時ファイルのチェックアウト先を Windows のファイルシステム(DrvFs)上のディレクトリに変更してやる必要があります。
一時ファイルの出力先は $TMPDIR
環境変数で指定することができます。.bash_profile
などに以下の記述を追加してください。バックスペースや CR
の処理を挟むので少し長くなっていますが,Windows の環境変数 %TMP%
の値を読みだして WSL 形式のパスに変換してから $TMPDIR
に設定しているだけです。
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
オプションを付けるとディレクトリ比較となり,変更のあるファイルがツリー形式で一覧表示されるようになります。もちろん,必要であれば個別のファイルを開いて差分を確認することもできます。
ディレクトリ比較を実行すると,一時ディレクトリに left
,right
というディレクトリが作成され,変更のあったファイルがチェックアウトされます。このとき,right
の内容がワーキングツリーと一致していれば,ファイルのコピーではなくシンボリックリンクが作られます。
この仕様はワーキングツリーのファイルをその場で修正できるため便利なのですが,WSL から WinMerge を使う場合には問題が起こります。WSL で作成されたシンボリックリンクは Windows ネイティブアプリの WinMerge からは見えないため,ファイルが見つからずエラーになってしまうのです。
この問題を解決するためには,--dir-diff
と同時に --no-symlinks
を指定して,シンボリックリンクを作るのではなく,ファイルをコピーするよう Git に指示します。以下のように alias に登録してしまうとよいと思います。
[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 での設定とまったく同じです。
[merge]
tool = WinMerge
[mergetool]
prompt = false
keepBackup = false
[mergetool "WinMerge"]
cmd = '/mnt/c/Program Files/WinMerge/WinMergeU.exe' \"$MERGED\"
trustExitCode = false
競合マーカーをもとに内容を分割して,左側ペインに --theirs
の内容(マージするブランチ)を,右側ペインに --ours
の内容(マージを受け入れるブランチ)を表示します。