[日本語訳]A successful Git branching model

2010年、オランダのエンジニアVincent Driessenさんの一提案に過ぎなかったgitのブランチモデル　**A successful Git branching model** はその後、世界中に広く浸透し今やgit運用モデルとしてデファクトスタンダードになりました。

今回、Vincent Driessenさんにご快諾いただき日本語訳を掲載することになりました。

![nvie@2x.jpg](https://qiita-image-store.s3.amazonaws.com/0/131316/3af8ad6b-7e96-25b6-deb8-7ce8e66e53fc.jpeg)

Org:
http://nvie.com/posts/a-successful-git-branching-model/

Contact:
me@nvie.com
twitter.com/nvie
github.com/nvie
keybase.io/nvie

# A successful Git branching model
## はじめに
　私がとあるプロジェクト(業務、プライベート双方)で1年ほど前に実際に運用してみて、とてもうまく運用できたと判った開発モデルに関して紹介してみたいと思います。とりあえず書き留めておこうと思ったものの、ちゃんと書き残しておく時間を確保できませんでした。ここでは、プロジェクトそのものの細かい話ではなく、単にブランチの運用方法とリリースの運用方法に関して紹介したいと思います。

![git-model@2x.png](https://qiita-image-store.s3.amazonaws.com/0/131316/8efe10bb-ffc5-6aad-6534-f56cb30a9e82.png)

Gitをソースコードのバージョン管理ツールとして注目してみましょう。

## なぜGitを使うの?
　中央管理型のソースコード管理ツールと比較して、Gitの⻑所/短所を議論したい場合こちらをご覧ください。
https://git.wiki.kernel.org/index.php/GitSvnComparsion
この件に関しては様々な議論がありますが、私はデベロッパーの一人として他のどのツールよりもGitを推したいです。(*2010年当時の文書です*) Gitはマージやブランチに対するデベロッパの考え方を大きく変えました。私は、CVSやSubversionを使っていたのでマージやブランチに少し不安がありました。(マージコンフリクトとかウンザリだし!)
でも、Gitになってからこれらの操作はとてもお手軽でシンプルになって、日常的なワークフローの一環で使用するごく普通の操作の一つになりました。例えば、CVSやSubversionの解説書などでは、ブランチやマージは上級者向けという扱いで後半の章にありますが、Gitの解説書だと基本の3章には登場してきます。
  この結果、ブランチすることやマージすることはもはや別に怖いことではなくなりました。
  ツールのお話はこのへんにしておいて、開発モデルに関して考えてみましょう。私がここで紹介しようとしている運用方法は、チームメンバー各個人がソフトウェアの開発進行管理をフォローするための一連の手続きの一つに過ぎません。

## 分散しているけど中央集権
　ここで紹介するブランチ運用方法がうまく機能するための前準備として、本当の意味での中央リポジトリをセットアップします。注意点として、このリポジトリはただ一つの中央リポジトリとして解釈する点です。というのも、Gitそのもの **DVCS(Distributed Version Control System)**であって、中央リポジトリという概念はないためです。この中央リポジトリを**origin**として参照することにしましょう。この**origin**という名称は、Gitユーザにとっても馴染みがあるためです。

![centr-decentr@2x.png](https://qiita-image-store.s3.amazonaws.com/0/131316/9c26edf3-62bd-8d12-38cc-a1a9028f7067.png)
　デベロッパはみんな、pullやpushはoriginに対してします。でも、中央へのpush/ pullとは別に、各デベロッパは他のデベロッパと相互に変更点をpullすることができます。例えば、2人以上のデベロッパが協力して大きな新機能を取り扱う際に役立ちます。新機能が作業途中な状態で**origin**にpushしてしまうことを回避することができるためです。
　厳密には、これはAliceがbobという名のリモートリポジトリとして、Bobのリポジトリを参照するように定義している過ぎないのです。逆もまたしかりです。

## メイン・ブランチ
　基本的なところでは、一般に出回っている手法から大きく影響を受けています。中央リポジトリには恒久的に存在する2つのメインとなるブランチがあります。

- master
- develop
![main-branches@2x.png](https://qiita-image-store.s3.amazonaws.com/0/131316/218214df-32a3-99b0-c03d-6a615941f99d.png)

　originリポジトリのmasterブランチであればGitユーザであれば皆さんお馴染みですよね。そのmasterブランチと並んで、もう一方のブランチがdevelopと呼ばれるブランチが存在します。
　私たちは、origin/masterブランチのHEADをいつでも出荷できる状態のmainブランチとして扱うことにします。
　そして、origin/developブランチのHEADは常に次のリリースに向けた最新版となるように扱うことにします。これを統合ブランチと呼ぶ人もいます。夜間自動ビルドの対象となるのもこのブランチです。(*2010年当時jenkinsは今ほど普及していなかった*)
　developブランチにあるソースコードが安定してリリースできる状態になった時点で、全ての変更点はmasterブランチへとどうにかしてマージして戻して、リリースナンバーのタグを打ち込みます。このやり方の細かいところは今も議論中です。
　つまり、変更点がmasterブランチにマージして戻すたびに、製品が新しくリリースされることになります。これを厳守すれば、理論的には、Git hook scriptを使って masterブランチにcommitがあるたびに自動的にビルドして製品を出荷することが出来ます。

## サポートブランチ
 masterブランチやdevelopブランチとは別に、私たちの開発時の運用ではいろいろなサポート・ブランチを使っていました。これらのサポートブランチはチームメンバー内での並行開発を補助したり、機能のトラッキングを簡単にしたり、製品のリリース準備をしたり、製品にある実際の問題を手早く修正したりする用途に使います。メインブランチとは違って、これらサポートブランチは常に限られた寿命があり、最終的には削除されます。

 使用するサポートブランチには

- 機能ブランチ (Feature branches)
- リリースブランチ(Release branches)
- Hotfixブランチ(Hotfix branches)

 などがあります。それぞれのブランチには特定の目的があり、どのブランチが起源でどのブランチがマージ対象としなければならないという厳格なルールがあります。ちょっと、これらについて説明したいと思います。
 Git的な概念としては、特別なブランチというわけではありません。これらのブランチをどのように運用するかでカテゴライズされます。もちろん、Git的にはごく普通のブランチに過ぎません。

### 機能ブランチ(Feature branches)

|  |  |
|:-:|:-:|
|ブランチ元| develop  |
|マージ先| develop |
|ブランチ名ルール| master, develop, release-?, or hotfix-? 以外  |

![fb@2x.png](https://qiita-image-store.s3.amazonaws.com/0/131316/3fb950b7-748a-5f9c-ce00-8eb59552f9cd.png)
 機能ブランチ(トピックブランチと呼ばれることもあります)は、近々発売される製品だとか、遠い先のリリースに 向けて新しい機能を実装する際に使用されます。機能ブランチの実装が開始された時点では、この機能ブランチがどのタイミングで統合されるのか確定していません。重要なのは、機能ブランチは実装が継続している間は存在し続けるのだけど、実装が終わった段階で出荷されるリリースに新機能としてdevelopブランチにマージされるか、あるいは残念な場合は切り捨てられます。大抵の場合、機能ブランチは開発メンバーのローカルのリポジトリのみに存在し、**origin**には存在しません。

#### 機能ブランチの作成

新しい機能ブランチを作成するには、developブランチからブランチします。

```
git checkout -b myfeature develop
“myfeature” という名の新しいブランチの作成と同時に、切り替えます
```

#### 実装が完了した機能ブランチのdevelopへの統合

実装を終えた機能ブランチは、リリースに備えて確実にdevelopブランチへと統合しておきましょう。

```
$ git checkout develop
developブランチへ切り替えます

$ git merge --no-ff myfeature
Updating ea1b82a..05e9557 (Summary of changes)

$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
myfeatureブランチは削除しておきます

$ git push origin develop
```
 --no-ffオプションを指定しておくと、fast­forwadが可能の場合であっても常に新しいCommitオブジェクトを作成します。こうしておくと、機能ブランチの履歴やグルー プにいるメンバーの全てのCommit記録が追加先のブランチに残ります。

![merge-without-ff@2x.png](https://qiita-image-store.s3.amazonaws.com/0/131316/8031fe56-b1a6-ebe7-42a0-090985c4d646.png)

 後者(plain)のケースだと、Gitの履歴からどのCommitオブジェクトがある特定の機能実装に相当するのか分からなくなります。logメッセージから手動で読まなければなりません。複数のCommitから構成されているある特定の機能をrevertする場合、後者のケースだと本当に大変です。その一方で --no-ff オプションを付けておくけば簡単です。
 確かに、--no-ffは少しだけ空のCommitオブジェクトを作成するけど、費用対効果はとても高いのです。

### リリースブランチ

|||
|:-:|:-:|
|ブランチ元|develop|
|マージ先|develop と master|
|ブランチ名ルール|release-?|

 リリースブランチは製品の新規リリースの準備をサポートします。最後の微調整をしても構いません。さらに、小さなバグ修正や、バージョン表記やビルド・タイムスタンプなどのメタデータの調整をしても構いません。これらの調整作業をリリースブランチに対して行うことで、developブランチは次の大きなリリースに向けた機能拡張に専念できます。
 developからブランチさせて新規リリースブランチを作成するタイミングは、developブランチの状態が新しくリリースさせても(ほとんど)良い状態になった時です。少なくともこの時点で、この新規リリース対象に含めるべき全ての機能ブランチはdevelopブランチにマージされていなければなりません。次のリリースに向けた全ての機能ブランチは、この新規リリースブランチが作成されるまでdevelopブランチへのマージは待たなければなりません。

#### リリースブランチの作成
　リリースブランチはdevelopからブランチすることで作成します。例えば現在の製品リリースのversionが1.1.5で、大きなリリースが近づいているとします。developブランチの状態は次のリリースに向けて準備が整っている状態で、これをversion　1.2 (1.1.6でもなく2.0でもなく)にすることを決めたとします。この場合、ブランチを切って新しいversion番号を反映させたブランチ名をつけてあげます。

```
$ git checkout -b release-1.2 develop
release-1.2ブランチへ切り替えます

$ ./bump-version.sh 1.2
Files modified successfully, version bumped to 1.2.

$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2 1 files changed, 1 insertions(+), 1 deletions(-)
```
 新しくブランチを作成して、そのブランチに切り替えた後、そのVersionへと引き上がります。このbump-version.shというのはワークにあるFileをCopyすることで新しいversionを反映させる仮のスクリプトとします。(もちろん、これは手段による変更でも可能です。重要なのは、あるFileが変更されている点です)そして、引き上げられたversion番号はコミットされます。
　この新しいブランチはしばらくの間、このリリースが完全に出荷されるまで存在し続けるます。この期間でのバグ修正などは(developブランチに対してではなく)このリリースブランチに対して適用されます。このリリースブランチに対して大きな新しい機能の追加は絶対に禁止です。リリースブランチはdevelopブランチにマージされるので、 次回の大きなリリースまで待ってください。

#### リリースブランチの終了
 リリースブランチが本当にリリース可能な状態になった時、やっておくべきことがあります。まず、リリースブランチをmasterに対してマージする必要があります。(*というのも、masterブランチに対する全てのコミットは原則的に新しいリリースだからです、覚えておきましょう*)
次に、このmasterに対するマージコミットに対して、後で履歴を見やすくするためにもタグを打っておかなければなりません。最後に、リリースブランチに対して行った変更点はdevelopブランチにマージして戻しておく必要があります。

```
$ git checkout master
masterブランチへ切り替えます

$ git merge --no-ff release-1.2
Merge made by recursive. (Summary of changes)

$ git tag -a 1.2
```

リリースされたら、後のためにもタグを打っておきます。
リリースブランチに対する変更点を残しておきたい場合、developブランチにもマージして戻しておく必要があります。

```
$ git checkout develop
developブランチへ切り替えます

git merge --no-ff release-1.2
Merge made by recursive. (Summary of changes)
```
この段階では、マージコンフリクトが起きてしまうかもしれません。(*多分、version番号を変えたところとか*)その場合、修正してcommitします。
  もう全て終えたのでリリースブランチを削除しましょう。というのも、リリースブランチはもう必要ないからです。

```
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).
```

### Hotfixブランチ(Hotfix branches)

|||
|:-:|:-:|
|ブランチ元|master|
|マージ先|developとmaster|
|ブランチ名ルール|hotfix-?|
![hotfix-branches@2x.png](https://qiita-image-store.s3.amazonaws.com/0/131316/bb10bf6a-ea69-3387-22b7-a17bf02a6d2f.png)
Hotfixブランチは、次の製品リリースへの準備という意味ではリリースブランチと良く似ていますが、意図的なものではありません。出荷中の製品で修正すべき不具合が発覚して即座に処置を施したい場合にHotfixブランチを切ります。深刻な不具合を緊急で修正したい場合、Hotfixブランチはmasterブランチの対応する製品リリースのタグからブランチで切ります。重要なのは、担当者が製品の修正作業中であっても、開発チームのメ ンバーがdevelopブランチで作業を継続できる点です。

#### Hotfixブランチの作成
　Hofixブランチはmasterブランチから作成されます。例えば、現在出荷されている製品Versionが1.2だったとして、その製品に深刻な不具合による障害が起きていたとします。でもdevelopブランチはまだ不安定だったとします。そんな時はHotfixブランチを作成してから問題の対応を始めましょう。

```
$ git checkout -b hotfix-1.2.1 master
Switched to a new branch "hotfix-1.2.1"

$ ./bump-version.sh 1.2.1
Files modified successfully, version bumped to 1.2.1.

$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1 1 files changed, 1 insertions(+), 1 deletions(-)
```
ブランチ切った際に、versionを引き上げることを忘れずに!
そして問題となっているバグを修正して、1つか複数のCommitで修正をCommitします。

```
$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem 5 files changed, 32 insertions(+), 17 deletions(-)
```

#### Hotfixブランチの終了
 修正が終わったら、このBugFixはmaserブランチにマージで戻しておく必要がありますが、developブランチにもマージで戻しておく必要があります。次のリリースにもこのBugFixを適用させるためです。リリースブランチの終わらせ方と全く同じです。
まず、masterブランチのリリースタグを更新します。

```
$ git checkout master
Switched to branch 'master'

$ git merge --no-ff hotfix-1.2.1
Merge made by recursive. (Summary of changes)

$ git tag -a 1.2.1
```
次にbugfixをdevelopブランチにも入れ込んでおきます。

```
$ git checkout develop
Switched to branch 'develop'

$ git merge --no-ff hotfix-1.2.1
Merge made by recursive. (Summary of changes)
```
ここで、一つだけ例外ルールがあって、現在進行中のリリースブランチが存在している時は、このhotfixの変更はdevelopブランチの代わりに、そのリリースブランチに対してマージさせておく必要があるという点です。Bugfixをリリースブランチに戻しておけば、やがてそのBugfixは、そのリリースブランチの終了時にdevelopブランチにもマージされていきます。もし作業中のdevelopブランチで即座にそのbugfixを必要として、そのリリースブランチの終了まで待てない場合、そのbugfixをdevelopブランチに対して今までどおり間違わずにマージしても良いでしょう。
  最後に、このテンポラリブランチを削除します。

```
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).
```

## まとめ
　特にこれといった目新しいことは、このブランチ運用方法にはないのですが、この全体像は私たちのプロジェクトにおいて、とても実用的であることが判りました。この洗練された運用モデルを通して、チームメンバーがブランチとリリースの流れの理解を共有することが出来ました。