Mercurial の Subversion convert extension
Mercurial 0.9.5 以降には,他の SCM からレポジトリを取り込むための convert extension が装備されました(⇒ ConvertExtension - Mercurial)。Subversion のレポジトリから convert するための extension ももちろん含まれています。
利点も多いのですが,残念ながらまだあまり練られていない印象で,欠点が多くなっています。
利点
- Mercurial に標準添付されている
- trunk, branches, tags というディレクトリを認識して,自動的に branch や tag を貼ってくれる
- Mercurial には,branch, tag を貼るための機構(changeset の属性)が builtin されています。Subversion convert extiosion では,Subversion での慣例的なディレクトリ branches 等を認識して,Mercurial システム上の branch や tag に変換してくれます。うまく動けば非常に面白いです。
欠点(バグ)
- あくまで import サイドの converter であり,push できない(仕様)
- branch がうまく切れないことがある
- tag がうまく貼られないことが多い
- 削除されたファイルをうまく取り扱えない
これらは Mercurial に欠陥があることを示しているわけではありません。convert extension の歴史が浅いため,まだきちんと動いていないというだけです。ですが,まだ実運用には難しいようです。hgsvn を使う方が現状まだマシかと。
実際に convert してみる
下記のような Subversion レポジトリを convert します。
% svn cp trunk branches/1.x % svn ci -m "branch 1.x added" % svn cp branches/1.x tags/1.0 % svn ci -m "tag 1.0 released" % svn cp trunk branches/2.x % svn ci -m "branch 2.x added" % svn cp branches/2.x tags/2.0 % svn ci -m "tag 2.0 released"
branch を2つ切っているのは,「convert extension で branch がうまく切れないことがある」バグを回避するためです。
hg convert
コマンドで取り込んでみましょう。
% hg convert http://localhost/svn/example example initializing destination example repository scanning source... sorting... converting... 3 initial import 2 base.txt added 1 branch 1.x added 0 branch 2.x added % cd example % hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
無事 convert できたようです。
どのような branch があるのか確認してみます。
% hg branches 2.x 3:48531de71762 1.x 2:003612635ee3 default 1:bebb1d832479 % hg branch default
Subversion の branches に対応して,1.x と 2.x が切られているようです。trunk にあたるのは default のようです。Working copy も default branch になっています。
では heads の様子はどうでしょうか(一部出力を省略しています)。
% hg heads changeset: 3:48531de71762 branch: 2.x tag: tip parent: 0:b0a5ad784788 summary: branch 2.x added changeset: 2:003612635ee3 branch: 1.x parent: 0:b0a5ad784788 summary: branch 1.x added changeset: 1:bebb1d832479 summary: base.txt added
各 branch に呼応して heads が存在しています。
では,tag の扱いはどうなっているでしょうか。
% hg tags tip 3:48531de71762
レポジトリの最新 changeset を示す tip しか存在していません。tags はうまく convert できなかったようです。
tags をうまく convert するには?
branches についてはおおむねうまく convert されているようですが,tags については convert されませんでした。いろいろ試行錯誤を繰り返した結果,tags を svn cp
した後に trunk に何らかの変更がないと changeset として認識してくれないことがわかりました。
じゃあ実例をば。
% svn cp trunk branches/1.x % svn ci -m "branch 1.x added" % svn cp branches/1.x tags/1.0 % svn ci -m "tag 1.0 released" % svn cp trunk branches/2.x % svn ci -m "branch 2.x added" % svn cp branches/2.x tags/2.0 % svn ci -m "tag 2.0 released" % touch trunk/dummy_2.0 % svn add trunk/dummy_2.0 % svn ci -m "dummy_2.0 added" % touch branches/2.x/for_2.1 % svn add branches/2.x/for_2.1 % svn ci -m "for_2.1 added" % svn cp branches/2.x tags/2.1 % svn ci -m "tag 2.1 released" % touch trunk/dummy_2.1 % svn add trunk/dummy_2.1 % svn ci -m "dummy_2.1 added"
tags を貼ったあとに,trunk にダミーファイルをこしらえて commit しています。またテストのため,tags/2.1 以降では branches/2.x に for_2.1 というファイルを置いています。
convert します。
% hg convert http://localhost/svn/example example initializing destination example repository scanning source... sorting... converting... 6 initial import 5 base.txt 4 dummy_2.0 added 3 dummy_2.1 added 2 branch 1.x added 1 branch 2.x added 0 for_2.1 added updating tags % hg update 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
「updating tags」というメッセージからすると,うまくいったくさいですね。
tag の状態をみてみましょう。
% hg tags tip 8:959cc2d717d1 2.1 6:4e2662fb6936 2.0 6:4e2662fb6936 1.0 5:50ddb61bc526
おお,先ほどと違って tag が貼られていますよ。
ん,でも 2.0 と 2.1 が同じ changeset を指していないですか?
% hg update -C 2.0 0 files updated, 0 files merged, 1 files removed, 0 files unresolved % ls base.txt % hg update -C 2.1 0 files updated, 0 files merged, 0 files removed, 0 files unresolved % ls base.txt % hg update -C 2.x 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % ls base.txt for_2.1
正しくは tag 2.1 では「for_2.1」というファイルが存在してしかるべきです。ですが,2.1 と 2.0 が同一になってしまっているので for_2.1 というファイルもありません。branch 2.x ではきちんと存在していますが。
このように,0.9.5 では仮に tag が import されていたとしても,正しくハンドリングされないようです。
削除されたファイルをうまく取り扱えない
さらに,より致命的なバグも存在します。
下記のような Subversion repository があるとします。
% mkdir a % touch a/x.txt a/y.txt % svn add a A a A a/x.txt A a/y.txt % svn ci -m "a/x.txt, a/y.txt added" % svn rm a/x.txt D a/x.txt % svn ci -m "a/x.txt removed"
一度 a/x.txt を作成し,その後削除しただけです。
このレポジトリを convert すると……
% hg convert http://localhost/svn/example example initializing destination example repository scanning source... sorting... converting... 2 initial import 1 a/x.txt, a/y.txt added 0 a/x.txt removed ** unknown exception encountered, details follow ** report bug details to http://www.selenic.com/mercurial/bts ** or mercurial@selenic.com ** Mercurial Distributed SCM (version 0.9.5) Traceback (most recent call last): File "/usr/bin/hg", line 14, in ? mercurial.dispatch.run() ...... snip ...... libsvn._core.SubversionException: ("PROPFIND request failed on '/trunk/a/x.txt'", 175002)
エラーを吐いて止まってしまいました。