SVNユーザがGitを使う上での要点と使い分けについて

# SVNとGit

私がいる環境では比較的Subversion(SVN)ユーザが多いが、ある時Gitが導入され、
それ以来よくGitの使い方について基本的な質問や相談を受けるようになった。
(私の場合、Mercurialを使ってるのでそれほど抵抗はなかった)

本稿は、私がそれらの質問や相談を受けていて感じた、
SVNユーザがGitを理解する上でのポイントや、
SVNとGitを使ってみた上での使い分けに関する情報をまとめたものである。

## SVNユーザがGitを難しく感じるポイント？

SVNもGitも同じVCS (バージョン管理システム)だが、
SVNの操作に慣れているヒトがGitを触れる上で、以下のポイントがネックになるようだ。

* 集中型VCSと分散型VCS
* ブランチに対する考え方

### 集中型VCSと分散型VCS

VCSには集中型と分散型がある。
SVNが前者、Gitが後者である。それぞれの特性は以下の通り。

|分類|例|説明|
|:----:|:----|:----|
|集中型|SVNなど|リモートサーバにある単一のリポジトリを皆で共有する。|
|分散型|Git,Mercurialなど|ユーザのマシンごとにそれぞれリポジトリがあり、リモートサーバ経由でバージョン情報 (変更情報)を交換する|

イメージにすると以下の通りである。
※円柱の図がリポジトリを表す。

---

* 集中型VCS

<img src="https://qiita-image-store.s3.amazonaws.com/0/263164/77ab8357-6478-d64e-6082-c0aa05c137fa.png" width="400px">

* 分散型VCS

<img src="https://qiita-image-store.s3.amazonaws.com/0/263164/c9e3cfb5-caf6-fcb0-0640-bc54d98a9267.png" width="400px">

---

VCSでは **コミット** によってリポジトリに変更情報をバージョンとして記録し、
**チェックアウト** によって特定のバージョンに切り替える。
SVNとGitもこの考えは同様であるが、
集中型VCSと分散型VCSでは、対象リポジトリがリモートサーバにあるか、
ローカルにあるかが異なる。
つまりSVNのコミットの感覚で、Gitのコミットを行ってもリモートサーバには反映されない。
なぜなら、分散型VCSでは自分のリポジトリはローカルにあるからだ。
(このような違いの所為か、GitのPushやPull(Fetch)がSVNのコミットやチェックアウトのようなものと
考えるユーザがいるとかいないとか……)

また、分散VCSの場合、リモートリポジトリとローカルリポジトリで
バージョン情報を連携するための機能が存在する。
それがPushやFetchと呼ばれる機能である。

リポジトリ間の操作が集中型VCSにはない機能であること、
そして前述したように集中型VCSと分散VCSで対象リポジトリの所在が異なることが、
SVNユーザがGitを触る上で理解を難しくする1つの要因のようだ。

尚、PushやFetchはオンラインでなければ当然利用できないが、
リモートサーバのバージョンへの変更(チェックアウト)やログの確認などは
オフラインでも利用可能である。
GitのPushやFetchをSVNのコミットやチェックアウトの延長線上で考えるヒトにとっては、
この動作は理解し難いようだが、
Fetchがリモートリポジトリの変更情報をローカルリポジトリに取得するもので、
チェックアウトやログの確認がローカルリポジトリに対しての操作であることを踏まえると、
その動きが妥当であることは分かるはずだ。
(Gitのチェックアウトでリモートサーバの最新にする場合、
実際のリモートサーバの最新情報ではなく、
最後にfetchした際の最新バージョンになるのはこのため)

## ブランチ

SVNとGitではブランチに対する考え方が異なる。
SVNではブランチを作成する場合はそれぞれのプロジェクトが独立し、
基本的に合流するようなことがない場合に利用される。
しかし、Gitでは並列に開発を行う場合や、作業担当を分けたい場合など、
管理単位を分けたい場合にブランチを作成する。
それゆえ、Gitでは頻繁にブランチの作成やマージ、削除などが行われる。
ブランチを細かく分けるメリットには以下のような点が挙げられる。

* バグ対応や開発案件などの時期が重なる場合、SVNではブランチの作成を躊躇う場面だが、Gitではとりあえずブランチを作成し、必要に応じてマージ、不要になれば削除すれば良い。
* 実装者や機能単位でブランチが分かれていれば、レビュー時に他のメンバの実装や別の機能の実装が含まれることがない。
* ブランチの記録が残るため、トレーサビリティが高い。

## SVNとGitの使い分け

### SVNとGitの長所・短所

私見だが、それぞれのVCSには以下のような長所と短所を感じた。

| 項目 | SVN | Git | 備考 |
|:----:|:----:|:----:|:----|
| ロック機構|あり|なし|バイナリを扱う場合はあった方が便利だが、ロックされたまま放置されるリスクもある。|
| 競合に関する方針|基本的に回避。競合した場合は解決。|競合したら解決。|分散VCSやブランチへにそれがよく現れてると感じた。|
|学習コスト|簡単|大変|分散VCSの方が覚えることが多い上、Gitは多機能なので。|
|コミット取り消し|不可能|可能||
|コミットログ編集|簡単|大変|Gitでコミットログを編集する場合はコミットを取り消して再コミットする。|
|部分チェックアウト|得意|苦手|Gitも一応できると聞いたが、試したことはないです。|
|オフライン利用|不可能|可能||
|複数のコミットをまとめる|不可能|可能|一般的に中途半端な状態でコミットするなと言われるが、これを使えば中途半端な状態でコミットしてもそれを後からなくすことができる。|
|Hooks|集中管理|ユーザごとに管理|SVNの場合、コミットに対する設定を一元管理できる。|

この他にもGitは空ディレクトリなどがコミットできない、などの機能的な違いもあるようだ。

### プロジェクト特性に応じた使い分け

プロジェクト特性で考えた場合、2つのVCSはそれぞれ以下のようなプロジェクトに向いていると考える。

* SVN
 * リリースが安定している場合。
 * バイナリが多い場合。
 * プロジェクト人数が少ない場合。(多くても良いが競合が少ないこと)
 * 資源を一括管理し、それについてのHooksを設定したい場合。(同じチェックで条件に合わなければコミットさせない、といった制御も可能)
* Git
 * バグ対応や並行開発などが多く、リリースが安定しない場合。
 * プロジェクト人数が多い場合。
 * 1人だけで開発を行う場合。
 * オフラインでの作業が多い場合。
 * 資源を他のヒトに提供しやすくしたい場合(GitHubやGitHubクローンアプリを使えば尚のこと)


## 最後に

最近ではSVNとGitの利用頻度は半分ずつぐらいであるが、やはり使い分けると便利である。
以前、リリースが安定しないプロジェクトでSVNを使ったり、Gitでコミットログ規約が面倒な(以前のコミットログを編集したくなる)案件に関わったりしたが、手間が多く、ツールに使われてる気分になった。
やはりVCSも作業効率化のためのツールである以上、用途を理解して使い分けるのが1番幸せそうである。