会社のプロジェクトのバージョン管理はSubversion。しかも、10G弱でコミット数も相当数あるリポジトリ。だけど、Gitを使いたい!と思って方法を模索したのでそのメモです。Gitをよく知らないのもあり、もっと良い方法があれば教えていただけると幸いです。
背景
会社のプロジェクトではSubversionが使われています。しかし、世の中ではGitの方が主流(?)ということもあり、自分もGitを使って開発をしたい、と思いました。
新人のペーペーの力でSubversionからGitへ移行することもできないので、なんとか自分だけでもGitを使えないものかと考え、調査・環境構築を行いました。
使用環境
Windows 7 + MSYS2
git-svn の利用
「SubversionリポジトリをGitで扱う」ことに関しては、ネットに情報が結構あったので、比較的すんなりできました。
使用するのは git svn
コマンドです。参考にしたのは以下のリンクなどです。詳しくはそちらをご覧ください。
[git-svnでSVN→Gitへの移行をやってみたログ - Qiita] (http://qiita.com/hidekuro/items/4727715fbda8f10b6b11)
現場がSubversionでつらい貴方へ…自分だけこっそりGitで開発する方法
Subversionリポジトリからクローン
svn checkout
に相当。
git svn clone -s --prefix=svn/ https://subversion/repos/
上記を実行するとtrunkのみがローカルブランチとして取り込まれ、Subversion の branches や tagsはリモートブランチとして取り込まれます。ローカルブランチ、リモートブランチ、リモートリポジトリについてはよく知らなかったので以下を参考にしました。
Git で「追跡ブランチ」って言うのやめましょう - Qiita
追跡ブランチ (tracking branch) というブランチが何なのか調べた
clone 中に中断することがあったので、その場合は以下のコマンドを実行。
git svn fetch
更新
svn update
に相当。
作業ディレクトリ(masterブランチにて)を最新のtrunkに更新する際は以下。
git svn fetch
git merge svn/trunk
or
git svn rebase
その他
他に git svn dcommit
とかありますが、今回は使用しておりません。ビビリのため使えず。Subversionリポジトリにcommitするときは、別途svn checkout
して、Git での変更をパッチとして当てて、commit しています。今後の課題。
個人的な問題点
冒頭で述べたとおり、今回扱う Subversion のリポジトリはサイズ・コミット数ともに大きく、巨大なリポジトリでした。git svn clone
だけで数時間…。何度か中断しつつも完了しましたが、新たな問題が発生しました。
Gitが重い
わかっているだけで以下の操作にとんでもなく時間がかかりました。
git status
git diff <commit> <commit>
- hash値のタブ補完(zsh使用)
git checkout <branch>
原因を調べてみると以下の記事を発見。
アホみたいにでかいgit repositoryを上手く扱う方法 - Qiita
まんま、2つの原因に当てはまっている…。どうしたものか。
巨大なSubversionリポジトリをGitで扱う
さて本題になります。先ほどの問題を解決するために今回は以下のようにしました。
- Subversionリポジトリから
git svn clone
でGitのローカルリポジトリ作成(←巨大) - 1.で作ったローカルリポジトリをリモートリポジト
リとしてgit clone
。ただし、shallow cloneを用いることでサイズ減を行う。 - 2.で作成したローカルリポジトリ上で作業。
- 2.で作成したローカルリポジトリから、1.で作成したローカルリポジトリに
git push
- 1.で作成したローカルリポジトリから
git svn dcommit
ここでそれぞれのリポジトリを以下のように定義します。
Subversion Repository
サーバー上にあるSubversionリポジトリ。git svn clone
して持ってくる対象となるリポジトリ。巨大。
git-svn Repository
git svn clone
で作成したローカルリポジトリ。巨大。手順1. で作ったもの。作業リポジトリとSubversionリポジトリの橋渡し的存在。
Local Repository
git-svn Rep からcloneして作成したローカルリポジトリ。shallow cloneを用いることでサイズ減を図る。実際に作業するリポジトリとなる。2. で作成したもの。
git-svn Repository
git svn clone
を使って作成します。説明済みなので省略。
bareリポジトリにして容量削減したいところでしたが、Subversionリポジトリの内容をローカルブランチに取り込めないため断念。
Local Repository
git-svn Repository を元にしてgit clone
して作成します。実際の開発作業はこのローカルリポジトリ上で行い、git-svn Repository に push します。
作成
Local Repositoryの作成には以下のコマンドを実行。
git clone --depth <depth> --no-single-branch file://path/to/git-svn/repository
--depth <depth>
は shallow clone のためのコマンドです。に数字を入れることで、取得するコミット履歴を過去件に限定するのでサイズの削減になります。
--no-single-branch
はリモートリポジトリ(git-svn Repository)から取得するリモートブランチを1つに絞らないようにするオプションです。これがないと、git-svn Repositoryで、現在アクティブなブランチしかリモートブランチとして登録されません(1つのブランチのみ持ってきたい場合はなくて構いません)。上記の--depth
オプションを使用することで、デフォルトで--single-branch
が設定されてしまうため、必要となります(@ystmg さん情報提供ありがとうございました)。以下git clone --help
より引用。
--depth <depth>
Create a shallow clone with a history truncated to the specified number of commits. Implies --single-branch unless --no-single-branch is given to fetch the histories near the tips of all branches. If you
want to clone submodules shallowly, also pass --shallow-submodules.
file://
は無いと警告を出されたので入れときました。
なお公式サイトの4.1 Git サーバー - プロトコルには以下の記述があります。
URL の先頭に file:// を明示するかどうかで、Git の動きは微妙に異なります。file:// を明示せずパスだけを指定し、かつコピー元とコピー先が同一のファイルシステム上にある場合は、Git は必要なオブジェクトにハードリンクを張ろうとします。もし異なるファイルシステム上にある場合は、Git はシステムデフォルトのファイルコピー機能を使って必要なオブジェクトをコピーします。一方 file:// を指定した場合は、Git がプロセスを立ち上げ、そのプロセスが (通常は) ネットワーク越しにデータを転送します。一般的に、直接のコピーに比べてこれは非常に非効率的です。file:// プレフィックスをつける最も大きな理由は、(他のバージョン管理システムからインポートしたときなどにあらわれる) 関係のない参照やオブジェクトを除いたクリーンなコピーがほしいということです。本書では通常のパス表記を使用します。そのほうがたいていの場合に高速となるからです。
更新
Subversion Repositoryの更新内容を取り込むには2段階の操作が必要になります。これはこの環境の欠点になります。
- git-svn Repositoryの更新
- Local Repositoryの更新
つまり、
git svn rebase #git-svn Repository上で操作
git pull #Local Repository上で操作
Subversion Repository への変更反映
これも上記と同様に2段階の操作が必要となります。私はビビリなのでgit svn dcommit
はやったことありません。申し訳ありません。
git push origin <ブランチ名> #Local Repository上で操作
git svn dcommit #git-svn Repository上で操作
終わりに
これまで巨大SubversionリポジトリをGitで扱う方法について私なりの環境を説明してきました。実際の業務ではコーディング自体はあんまり任されないので、必要が出てきたらLocal Repositoryを作って、作業、git-svn Repositoryにpush、終わったらLocal Repository削除 と言ったように運用しています。
Local Repositoryを削除しても git-svn Repository に自分の作ったものの記録が残るのもこの環境の良いところかなとも思っています。