Mon, Dec 28, 2015

Gitのマージを図解する

Gitのマージを図解する

このエントリでは、Gitが提供するマージのための機能の内、主なもの4つ、真のマージリベースファストフォワードマージチェリーピック について図解する。 ここでマージとは、とあるブランチのコミットが入れた修正を別のブランチに取り込むこととする。

この記事を事前に読んでGitのオブジェクトモデルを理解しておくと分かりやすいかもしれない。

ここで説明するマージは全てローカルリポジトリ内のブランチを操作対象とする。


真のマージ

真のマージは、複数のブランチでそれぞれ開発が進んでいて、つまりそれぞれのコミットグラフが伸びている場合に、それらの修正を統合するときに実行する。 マージするブランチはいくつでも指定できる。

基本的なコマンドはgit merge <ブランチ(複数可)>

操作に成功すると、マージ後のプロジェクトの状態を表すコミット(マージコミット)が作られ、カレントブランチの先頭に追加される。 マージコミットは、マージした全てのブランチが指していたコミットを親として持つ。

このマージはマージコミットを追加するだけであり、既存のコミットを一切変更しないことを認識しておくべし。

以下、真のマージの実行例を図示する。

リベース

リベースは、あるブランチで作った一連のコミットの起点(ベース)を移動したいときに実行する。 この操作は一般的にはマージとは呼ばれないが、冒頭に書いたマージの定義からするとマージと見なせないこともないのでここに挙げる。

基本的なコマンドはgit rebase <ブランチ>。 このコマンドは、カレントブランチの起点を指定したブランチが指すコミットに移動する。

この操作に成功すると、カレントブランチで作ったコミットは(実質)消え、それと同等の修正をもたらす別のコミットが移動先のコミットを起点として作成される。(※1)

リベースは既存のコミットを消し、コミットグラフを変更してしまうということを認識しておくべし。

以下、リベースの簡単な実行例を図示する。


上のスライドのように単純なコミットグラフならいいが、リベースするブランチが分岐していたりするとややこしいことが起き得る。 そういうケースにはO’Reillyの蝙蝠本などでよく勉強してから臨むべし。

(※1: より正確にはgit rebase <ブランチ>は、

  1. カレントブランチで作った各コミットが入れた変更をパッチにして、
  2. それを古い順に一つずつ、指定したブランチが指すコミットに適用しながら新しいコミットを作り、
  3. カレントブランチが指しているコミットをORIG_HEADで指し、
  4. カレントブランチを最新のコミットを指すよう更新する。

2で、指定したブランチが既にチェリーピック(後述)などでカレントブランチのとあるコミットの変更を取り込んでいた場合、そのコミットのパッチの適用はスキップされ、そのパッチによるコミットも作られない。

また、上でカレントブランチのコミットは実質消えると書いたが、当面はオブジェクトが本当に消えるわけではないし、ORIG_HEADとかが指しているのでもどることもできる。)

ファストフォワードマージ

ファストフォワードマージは、マージ先のコミットが全てマージ元に含まれているときに使えるマージ。 この操作は既存のコミットグラフをいじらないしマージコミットも作らない特殊なマージ。 (実のところマージじゃないと言ってもいい。) このマージを実行した後は、コミットグラフは一直線になり、ブランチを作らずにコミットを作った場合と同様になる。

このマージは、git merge <ブランチ>を実行したときに可能であれば実行される。 (でなければ真のマージが実行される。オプションで選択することもできる。)

以下にファストフォワードマージの例を図示する。


ファストフォワードマージはよくリベースとともに実行される。 リベースのスライドの最後のページの図は、ここのスライドの最初のページの図と同じになっている。

リベース + ファストフォワードは、トピックブランチで入れた修正を、そのブランチを作ったという履歴を残さずに別のブランチに取り入れたいときなどに使う手法。 マージコミットを作る手法よりもコミットグラフをシンプルに保てる。

チェリーピック

チェリーピックは、あるブランチの任意のコミットによる修正を別のブランチに取り込みたいときに実行する。 他の3つのマージに比べて分かりやすい操作であり、また操作対象にするブランチやコミットの自由度が高いので使いやすい。 その反面、コミットログなどに明記しないとどこのコミットをマージしたのかが分からなくなる。

基本的なコマンドはgit cherry-pick <コミット>

操作に成功すると、指定したコミットと同等の修正をもたらす新しいコミットが作成され、HEADに追加される。

この操作はコミットを追加するだけであり、既存のコミットは変更しない。

以下にチェリーピックの例を図示する。