gitとsvnの自動同期スクリプトの作成

こんにちは。株式会社インターファクトリーの研究開発部のダイケンです。

この度弊社では、svnでバージョン管理してきたシステムをgitへ移行させることになりました。しかし、ビルドやデプロイにsvn上のファイルを使用する運用がされており、すぐに完全移行するのは難しいという話になりました。このため、gitとsvnを自動同期するスクリプトを作成して、gitとsvnを並行で運用する期間を設けることにしました。

今回は、そこで作成したスクリプトについて紹介させていただきます。

概要

gitとsvnを同期するため、git svnを利用しました。

参考URL

https://git-scm.com/book/ja/v1/Git%E3%81%A8%E3%81%9D%E3%81%AE%E4%BB%96%E3%81%AE%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E9%80%A3%E6%90%BA-Git-%E3%81%A8-Subversion

git svn cloneによってsvnからgitへリポジトリを移行します。

prefixオプションにsvn/を指定してsvnを取り込むためのブランチを他と区別できるようにしました。

リポジトリの構成は下図のようにしました。

図1.リポジトリの 構成

git側のサーバーにはnon-bareとbareの2種類のリポジトリを置いています。
これは、git svnのコマンドで作成されるリポジトリがnon-bareリポジトリでなければならなかったためです。
non-bareリポジトリには、作業ブランチは開発者がプッシュする master ブランチとsvn の内容を取り込む trunk ブランチを用意しました。

  • git→svn : masterの内容をgit mergeして、git svn dcommitでsvnへ反映
  • svn→git : svnの内容をgit svn rebaseで取り込んで、git mergeでmasterへ反映

 

図2.ブランチ

ブランチを2つ用意したのは、git svn dcommitを行った際にgit rebaseが行われてコミット情報が変更されてしまい、1つのブランチでは上手くいかなかったためです。なお、git rebaseが行われるのはsvnと同期済みのコミットコメントにgit-svn-idを追記するためだと思われます。

作成したスクリプト

フックの有効化

スクリプトの動作中はリポジトリが操作されないようにするため、gitフックを利用しました。今回利用したpre-receiveはサーバー側でプッシュを受け取った際に動作します。プッシュを受け取った際に全て拒否するスクリプトを、ファイル名を変更することで有効化/無効化する仕組みにしています。

svnとgitの更新チェック

gitとsvnが更新されているかをチェックして、必要なコマンドのみ実行できるようにします。

gitはリモートとローカルの最新のコミットを比較して、コミットが異なっている場合は更新されていると判断します。

git ls-remoteコマンドでリモートの最新コミットを取得します。この時、コミットIDの後ろにブランチ名が付くため、awkコマンドでコミットIDのみ切り出しています。

svnはgit svn fetchの結果が空でなければ更新されていると判定します。

git svn fetchコマンドは、svn側に未取り込みのコミットがある場合、コミット情報をリモートブランチへ取り込みます。この時、取り込まれた実行結果となるため、空の場合は更新がなかったということになります。

trunkへsvn側から更新取り込み

trunkブランチへsvnの更新を取り込みます。

ここでは、競合が起こるリスクを減らすためにgit svn rebaseではなく、git svn fetchgit resetによって変更を取り込むことにしました。

先に行った更新チェックのgit svn fetchコマンドでsvn側の更新はsvn/trunkへ取り込まれた状態となっています。ここで、svn/trunkへのgit reset –hardを行い、更新が取り込まれた状態にします。

masterへgit側から更新取り込み

masterブランチへ更新を取り込みます。

svnと同様に、gitもfetchとresetを利用して最新化しています。

svn側に反映

git mergeによってtrunkブランチへマージを行い、git svn dcommitによってsvn側への反映を行います。

原因は分かっていませんが、git svn dcommitは1度失敗してもリトライした際には成功することがあります。このため、4回までリトライする作りにしました。

git側に反映

masterブランチへマージを行い、git側に反映します。

フックの無効化とプッシュ

最後にフックの無効化とプッシュを行っています。

まとめ

スクリプト作成中の検証でgit svnコマンドでエラーが多発し、解消させる方法を考えるのに苦労しました。git svnコマンドの詳細な動作については調べても分からず、トライアンドエラーで想像で少しずつ理解していき進めるしかありませんでした。

稼働当初こそ同期エラーが多く手作業での修復が必要だったものの、日が経つうちにエラーも少なくなってきました。理由は不明ですが、おそらく開発者がgitの扱いになれて無駄なコミットやマージが少なくなったからではないかと思われます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です