Mercurial の Subversion 連携における問題の調査
※ 修正が取り込まれた 2.2. 版以降であれば、言語設定に起因する変換失敗は発生しません。それ以前の版を利用する場合は、"LC_ALL=en_US.utf-8; export LC_ALL" 等により、言語設定を無効にしてください。
なお、convert エクステンションでは、内部処理が全て UTF-8 で実施されるため、HGENCODING やロケール設定で cp932 を指定しても、ファイル名の cp932 化はできません。
話の発端は以下の tweet:
tweet を見た時点で想定される原因としては、以下の要因によって、日付文字列形式が想定外になっているのであろう、というもの:
3つ目の要因の場合、クライアント側ではどうにも対処が出来ないので、念のため日付形式を追加可能にするエクステンションの実装を裏で実施。
どうやらロケール設定の問題だった模様で、"export LC_ALL=C" で回避できたらしいのだが、そんな面倒な手間をユーザに強いるのはアレなので、できれば対応修正をしておきたいところ。
というわけで、手元の環境で色々試してみたところ……再現しねぇ……orz
変換を実施するクライアント側のロケールだけだとアレなのかもしれないので、念のためサーバ側も ja_JP.utf-8 になるようにシステムのデフォルトロケールも設定してみたのだが、やはり再現しない。
コミットログに日本語を入れれば違うかも?ということで、日本語ログを入れてみたけれど、やっぱり再現しない。
問題が発生した環境での Mercurial や python-subversion バインディングの版を確認してもらったところ:
- Mercurial は 2.1
- python-subversion は 1.6.12dfsg-4ubuntu5
とのこと。僕の環境だと:
- Mercurial は 2.1.2
- python-subversion は 1.6.12dfsg-5(@Linux) か 1.7.4-1(@Cygwin)
なのだけど、少なくとも Mercurial の 2.1 〜 2.1.2 では、ロケール設定や日付解析に関する修正は取り込まれていない。となると、python-subversion の差かなぁ。
さてどうしたものか?と思案しつつ、つらつらとソースを眺めていて、subversion の日付解析呼び出し部分を見て:
# Example SVN datetime. Includes microseconds. # ISO-8601 conformant # '2007-01-04T17:35:00.902377Z' date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
あれ?なんか妙だぞ? > '2007-01-04T17:35:00.902377Z'
上記のコードの date 変数には、python-subversion から返却される日付情報文字列が格納されているのだが、そういえば『不正な日付』とみなされた文字列は:
この文字列は UTF-8 で言うと『月 1月 24 11:16:01 2011 +0000』になる。
ロケールで『言語』設定が ja の環境で "svn log" すると、以下の様な形式でログ表示されるのだが:
r11 | fujiwara | 2012-04-23 20:06:01 +0900 (月, 23 4月 2012) | 2 lines
変換処理で使用したいのは明らかに赤文字の方だけど、変換失敗時に返却されているのは青文字の方っぽい。
これまでは青文字部分を見て『ロケール設定が日付表示に影響している』と思っていたけど、"svn log" でも赤文字部分が常時表示されていることから、版によっては、python-subversion バインディングが間違って青文字部分の localize された日付情報を返却してるんじゃねぇ?という疑念が。
# subversion には出力形式改変の『テンプレート指定』って無いよね?
しかも、以下の情報の通り『変換も途中まではうまくいって、あるコミットだけ不正な日付』というのは、なんかロケールの問題では無い気が…。
まさか、localize 文字列長が想定長より長いために、メモリリークで想定外の領域を読み出したりしてたりしないよね?(笑) > python-subversion バインディング
で、結局のところ、現状では問題の根っこは判明していないのだけれど、とりあえず原因特定のための情報を吐き出すデバッグコードパッチを作成してみた。
適用手順は以下の通り。
# お手数ですが情報採取お願いします > @r_rudi
(調査結果はその2に続く)
(1) ソースの入手
ソースアーカイブをダウンロードするか、Mercurial 自身のリポジトリをクローンするなどして、Mercurial のソースを入手する。
# とりあえず最新の 2.1.2 版を想定
(2) パッチの適用
以下のパッチをファイルに保存 ("convert-debug.patch")。
diff -r df883eb76f03 hgext/convert/subversion.py --- a/hgext/convert/subversion.py Mon Apr 23 21:31:10 2012 +0900 +++ b/hgext/convert/subversion.py Mon Apr 23 23:01:00 2012 +0900 @@ -771,6 +771,8 @@ # Example SVN datetime. Includes microseconds. # ISO-8601 conformant # '2007-01-04T17:35:00.902377Z' + self.ui.debug('[DBG] subversion:_fetch_revisions: ' + 'rev=[%s] date=[%s]\n' % (rev, date)) date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"]) log = message and self.recode(message) or ''
Mercurial ソースツリーのルートでの patch コマンド実行によりパッチを適用。
$ patch -p1 -u < convert-debug.patch
(3) インストール
ビルドしてインストールを実施。
$ make PREFIX=${HOME}/hg/convert-debug install-bin
(4) 変換実施
問題が発生する環境で、パッチ適用版の hg コマンドで変換を実施する。この際に、原因特定用情報を出力させるために、必ず --debug オプションを指定すること。
$ ${HOME}/hg/convert-debug/bin/hg --debug convert -s svn SVN-URL
出力される情報の "[DBG] subversion:_fetch_revisions:" 行に、subversion から得られる各リビジョン毎の日付が表示される。
解析処理直前に出力しているので、最後に出力されたリビジョンが、変換失敗の原因ということに。