お前らのコミットフックは間違っている


という話を書こうと思ったんですよ。まあタイトルは釣りなんですけど。

別にたいした話ではないんですが、

コミット前に××をチェックする

的なフックのサンプルとしてネットで見つかるのが、

precommit.py
def hook(ui, repo, **kwargs):
    ctx = repo[None]
    for file in ctx.files():
        # 何かチェック

みたいなコードをprecommitフックに指定しましょう、っていうのが多いんですね。
この場合のctxってworkingctxってやつで、ここには作業ツリー内で変更されてるファイルの情報が入ってる訳ですよ。
それでいいならいいんですが、それだと コミット対象として選択してないファイルも含まれちゃうよ? ってことで、

pretxncommit.py
def hook(ui, repo, **kwargs):
    node = kwargs["node"]
    ctx = repo[node]
    for file in ctx.files():
        # 何かチェック

みたいなのを、pretxncommitフックに指定すれば、changectxが取れるから、ちゃんとコミットに含まれるファイルだけを列挙できますよ、っていう話。
pretxn~ であんまり時間のかかる処理はしない方がいいよって話もあるんですが、ローカルリポジトリなら大丈夫でしょう。

と思ったんですが、

commit --amendの時はどういう動きになるの?

とふと疑問に思って試してみたら、何と

precommitフックはcommit --amendのときには実行されない

ということが分かりました。 マジで!?バグじゃなくて?


まあいいや、やっぱりpretxncommit大勝利だな、と思ったんですが、ためしにpretxncommitをFailさせてみる(return Trueする:Mercurialのフックはreturn TrueがFailなのです)と、まあ、なんということでしょう

作業ツリーが壊れる

という驚きの結果に。


壊れるっていうのはちょっと言いすぎですね。
hg status とかで 警告: 作業領域の親 '1187acbad27d' が未知のリビジョンです! みたいな警告が出るようになって、TortoiseHgとかで見てもこんな状態になってますが、

この時点ではどのファイルも失われていないはずです。
ただ、ここであわてて hg update -r tip とかってすると、(多分)変更していたファイルは永遠に失われることになりそう。

ここから安全に元の状態に戻す方法を考えたんですが、

1.作業ツリーのファイルをバックアップする
2.hg update -r tip を実行する
3.バックアップからファイルを上書きする

とかしか思いつきませんでした。もっといい方法がありそうなら教えてください。

という訳で、

結論:Mercurialのコミットフックは間違っている

ほんとに?
正直僕が何か間違えてる気がするので、突っ込み歓迎です。

どっちにしろ、せっかくのDVCSなので、コミットごとにちまちまチェックしなくても中央へのpushのときにガードしとけばいいんじゃないの、って気はしますね。

Mercurial Advent Calenderの枠もらって書こうと思ったけどあんまりいいオチついてないので野良で。

書き忘れた。
環境は Windows7 32bit
Mercurialのバージョンは 2.4+6-35ba170c0f82
です。

2012/12/07 追記

precommitがamend時に実行されない件は報告されていました
http://bz.selenic.com/show_bug.cgi?id=3609
pretxncommitがFailになると作業コピーが壊れる件は、こちらと同じ原因のようです
http://bz.selenic.com/show_bug.cgi?id=3670