この文書は無保証です。
要約
svn 単独で運用していた環境から、svn をメインリポジトリサーバとして使用したままで、hg を使った 分散リポジトリ環境に移行した。キーワード
hgsubversion 拡張, largefiles 拡張, hg graft コマンド環境と前提条件
OS | hg | |
PC1 | Mac OS X 10.6 | hg 2.5.2 |
PC2 | Windows7 Professional SP1 x86 (32bit) | Tortoisehg 2.8, Mercurial 2.6 |
- PC1 と PC2 のユーザは別人である。
- svn サーバへアクセス出来るのは PC1 のみである (SSH 鍵の都合上)。
- PC2 からのコミットを検証して、PC1 から svn サーバに push する。
- 透過的コミットは、はなから目的としない。
結論
概要
- svn サーバからは必要な svn ブランチのみ clone すること。svn ブランチ間のマージは諦める。(svn でやる)
- svn のタグは諦める。
- svn との連係は default ブランチ、hg 側は base ブランチと決める。
- repoB は連係専用とし、repoB で通常の編集作業は行わない。 これを守ることで、repoB の default と base の間の同期作業で、graft 時にローカル変更との衝突が発生しない。
構築手順(svn ブランチごとに行う)
- repoB の構築
- hgsubversion 拡張を enable する。
- largefiles 拡張を enable する。
- hg clone svn://svnserver/repoA/svnbranch repoBS
- hg lfconvert repoBS repoB
- cd repoB
- cp ../repoBS/.hg/hgrc .hg/hgrc
(済んだら repoBS は消して良い) - hg svn rebuildmeta
- hg up
- hg branch base
- hg commit -m 'create base branch'
- hg serve --port 8881
ポート番号はブランチごとによしなに決める。
- repoC の構築
- hg clone repoB repoC
- hg up -r base
- repoD の構築
- hgsubversion 拡張を enable する。
- largefiles 拡張を enable する。
- hg clone http://PC1:8881/ repoD
- hg up -r base
運用手順
- repoB から svn に push する
- hg up -r base
- よしなに確認する。
- hg up -r default
- hg graft -r 'branch(base)'
- hg outgoing
最終確認する - hg push
- repoB に svn repoA から pull する
- hg up -r default
- hg incoming
- hg pull
- hg up -r base
- hg graft -r 'branch(default)'
- repoC, repoD での通常作業
- base ブランチに対してcommit。base ブランチからさらにブランチしてもよい。 base ブランチの内容だけが svn への push 候補となる。
- 普通に push, pull する。
知識とは失敗である、その手順
失敗: largefiles 拡張が必要
まずなにも考えず hg clone で svnリポジトリ全体をクローンする。
- PC1
- hgsubversion 拡張を enable する。
- hg clone svn://svnserver/repo1 repo1
→成功。2時間ぐらいかかった。 - cd repo1
- hg serve --port 8881
- PC2
- hg clone http://PC1:8881/ repo1
→数十分後に OutOfMemory の Python 例外
- hg clone http://PC1:8881/ repo1
調べたり聞いたりしたところによると、hg が想定しているファイルサイズは <10MB だそうで。 ファイルを全部メモリに展開するみたいで、32bit OS では Python のアドレス空間が不足する。
Twitter で愚痴を言ってみたところ、、、
Mercurial の Out Of Memory 地獄にハメられた。
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@moveccr #mercurialjp もしかしてhgsubversion併用での hg clone 時の out of memory でしょうか?であれば、以下の回避方法が参考になると思います groups.google.com/d/msg/mercuria…
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@moveccr 管理対象ファイルが余程大きくなければ out of mermory は発生しない(ファイル数上はmozilla規模のソースでも大丈夫)と思いますが、潜在的問題の可能性もあるので、--traceback 付きでの実行結果&再現条件等を知らせて頂けると助かります
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@flyingfoozy ええと、hgsubversion(MacOSX)でcloneしたリポジトリをさらにhg clone(Win7 x86)しようとしたとき、です。
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@flyingfoozyWin7 x86でhg clone -r1 xxxxhg pull -r365してからhg pull -r366したものがこちらです。moveccr.blogspot.com/2013/05/mercur…
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@flyingfoozy r366 には 4ファイルあってサイズ合計は1GBほどです。大きすぎるんだと思いますが、はっきり言って、hg 使うのは諦めようかと考えている状態です。
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@moveccr 情報ありがとうございます。pull での連携先リポジトリの設定ファイルで [server] preferuncompressed=True を設定して再度 pull を試してみてもらえますか? mercurial-users.jp/manual/hgrc.5.…
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@flyingfroozy タスクマネージャでワーキングセットサイズを観測していると、1GBを超えたあたりで out of memory で終了するようです。
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@flyingfoozy はい、やってみます
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@flyingfoozy やってみました。が、全く同じ結果となりました。
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@moveccr #mercurialjp あるいは、pull 連携先において hg bundle -t none で無圧縮bundleファイルの書き出し⇒ファイル転送⇒win側で hg unbundle でも回避できるかも?(ネットワークが切れやすい場合などにも使えます)
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@moveccr #mercurialjp あぁ、発生位置からすると、履歴に記録する際の圧縮処理でmemory errorが発生しているみたいなので、単純にx86環境での利用可能メモリの上限に引っかかっているのかもしれませんねぇ。だとすると、運用で回避するのは難しいかも……
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@moveccr #mercuraljp Mac側で問題無いのはMacOSが64bit環境だからだと思われます mercurial.selenic.com/wiki/HandlingL… Windows側でも64bit版バイナリを使用すれば大丈夫だと思いますが、OSが32bit版だったりします?
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@moveccr おっと、何故かメンションが機能してなくて、見落としてました。やはり 32bit 環境の壁でメモリ不足になっている印象ですね > 1GBを超えたあたりで out of memory
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@flyingfoozy Windows7のほうは32bit版です。なお、Window7 x64 では clone 出来ました。私個人が使うぶんには良いのですが、他の人全員64bitにしろとは言えないので、困っています。
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@moveccr #mercurialjp largefilesエクステンション mercurial-users.jp/manual/hg.1.ht… の lfconvert 変換により大容量ファイルを分離する手もありますが、ファイルサイズ次第では結局メモリが不足する可能性はありますねぇ>32bit環境
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@moveccr #mercurialjp もしも largefiles エクステンションに興味があれば、以下のエントリが参考になれば幸いです d.hatena.ne.jp/flying-foozy/2…
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@moveccr #mercurialjp largefiles エクステンション使用時は、large 扱いの管理対象ファイルは圧縮を行わないので、通信の圧縮設定さえ外せばそこそこのサイズのファイルまで扱えるとは思います(確定的な情報が無くて済みません)
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@flyingfoozy lfconvertについて調べてみます。ありがとうございました。
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@flyingfoozy lfconvert したリポジトリに対して、hg pull することで、完走しました。ありがとうございました!
— Y.Sugaharaさん (@moveccr) 2013年5月13日
@moveccr #mercurialjp 無事利用可能になった様で何よりです。largefiles は(コーナーケース的な)パターン指定時の hg status 時の挙動や、性能的な点で、まだ問題がありますが、可能な範囲で対処しますので、気になる点があったらお知らせください。
— FUJIWARA Katsunoriさん (@flyingfoozy) 2013年5月13日
@flyingfoozy さんご親切にありがとうございました。
というわけで largefiles 拡張が要るとのこと。
→やりなおし
失敗: hg lfconvert + hg svn rebuildmeta の罠
- PC1
- largefiles 拡張を enable する。
- hg clone svn://svnserver/repo1 repo1S
- hg lfconvert repo1S repo1
- cd repo1
- hg pull
→エラー。paths の設定は lfconvert で引き継がれていない、というか hgrc がコピーされない。 - cp ../repo1S/.hg/hgrc .hg/hgrc
- hg pull
→svn のリビジョンをもう一度全部 pull して来ようとする。
lfconvert したリポジトリは repo1S とは全く別のリポジトリで、ハッシュ値も全く違うものになる。 なのでリポジトリ内の clone した時のリビジョンと、svn の対応リビジョンの情報が一致しなくなり、 hg pull (incoming) で見える svn 側のリビジョンは別のものとして扱われてしまう。
→hg svn rebuildmeta
失敗: hgsubversion default ブランチの罠
hgsubversion 拡張は default ブランチを svn との連係に使用するが、svn にはマージリビジョンの 概念がないので、マージリビジョンがあると push できない。
さらに
- うっかりローカル変更があると、svn 側と衝突したときのマージが面倒。
- commit 後、svn に push するとハッシュ値やユーザ名が svn 側のものに変わる。
→push するまでハッシュが確定しない。したがってリビジョンを参照する(ログに関連リビジョンのハッシュを記載する)ことは不可能。
世の中を見ると、merge - commit - revert する方法が提案されていて、確かに可能だが、
- 複数のコミットで構成されるマージの場合、コミットがまとめられてしまう。
- 一つ一つのコミットを svn に送信したい場合は、一つづつ merge - commit - revert しなければならない。
- commit ログをもう一回入力しなければならない。
- リビジョングラフの形が気持ち悪い。
(イマイチ理解していないのでどこか間違ったのかもしれない)
さらに世の中には rebase する方法も提案されているが、試すより先に graft を思いついたので試していない。
解決策
- default ブランチは svn との連係専用にしてここで編集しない。
- repoC, repoD でのコミットは、base ブランチに行う。default は(過去ログ以外)見ない。
- graft を使う。この場合、default ブランチと base ブランチはリビジョングラフ上で交差しない。
これは嫌な人には嫌だろうが、今回のケースでは svn に push したコミットの author が書き換えられるため、 同じ変更でも別のコミットに見えてくれる方が都合が良く、怪我の功名。
graft は今回の用途には非常に都合よくできていて
- マージリビジョンは無視される。
- graft したリビジョンは記録されていて、何回も graft されるようなことはない。
- コミットログはそのままコピーされる。
失敗: 非標準レイアウトの罠
非標準レイアウトの場合 hgsubversion は全てのブランチとタグを通常のディレクトリとして svn から取得する。
これは svn の ブランチとタグが只のコピーと区別がつかないせいだが、clone したディレクトリが 恐ろしく肥大化してしまい、何をしても時間がかかるようになってしまう。
解決策
- 個別のブランチを svn から取り出す。svn ブランチ間マージは hg では実行しない。