2016-02-22T19:42:55+09:00

svn2git でエラーが発生するとレポジトリを問題となるリビジョンだけ省いて Git に移行した

svn2git で、Subversion レボジトリを Git に移行しようとしいたところ、特定のリビジョンを処理している所でエラーが発生して svn2git が完了しない。

r3963 = f335d195f7687cce1c7c3f1a316f7c0ae686b0e7 (refs/remotes/svn/tags/Release_2_2_1)
	M	FileTableController.m
	M	History.rtf
	A	QuickDMG.xcodeproj/tkurita.mode1v3
	M	QuickDMG.xcodeproj/project.pbxproj
	M	QuickDMG.xcodeproj/tkurita.pbxuser

なんで、こんなエラーが発生するのかも分からんし、回避策をかなり頑張って探したけど見つからない。

過去のコードがそんなに大事とも思わないので、レボジトリの移行をあきらめてもいいのだけど、シャクなので Git の勉強と思って、web を検索しまくり、頭を抱えてのたうち回り、無駄なことしているのではという自己嫌悪に悩まされながら、いろいろ考えてみた。

考えついた唯一の方法は、エラーが発生するリビジョンを避けて、Git レボジトリに変換し、Git に移行してから、そのレボジトリをつなげる事。大まかな手順は、次の通り。

次に細かく実行コマンドも含めて紹介する。

今、リビジョン 3963 でエラーが発生するので、その前後のコミットを別々のレボジトリとして、svn2git する。すなわち、

# r3962までを svn2git
$project=project1; mkdir base-repo; cd base-repo
svn2git ${SVNREPOS} --authors ../authors.txt \
                    --trunk /${project}/trunk \
                    --tags /${project}/tags \
                    --branches /${project}/branches \
                    --revision 0:3962
# r3964以降を svn2git
$project=project1; mkdir repo3964; cd repo3964
svn2git ${SVNREPOS} --authors ../authors.txt \
                    --trunk /${project}/trunk \
                    --tags /${project}/tags \
                    --branches /${project}/branches \
                    --revision 3964:HEAD

こうして、エラーが起きるリビジョンを除いて、base-repo と repo3964 ができた。base-repo に repo3964 を取り込む。

cd base-repo
git remote add repo3964 ../repo3964
git fetch repo3964
git fetch --tags repo3964 # これを忘れると、repo3964 に含まれるタグが失われる。

取り込んだ repo3964 のコミット群を rebase によって、base-repo の master の後ろに引っ付ける。

git checkout repo3964/master
git rebase master

rebase でコンフリクトが発生したら、コンフリクトしたファイルを修正して、add する必要がある。今の場合、git check --theirs で marge されている側のファイルを選択するべき。次のようなシェルスクリプトで一気にコンフリクトを解消できる。

for f in `git ls-files -u | cut -f 2 | sort -u`;do
  git checkout --theirs $f; git add $f
done

コンフリクトを解消したら、rebase を続ける。

git rebase --continue

rebase が完了すると、base-repo の master と repo3964 の master の歴史がつながる。

ここで repository の状態を確認すると、元 repo3964/master から取り込んだコミット郡は master から生えている名無しのブランチになっていると思われる。

$ git status
HEAD detached from repo3964/master

$ git branch
* (HEAD detached from repo3964/master)
  master

master を最新のコミットまで移動させて HEAD と master を一致させる必要がある。そのために、master に名無しブランチを merge する。ブランチに名前が無いと処理に困るので、一時的な名前を付ける。

git branch repo3964

master に戻り、ブランチ repo3964 をマージする。

git checkout master
git marge repo3964

最後に、一時的なブランチ名を消す。

git branch --delete repo3964

これで、できる限り Subversion から Git へ変更履歴を移せたと思う。

いや〜、こんな事で来ちゃうなんて、Git はすげーなと思う。一方で、Subversion のできるようにしかできな優しい世界も懐かしく。悩みは、確実に Subversion を使っているときの方が少なかった。

誰かのお役に立てますように。