読者です 読者をやめる 読者になる 読者になる

Mercurial のキーワード拡張を使うと改名が追跡されない問題の対応方法

Mercurial でバージョン管理しているファイルを Mercurial を通さずに改名したときに変更前ファイルが追跡できなかったときのメモ *1

普通は追跡できる

[shrimp]$ hg init hoge; cd hoge

# 適当にファイルを作ってコミット
[shrimp]$ echo 'hoge' > a.txt
[shrimp]$ hg add
adding a.txt
[shrimp]$ hg commit -m "add a.txt"

# Mercurial を通さずにファイル名を変更
[shrimp]$ mv a.txt b.txt

# 変更前ファイルの削除と変更後ファイルを登録してコミット
[shrimp]$ hg addremove
adding b.txt
removing a.txt
[shrimp]$ hg commit -m "a.txt rename to b.txt"

# b.txt の変更履歴を見る
# 改名前の a.txt を作成したリビジョン0も出力される
[shrimp]$ hg log --follow --template '{rev} {desc}\n' b.txt
1 a.txt rename to b.txt
0 add a.txt

キーワード拡張を使うと追跡できない

キーワード拡張の設定はこんな感じ。

[shrimp]$ cat .hg/hgrc
[extensions]
keyword =

[keyword]
**.txt =

[keywordmaps]
Header = {file|basename} {node|short}

こうしておいてファイルの中に $Header$ と書いておくと $Header: a.txt 714c62379545 $ みたいに展開してくれるのだが、こうしておいて先ほどと同じようにしても今度はファイル名変更前の a.txt を作成したリビジョン0 が出力されない。

[shrimp]$ hg init fuga; cd fuga

# ./.hg/hgrc にキーワード拡張を設定

# 適当にファイルを作ってコミット
[shrimp]$ echo '$Header$
ほげほげ' > a.txt
[shrimp]$ hg add
adding a.txt
[shrimp]$ hg commit -m "add a.txt"

# Mercurial を通さずにファイル名を変更
[shrimp]$ mv a.txt b.txt

# 変更前ファイルの削除と変更後ファイルを登録してコミット
[shrimp]$ hg addremove
adding b.txt
removing a.txt
[shrimp]$ hg commit -m "a.txt rename to b.txt"

# b.txt の変更履歴を見る
# 改名前の a.txt を作成したリビジョン0が出力されない
[shrimp]$ hg log --follow --template '{rev} {desc}\n' b.txt
1 a.txt rename to b.txt

解決方法

addremove するときに --similarity オプションを指定すればいい。

[shrimp]$ hg init piyo; cd piyo

# ./.hg/hgrc にキーワード拡張を設定

# 適当にファイルを作ってコミット
[shrimp]$ echo '$Header$
ほげほげ' > a.txt
[shrimp]$ hg add
adding a.txt
[shrimp]$ hg commit -m "add a.txt"

# Mercurial を通さずにファイル名を変更
[shrimp]$ mv a.txt b.txt

# 変更前ファイルの削除と変更後ファイルを登録してコミット
# このとき --similarity オプションを指定する
[shrimp]$ hg addremove --similarity 1
adding b.txt
removing a.txt
recording removal of a.txt as rename to b.txt (37% similar)
[shrimp]$ hg commit -m "a.txt rename to b.txt"

# b.txt の変更履歴を見る
# 改名前の a.txt を作成したリビジョン0も出力される
[shrimp]$ hg log --follow --template '{rev} {desc}\n' b.txt
1 a.txt rename to b.txt
0 add a.txt

まとめ

--similarity オプションは、ファイルの類似度をパーセンテージで指定して改名検知するためのオプション。指定しない場合は100が指定されたものとみなされて、完全一致するファイルのみが改名とみなされる。キーワード拡張を使っているときに Mercurial を通さずに改名するとキーワード部分が一致しないため改名検知されない。しかし、リファクタリングで改名した場合などはそれが明らかであるから、--similarity オプションで意図的に類似度を下げて Mercurial に改名を検知させればいい *2

IntelliJ IDEA は大丈夫

IntelliJ IDEA + hg4Idea の場合は IntelliJリファクタリング機能で改名しても、ちゃんと hg log --follow で改名前のファイルが出力される。便利。

*1:IDEリファクタリング機能でディレクトリを移動しまくった

*2:--similarity オプションに0を指定すると改名を一切検知しなくなるので注意