External Diffのはなし


これはTortoiseHg Advent Calender 2012 18日目の記事です

※以下の内容は、Windowsのみを対象にしています。LinuxやMacの場合はいろいろ違うところがあると思います。

2012.12.27 修正
diffargsのプレースフォルダがまちがっていたので訂正しました
$plabel → $plabel1

イントロダクション

Merucurialで外部Diffツールを使いたいときは、ExtDiff Extensionを使って、こんな感じで設定する訳ですが、

hgrc
[extdiff]
# 外部Diffツールのパスを指定
cmd.winmerge = C:\Program Files\WinMerge\WinMergeU.exe
# 渡すオプションを指定
opts.winmerge = /s /e

TortoiseHgもこの設定は見てくれるのですが、TortoiseHgの場合だと、Diffツールがインストールしてさえいれば、何の設定もしなくても使えるようになってますよね?
あれはどこに設定されているんでしょうか?

ぐぐってもなかなかそれらしい情報は見つからないんですが、TortoiseHgのインストールフォルダをGrepしてみると、どうやら hgrc.d\MergeTools.rc というファイルに書かれているようです。
以下一部抜粋。

MergeTools.rc
[merge-tools]
; Windows version of BeyondCompare 3
beyondcompare3.priority=-1
beyondcompare3.args=$local $other $base /mergeoutput=$output /ro /lefttitle=parent1 /centertitle=base /righttitle=parent2 /outputtitle=merged /automerge /reviewconflicts /solo
beyondcompare3.premerge=False
beyondcompare3.regkey=Software\Scooter Software\Beyond Compare 3
beyondcompare3.regkeyalt=Software\Wow6432Node\Scooter Software\Beyond Compare 3
beyondcompare3.regname=ExePath
beyondcompare3.gui=True
beyondcompare3.diffargs=/lro /lefttitle='$plabel1' /righttitle='$clabel' /solo /expandall $parent $child
beyondcompare3.diff3args=$parent1 $parent2 $child /lefttitle='$plabel1' /centertitle='$clabel' /righttitle='$plabel2' /solo /ro
beyondcompare3.dirdiff=True

ツール名.オプション = 値 という形式で指定していく訳ですね。
どんなオプションがあって、それぞれ指定するとどうなるのかをちょっと見てみました。

merge-toolsオプション

[2012.12.19 追記] この辺のオプションについては、 hg help config にけっこうしっかり書いてましたw 気づいてなかった・・・

実行ファイルの検索につかう設定

regkey, regkeyalt, regname, regappend

ツールのフルパスを探し出すためのレジストリ情報を指定します。こんな感じのルールみたい。

  1. HKEY_LOCAL_MACHINE{regkey} の、{ragname} の値をフルパスとして取得
    {regname} が指定されていない場合は(規定)の値?
    なければHKEY_CURRENT_USERも見る。

  2. {regkey}に該当するキーがなければ、{regkeyalt}の方で試す

  3. {regappend}が指定されている場合は、その値をフルパスに追加する
    (レジストリにはフォルダパスが記述されていて、exeファイル名を付加しないとならない場合に使う)

executable

レジストリからフルパスを取れない場合はこっちで直接フルパスを指定します。

差分表示に関する設定

diffargs

差分表示の時に渡す引数です。
使えるプレースホルダは、以下の通り

プレースホルダ 説明
$parent または $parent1 比較元のパス
$child 比較先のパス
$plabel1 比較元の表示名
$clabel 比較先の表示名

diff3args

3way差分表示の時に渡す引数です。
3way差分表示に対応しているツールの場合は、マージリビジョンを選択して「親リビジョンとの差分」を表示したときに両方の親との差分が一度に見れる訳ですね。
※3way未対応の場合は1つ目の親リビジョンとの差分だけが表示されます。

使えるプレースホルダは、こうなります。

プレースホルダ 説明
$parent1 比較元1のパス
$parent2 比較元2のパス
$child 比較先のパス
$plabel1 比較元1の表示名
$plabel2 比較元2の表示名
$clabel 比較先の表示名

dirdiff

True or Falseを指定します。
ディレクトリどうしの比較ができる場合はTrueを指定します。

リビジョン単位での差分をみる場合、ディレクトリ比較ができるツールの場合は直接ツールが起動されます。
できないツールの場合は、代わりにこんな画面が表示されます。

dir3diff

dirdiffの3way版のようです。

usewin

Trueを指定すると、dirdiff、dir3diffがTrueの場合でも上の「GUI差分表示」画面が表示されるようになります。

マージに関する設定

args

マージの時に渡す引数です。

プレースホルダ 説明
$base 分岐元のファイルのパス
$local マージされるファイルのパス
$other マージするファイルのパス
$output 出力ファイルパス

gui

GUIのあるツールの場合はTrueです。バッチで処理する時に間違ってGUIが起動されてしまわないようにするガード用です。

binary

バイナリファイルのマージができるツールの場合はTrueです。

fixeol

改行コードを勝手に変えちゃう可能性のあるツールの場合は、Trueを指定しておくとマージ後に直してくれるようです。

checkchanged

Trueを指定すると、処理後にファイルが変更されているかどうかのチェックを行います。

checkconflicts

Trueを指定すると、処理後にコンフリクトマーカーが消えているかどうかのチェックを行います。

premerge

Trueを指定すると、ツール起動前にinternal:merge相当の処理を実行して、失敗したときだけツールを起動するようになります。
Falseを指定した場合はいきなりツールを起動します。

と思いきや、 keep という文字列を指定することもできるようになっているようです。
keep を指定すると、Trueを指定した時と同じように先に internal:merge を実行するのですが、失敗した場合に、 出力ファイルにコンフリクトマーカを埋め込んだままで マージツールを起動します。
(Trueを指定した場合は、マージツール起動前に出力ファイルはinternal:merge前の状態に戻されます)

これを有効にした場合、 checkchangedではマージが成功したかどうか判断できなくなるので、適切な戻り値を返さないツールについてはcheckconflictsもあわせて指定する必要があります。

設定の調整

という訳で、もしプリセットされたツール設定が気に入らない場合は、このファイルから Mercurial.iniとかに設定をコピーしてきて、そこで修正すればOKです。

試しに、 僕が主に使っているWinMergeUの設定をいろいろ変えてみます。

起動オプションの変更

diffargに、 /e というオプションが指定されています。
これは、 Escキーでプログラムを終了するという指定なのですが、ファイル単位の比較の時はともかくディレクトリ比較の時にはとても使いづらい(いくつもファイルを開くので、Escキーでは現在のファイルを閉じるだけにしてほしい)ので、このオプションを外します。

GUI差分表示画面の利用

ディレクトリ比較で直接ツールが起動されるのは確かに手軽なんですが、実は僕はあまり好きではありません。
というのは、そのモードだと、作業領域とあるリビジョンを比較したときに、ツール上で作業領域のファイルを修正することができなくなってしまうんですね。
(ディレクトリ比較モードだと、作業領域のファイルを直接開くのではなくてTempにコピーして開くため。もしかするとLinux版ではシンボリックリンクでよろしくやってくれるのかも)

差分ツール上で変更箇所を確認しながらコメントを書き足すとかよくやるのでこれはちょっと困る、ということで usewin = True を指定します。

手抜きマージ

WinMergeも一応3wayマージの機能は持っているんですが、kdiff3などの本格的3wayマージツールの機能と比べてどうしても見劣りします。
この際、3wayマージはすっぱりとあきらめて(3wayマージが必要なときは他のツールを使うことにして)、WinMergeは簡易コンフリクト解消に特化する形の設定にすることにします。

WinMergeにはコンフリクトファイルを開く機能がある(コンフリクトマーカーを読み取って、localとotherを左右に振り分けて表示できる)ので、この機能を利用します。

オプション
premerge keep
args /e $output
checkconflicts True

まず、premerge = keep とすることで、ファイルにコンフリクトマーカーを埋めたままWinMergeに渡せるようにします。
起動時の引数としては、単にコンフリクトファイルを渡すだけでいいので、 args = /e $output となります。
さっき書いたとおり、 premerge = keep の場合は checkchanged は効かなくなるので、 checkconflictsを使うようにします。

これで、手抜き版の設定ができました。3wayマージとくらべると情報量は減りますが、コンフリクトした個所だけをチェックできるので、効率的にマージを行うことができます。

まとめ

Mergeツール/外部Diffツールのプリセット定義は TortoiseHg\hgrc.d\MergeTools.rc にありました。
プリセット定義をいろいろカスタマイズして、より快適なMercurialライフを送りましょう :)

ちなみにマージツールとしては今のところコストパフォーマンス含めて最強なのはkdiff3だと思います。日本語周りがちょっとアレですが・・・。

と思ったんですが、改めて使ってみるとBeyondCompare 3も悪くないかも。(前に試した時は何か残念なポイントがあったような気がするけど、それが何だったか思い出せませんw)