彷徨えるフジワラ

年がら年中さまよってます

(利用状況次第では) Mercurial 2.3 版利用の回避を推奨

簡単にまとめると:

リビジョン指定に revsets 表記を多様する人は、想定外の挙動が発生する可能性があるので、Mercurial 2.3 版の利用は回避することを推奨

最新の情報に関しては、こちらのエントリを参照のこと。

以下、詳細というか、障害要因調査の顛末。

事の発端は、"hg convert" と filemap 指定の併用により、追加したファイルを履歴を遡って改名する実験をしていた際の、以下のような挙動の発見。

$ ADDEDREV=`hg -R src log -r "min(filelog('re:b'))" --template '{node}'`
$ echo ${ADDEDREV}
9c7bcdc44ce39d06d3fb9e4e0ee6a53c72122673
$ PARENTREV=`hg -R src log -r "${ADDEDREV}^1" --template '{node}'`
$ echo ${PARENTREV}
9c7bcdc44ce39d06d3fb9e4e0ee6a53c72122673
$

は????? "^1" (第1親リビジョン)指定を付けているのに、なんで ADDEDREVPARENTREV が同じなの?

あれこれ試行錯誤してみたけれど、さっぱり埒があかない。

そう言えば、revsets 指定の解釈だけを行うデバッグ用コマンドってなかったっけ?と "hg debugrevspec" の存在を思い出して、こちらでも試してみることに。

$ hg -R src debugrevspec "${ADDEDREV}"
3
$ hg -R src debugrevspec "${ADDEDREV}^1"
2
$

こっちは想定通りに解釈されてる (親のリビジョン番号 = 自身のリビジョン番号 - 1)。

"hg log" の "-r" 指定と "hg debugrevspec" の引数指定では、解釈処理のルートが違うとか?

色々原因箇所の絞り込みを試みたのだけれど、結構根が深そうなので、とりあえずどのあたりの版がヤバイのかをざっと確認したら、まずは障害報告だけ先に済ませてしまおう&できれば他の人に直してもらおう(笑)、という結論に。

版違いで挙動を確認してみると……あれ?ピンポイントに 2.3 版でだけ現象が再現する。

デスクトップ PC 上で使っていた版が、更新をサボっていたために、たまたま障害を踏む 2.3 版だっただけで、最新版にしておけば大丈夫だったんだ……orz

まぁ、慌てて『バグだ!バグだ!』と騒がなくて済んだのだから、良しとしよう。いや、バグではあるんだけど、既に修正済みだからね。

『revset』をキーワードに履歴を検索してみたけど:

$ hg log -r '2.3::2.3.1 and keyword(revset)'
チェンジセット:   17410:7c865f30e2b8
ブランチ:         stable
ユーザ:           Pierre-Yves David 
日付:             Fri Aug 31 23:27:26 2012 +0200
ファイル:         mercurial/repair.py
説明:
strip: fix revset usage (issue3604)

The `repair` code builds a giant revset query instead of using the "%lr" idiom.
It is inefficient and crash when the number of stripped changeset is too big.

This changeset replaces the bad code by a better revset usage.

これしか抽出されないし、issue3604 は何か違う感じ。

何の気なしに試してみたところ:

  • 40 桁のハッシュ値を使った "${ADDEDREV}^1" 記述は正しく機能しない
  • 12 桁のハッシュ値を使った "${ADDEDREV}^1" 記述は正しく機能する
  • リビジョン番号を使った "${ADDEDREV}^1" 記述は正しく機能する

ということが判明。更には 39 桁のハッシュ値でも但しく機能したので、どうやら『40 桁』が現象発生の閾値っぽい。

駄目元で『40』をキーワードに検索してみると:

$ hg log -r '2.3::2.3.1 and keyword(40)' -v
チェンジセット:   17353:bde1185f406c
ブランチ:         stable
親:               17351:9d9d15928521
ユーザ:           sorcerer
日付:             Thu Aug 02 19:10:45 2012 +0400
ファイル:         mercurial/parsers.c
説明:
revlog: don't try to partialmatch strings those length > 40

_partialmatch() does prefix matching against nodes. String passed
to _partialmetch() actualy may be any string, not prefix only.

For example,
"e410be8603932e46a51298748a4b874739037fad or 300" is a good
argument for _partialmatch().

When _partialmatch() searches using radix tree, index_partialmatch()
C function shouldn't try to match too long strings.

あったよ、おい!

簡易テストで試してみても、このリビジョンを契機に、動作が修正されているのが確認出来たので、間違いなさそう。

ただし、この修正、どうやらテスト追加とか全然やってなさそう (前後のリビジョンでも実施してない) なので、デグレード防止のテストを追加した方が良さそうな感じ。

なお、"[http://mercurial-users.jp/manual/hg.1.html#bisect:title=hg bisect]" を使って、障害要因リビジョンの絞込みをしてみると……

チェンジセット:   16665:e410be860393
ユーザ:           Bryan O'Sullivan 
日付:             Sat May 12 10:55:08 2012 +0200
ファイル:         mercurial/parsers.c mercurial/revlog.py
説明:
revlog: speed up prefix matching against nodes

The radix tree already contains all the information we need to
determine whether a short string is an unambiguous node identifier.
We now make use of this information.

In a kernel tree, this improves the performance of
"hg log -q -r24bf01de75" from 0.27 seconds to 0.06.

この修正が原因だった模様。