git-svnを利用した運用を考える

最近、諸事情あって`subversion`から`git`へプロジェクトの半ばでバージョン管理を変更しました。その際にちょっと変わった形で`git-svn`の運用を行う事になったのでやりかたに関して簡単にログを残しておきます。

![](http://subversion.apache.org/images/svn-square.jpg)![](http://subversion.apache.org/images/svn-name-banner.jpg) ![](http://git-scm.com/images/logos/2color-lightbg@2x.png)

# プロジェクトを取り巻く事情
今まではSubversion(仮にメインとします)を用いた運用を行っていたのですが、気軽にtrunkに対してコミットする事が出来ない事情がありました。では、メインSubversion上にbranchをたてればいいじゃんという話になるのですが、ブランチすら気軽に切る事が出来ない状態でした。その上、メインのSubversionにコミットすることが可能なのはコミッターだけとなっておりコミッターの負担が増えつつありました。

## 今までの運用
今まではメインSubversionとは別に **同期しないSubversion(仮にローカルとします)**を別途立てて、なんと手動でメイン<=>ローカル間の同期を取りながら運用を行ってきました。
![Untitled.png](https://qiita-image-store.s3.amazonaws.com/0/11214/ec0f3a4b-c7c8-f7d6-d6fd-22f6983afdfb.png "Untitled.png")
*※破線の部分はVCSによって連携していない部分です。*

当然ですがこの運用ははっきり言って色んな無理があり様々な問題が発生しました。
例えば、、、

* メインからローカルへ **ファイル単位で取り込みを行う必要があった**。
* メインからローカルへ **取り込むタイミングが保証出来なかった**。
* メインからローカルへ取り込みを行った際に **どの変更を取り込んだかログが紛失してしまう**
* メインのブランチが増えた場合に **同期しなければいけないブランチが増える**
* ローカルからメインへコミットする際にコミッターがコミットを行うが、 **ファイル単位**でメール添付で行っておりミスがしやすい。
* ***何より色々めんどくさい!!***

.. まぁ、正気の沙汰じゃないですね。

# git-svnを利用した解決策を探る
まだコミットの粒度が大きい場合は、 **コミッターの注意深い対応と開発者の空気を読んだ行動**により、この運用でもなんとかなっていました。
しかしながら障害対応なども割り込んでくるため個別に行う必要がありカオスな状況になっていきます。
何よりこんなリスキーな運用を回しきれる訳がありませんし、自分がどこかで間違える自信があります。
そこで`git-svn`を導入する事でローカルの`Subversion`を`git`に置き換えて同期させる方法を検討しました。

基本的には、`git-svn`を用いた通常のワークフローで問題なかったのですがいくつか問題がありました。

### コミッターの制限
メインのSubversionへコミットが可能なのはコミッターのみでそれ以外のユーザがコミットをする事が禁止されています。

### コメントの問題
ある特定の書式に従ってコミットコメントを書く必要があり、マージ運用を行った場合に問題が発生する可能性がありました。

### gitの習熟度
全員で10名ほどのプロジェクトですが、数名をのぞき`git`の習熟度がとても低い状態でした(というか知らない状態)。その状態で`git`のみではなく`git-svn`を利用出来る状態は危険な状態が予想されました。

# というわけで解決策
これらの問題を考慮した上で以下のような方針で行うようにしました。
リポジトリの構成は以下のようになります。
![Untitled(1).png](https://qiita-image-store.s3.amazonaws.com/0/11214/d2e5038b-3140-735d-9a22-5381f215c7f0.png "Untitled(1).png")


1. コミッターのみ`git-svn`を利用する。
1. その他の開発者は`git`のみを利用する。
1.  開発者はgitの共用リポジトリを参照し、それをcloneして開発を行う。
1. 共用リポジトリはコミッターが所有する物とする。
1. 共用リポジトリの`master`はメインSubversionのtrunkとなる。
1. 共用リポジトリの`svn/`から始まるブランチは、メインSubversionのブランチとなる
1. master及びsvn/のリポジトリに対して開発者が直接コミットを行う事は出来ず、コミット可能なのはコミッターのみとする。

## コミッターの流れ
### 最新化(メインSubversionからの変更履歴の取り込み)
1. `git svn rebase` を用いて、メインSubversionの変更を取り込みます。
2. `git push`で共用リポジトリにメインSubversionの変更をpushする。

### ソースの反映(Pull Requestの取り込み)
githubライクに開発者からPullRequestを受け取る事で、メインSubversionへのコミットを行います。

1. `git checkout master`でブランチ対象のリポジトリに移動します。
2. `git svn rebase` でまずメインSubversionと同期します。
3. `git push` によってメインSubversionと共用リポジトリの内容を同期させます。
4. `git fetch`で共用リポジトリにpushされたブランチ内容を取得します。
5. `git merge --no-ff --no-commit BRANCH_NAME` で変更内容を`maste`に取り込みます。
6. 取りこんだ内容が問題ないか動作確認を行います。
7. 問題ないようであれば、`git commit`で`master`にコミットを行います。この際にメインSubversionにコミットするコミットコメントを記述します。
8. `git svn dcommit -n`で問題なくメインSubversionへコミットできるか確認を行います。
9. `git svn dcommit --interactive`でコミットされるログなどを確認します。
10. 問題ないようであれば`git svn dcommit`で変更内容をメインSubversionへ送信します。
11. 最後に`git push`で共用リポジトリにコミットした内容を反映させます。

### ブランチマージ時のコンフリクト
上記5において、当然コンフリクトが発生する可能性があります。その場合はそのPullRequestの差し戻しを行います。この辺りは通常のgithubのPullRequestと同じ運用ですね。

## 開発者の流れ

開発者は共有gitのみを参照しながら作業します。

### 新規ブランチ作成からコミットまでの流れ

1. `git clone git-share local-git` でローカルに共有gitをcloneします。
2. `git checkout master`でマスターブランチへ切り替えます
3. `git checkout -b issue/xxx`で障害対応管理単位でブランチを切ります。
4. 修正してコミットを繰り返します。
5. 修正が完了したタイミングで、`git push origin issue/xxx:issue/xxx`でローカル変更を共用リポジトリにpushします。
6. `git request-pull`で修正した内容を取り込んでもらうようにコミッターにメールを行います(当然口頭でも一言言いますが)

### 共有gitの内容をローカルへ

当然定期的にメインSubversionの内容をローカルへ反映させる必要があります。任意のタイミング以外でも、上記5の`git push`を行うタイミングの前などで以下の手順で最新化します。

1. `git checkout master`で共有gitと同期させているmasterへ移行します。
2. `git pull --rebase`で最新の変更を取り込みます。
    ＃ ローカルのmasterは共用リポジトリのmasterであり、メインSubversionのtrunkとなるため、 `pull`は `--rebase`を強制とします。


# 問題点

## 共有gitとメインSubversionの差分をコミッターしか反映出来ない
この点に関してはgitの習熟度に応じて各メンバーにgit-svnの利用を覚えてもらう事で、メインSubversionから共有gitへのルートを広げました。


# 最後にちょっとだけアクセントを
基本的にはこの構成で問題なかったのですが、共用リポジトリを見る方法を準備していませんでした。
そこで、今回はアトラシアン社の[Stash](https://www.atlassian.com/ja/software/stash/overview/feature-overview)を導入しています。

![](https://www.atlassian.com/ja/wac/software/stash/overview/productLogo/imageBinary/stash_logo_productspage.png)

今なら [GitBucket](https://github.com/takezoe/gitbucket) という選択肢があったのですが当時はちょうどリリースする前で断念しました。

ただ、Stashのスターターライセンスは有料なだけあって、PullRequestや権限回りがしっかりしていたのでみんながGitのPush/Pull、PullRequestの概念になれるのにとても役に立ったと思います。

というわけですので、SVNからgitに移行しようとしていて躊躇している方は一度試してみてはいかがでしょうか。