いまさらだけどSubversionからGitへ移行する（その２: プロジェクト・ディレクトリー分割）

先日、こちらの記事を書きました。

[いまさらだけどSubversionからGitへ移行する](https://qiita.com/hkato/items/1b092bff149b13f77505)

今回はその続きでSVNリポジトリー構成のバリエーションの話とGitLabなどを中心としたGitリポジトリー管理ツールとのマッピングの話になります。SVNリポジトリー上に実プロジェクトが複数含まれていた場合になります。SVN上での作業というよりも変換直後の複数実プロジェクトが混在する大きなGitリポジトリーをどうGit上で操作し分割していくかになります。

## 過去のSVNと最近のGitプロジェクト構成

前回の移行はこんなパターンに主に対応。

```text
myproj
    ├─trunk
    ├─branches
    │  ├─hoge-fix
    │  └─foo-func
    └─tags
       ├─RELEASE_1_0
       └─RELEASE_2_0
```

これは、GitLab/GitHubみたいな Group や Subgroup, Organization や Team みたいな概念がある場合、移行先は例えば、

- `https://gitlab.example.com/mygroup/myproj.git` or `git@gitlab.example.com:mygroup/myproj.git`


などに単純に移行することになるかと思います。

今回は、次のような形式で、GitLabの場合Group相当の部分にSVNのリポジトリーが存在し、その下にサブディレクトリーとして複数のプロジェクトが存在する場合、

```text
myproj
    ├─trunk
    │  ├─proj-a
    │  └─proj-b
    ├─branches
    │  ├─proj-a-hoge-fix
    │  └─proj-b-foo-func
    └─tags
       ├─proj-a-RELEASE_1_0
       ├─proj-a-RELEASE_2_0
       └─proj-b-RELEASE_1_0
```

これを、

- `https://gitlab.example.com/mygroup/proj-a.git` or `git@gitlab.example.com:mygroup/proj-a.git`
- `https://gitlab.example.com/mygroup/proj-b.git` or `git@gitlab.example.com:mygroup/proj-b.git`

という形で`Group/Project`という形で分けたい場合を想定しています。

## 作業方法

[前回の記事](https://qiita.com/hkato/items/1b092bff149b13f77505)の作業でSVNリポジトリーが次のGitリポジトリーに既に移行できているものとします。

- `https://gitlab.example.com/mygroup/myproj.git`

### 該当プロジェクトディレクトリーだけを抽出する

`myproj` を `proj-a` ディレクトリーにcloneし、

```sh
$ git clone git@gitlab.example.com:mygroup/myproj.git proj-a
$ cd proj-a/
```

`proj-a`を抽出しそれ以外を削除

```sh
$ git filter-branch --prune-empty --subdirectory-filter proj-a master
```
これで、元の `myproj/proj-a/*` の内容が `proj-a/*` に抽出され他は消えます。

### ブランチの抽出

```sh
$ for BRANCH_NAME in $(git branch -r | grep -e 'origin/myproj-a' | sed -e 's:origin/::');do
  git checkout "$BRANCH_NAME";
done
```
```sh
$ git checkout master
```

上記は、`proj-a` のブランチ名付与が `proj-a-hoge-fix` など `proj-a`で始まっているのが条件。

そうでない場合は、何が `proj-a` 用のSVNブランチであったかを予め確認しリストアップしたブランチ名をテキストに書いておいて、for文で回すなどするのが良いかと思われます。あるいは元のSVNリポジトリーの`branches`を確認しつつ手で、

```sh
$ git checkout hoge-fix
$ git checkout foo-func
.
.
```

するのも数が少なければ良いかと。

幸い、私のSVN環境ではブランチ名前を統一していたので、上記のようにfor文で回せました。

### 異なるプロジェクトのタグの削除

`proj-a` 以外の `proj-b`, `proj-c`, ... のタグが残っているので削除する必要があります。

もっと良い方法があるかもしれませんが、


```sh
$ for TAG_NAME in $(git tag -l | grep -v 'proj-b.*'); do
   git tag -d "$TAG_NAME";
done
$ for TAG_NAME in $(git tag -l | grep -v 'proj-c.*'); do
   git tag -d "$TAG_NAME";
done
.
.
```

こんな感じで消去。

こちらもブランチ同様に `proj-a-RELEASE_1.0` など `proj-a`で始まる名前をルールとしてタグ付けしていたので、上記で回せました。ブランチ同様に予めタグ一覧をテキストなどにしてfor文で回したり、GitLabなどをリモートリポジトリーにするなら、タグは残したままpushし、WebGUI上からポチポチと関係ないものを削除するのもアリかもしれません。

### リモートへの登録

まあ、ここらへんは普通にリモート先を変更してpushします。

```sh
$ git remote rename origin old-origin
$ git remote add origin git@gitlab.example.com:mygroup/proj-a.git
$ git push -u origin --all
$ git push -u origin --tags
```

### 繰り返し

以上を `proj-b`, `proj-c`, `proj-d`, ...で繰り返し、

- `https://gitlab.example.com/myproj/proj-a`
- `https://gitlab.example.com/myproj/proj-b`
- `https://gitlab.example.com/myproj/proj-c`
- `https://gitlab.example.com/myproj/proj-d`

などが移行できたら完了。

## まとめ

面倒くさいですね。
SVN上でブランチ名、タグ名をある程度ルール化していたから良かったものの、そうでなかったらもっと手間がかかるはず。