Windowマシンで行う git-svnでのSubversionからGitへの移行方法

Windows環境でのSubversionからGitへのリポジトリ移行作業についてまとめました。
似たようなことは [SubversionからGitへの移行でチーム影響を最小限にする - Qiita](http://qiita.com/mookjp/items/7d6ac31521a671f8b806) にも書いていますが、作業中心に短く書いています。

作業はWindowsマシン上で行い、Gitリポジトリは作業を行うローカル環境のほかにリモートリポジトリを作成するものとします。

## 必要なツール

以下をあらかじめインストールしておきます。

* [Git for Windows](https://msysgit.github.io/)

## Gitリポジトリを作成する

移行先となるGitリポジトリを作成します。
チームで共用するリモートGitリポジトリ（GithubやGitlab上など）はあらかじめ作成しておきます。

### ローカルGitリポジトリを作成

まずは移行作業を行うためのローカルリポジトリを作成します。

`git svn init`でSubversionリポジトリを登録し、Subversionリポジトリの変更は`git svn fetch`で取り込みます。

```shell-session
git svn init \
-T <trunkのURL>
-b <branchesのURL>
-t <tagsのURL>
--username=<ユーザー名> #認証がある場合はパスワードの入力をします
# ※svn init --prefix=svn/ でSubversionのブランチのみにprefixをつけておくこともできます

git svn fetch
# 以下、Subversionでの変更履歴の取り込み。大きなリポジトリだとかなり時間がかかる
```

上記コマンドを実行すると、ローカルリポジトリにSubversionのブランチとタグが取り込まれています。Subversion上のブランチとタグはremotes/以下に登録されています。

```shell-session
git branch -a
* master
  remotes/20141204_xxx-1.0.0
  ... その他ブランチ一覧。省略
  remotes/tags/20141126-xxx-1.0.0
  ... その他タグ一覧。省略
  remotes/trunk
```
`.git/config`を見ると、`svn-remote`という項目にSubversionリポジトリ独自のrefspecが設定されています。

```shell-session
[svn-remote "svn"]
        url = http://svn-server/xxx/my-project
        fetch = src/xxx/trunk:refs/remotes/trunk
        branches = src/xxx/branches/*:refs/remotes/*
        tags = src/xxx/tags/*:refs/remotes/tags/*
```

### Gitのブランチ・タグを作成

`git svn fetch`でSubversionのブランチやタグのrefがremotes/以下に入ります。
ここからGitのローカルブランチ・タグを作成して、Gitのリモートリポジトリにpushします。

#### ブランチを作成

タグは`tags/xxx`のような名前でrefが作成される。ブランチのみGitのブランチを作成する。

```shell-session
git branch -r | egrep --text --invert-match "tags|trunk" | xargs -iBRANCH_NAME git branch BRANCH_NAME BRANCH_NAME
```

#### タグを作成


```shell-session
git branch -r | egrep --text "tags/" | cut -d / -f2 | xargs -iVAR git tag VAR tags/VAR
```

### Gitのリモートリポジトリへpush

あらかじめGitのリモートリポジトリを用意しておきます。
このリポジトリには`http://my-repo/my-project.git`でアクセスできるものとします。

```shell-session
git remote add origin http://my-repo/my-project.git
git push origin --all # すべてのブランチをpush
git push origin --tags # すべてのタグをpush
```

## SubversionとGitのリポジトリを並行して利用する場合の差分適用

複数の案件を並行で開発しているチームでSubversionからGitへ移行する場合、どうしても開発をストップできない状況になることもあるかと思います。
その場合は、SubversionリポジトリとGitリポジトリを併用する移行期間を設けて、両者の差分を適用しながら開発を進めることができます。

以下の例ではSubversionリポジトリ、Gitリポジトリ両者に`feature-a`という共通のブランチが作成されているものとします。

### Subversionリポジトリの差分をGitリポジトリに取り込む

Subversionリポジトリの`feature-a`ブランチでも開発は続いており、この差分をGitリポジトリへ取りこみたい場合は以下のような作業を行います。

```shell-session
# Subversion側の修正をすべて取得
git svn fetch
# リモートのGitリポジトリでの修正を取得
git fetch origin feature-a
# Gitリポジトリのブランチをcheckout
git checkout feature-a
# Gitのfeature-aへSubversionのfeature-aをマージ
git merge remotes/feature-a
# Gitのリモートリポジトリへpush
git push origin feature-a
```

上記の作業を行うことで、Subversionリポジトリでの差分もGitリポジトリへ取り込むことができます。

### Gitリポジトリの差分をSubversionリポジトリに取り込む

逆に、Gitリポジトリでの差分をSubversionリポジトリへ取り込む場合は以下のようにします。

```shell-session
# Subversionリポジトリでの変更を取り込みます
# ローカルブランチfeature-aに反映させます
git svn fetch
git checkout feature-a
git merge remotes/feature-a

# ローカルGitリポジトリでリモートGitリポジトリの変更を取り込みます
git fetch origin
git checkout feature-a
# Gitリポジトリでの差分を --squash で1つにまとめてマージコミットを作成します
git merge origin/feature-a --squash
git commit

# dcommitでSubversionリポジトリへ反映させます
git svn dcommit

# dcommitしたらGitのリモートリポジトリにもマージコミット分をpushします
git push origin feature-a
```