絶賛 Git 勉強中でして、自分向けのメモ書きとしてマージとリベースの違いを調べて整理してみました。
GitHubにはほかのブランチの更新分を自分のブランチに取り込む際、git merge/git rebaseの二つのコマンドがありますが、その違いを整理してみました。
やったこと
devブランチから派生した dev_#50, dev_#60があって並行で開発していたとして、dev_#60 がさきにdevにマージされたとします。dev_#50の開発者は、書き換えられたdevの更新分を取り込んだうえで、さらにdevへ自分の更新分をマージしてもらいたいわけですが、devの更新分を「マージ」で取り込んだ場合と「リベース」で取り込んだ場合で、どうなるかって比較をしてみました。
臨場感(?)を出すため、マージする時に修正をコンフリクトするようにしてあります。
public class Calc{
public double execute(int source){
return source ;
}
}
こんなソースに、
dev_#60で修正
public class Calc{
public double execute(int source){
// 消費税対応1.05倍 #60 2016/12/26
return source * 1.05;
}
}
こんな修正や
dev_#50で修正
public class Calc{
public double execute(int source){
/* 消費税対応1.08倍 #50 2016/12/26 */
return source * 1.08;
}
}
こんな修正を入れてコンフリクトさせます。
繰り返しですが、dev_#60分はdevに取込済みで、それをふまえてdev_#50の更新をdevへ取り込むケースを考えます。
初期設定
mkdir sample && cd $_
git init
Calc.javaを作成し、コミットします
git add Calc.java && git commit -m 'initial'
利用するブランチを作成します。
git checkout -b dev && git checkout -b dev_#50 && git checkout -b dev_#60
まずdev_#60でソースを修正し、コミットとdevへのマージを行います
git commit -a -m '消費税対応1.05倍 #60 2016/12/26'
git checkout dev
git merge --no-ff dev_#60
つづいて、dev_#50でソースを修正し、コミットまでしておきます。
git checkout dev_#50
ここでコード修正。
git commit -a -m '消費税対応1.08倍 #50 2016/12/26'
ここまでで、dev,dev_#60,dev_#50 の状態は下記のようになりました。
マージ
まずは、普通にマージします。
$ git checkout dev_#50
$ git merge dev
このとき、競合が起きますが、落ち着いて修正を行い、
$ git add Calc.java
$ git commit -m 'merge commit'
でマージ完了です。さらに、dev側にマージします
$ git checkout dev
$ git merge --no-ff dev_#50
さて、このマージが完了したdevですが、
$ git checkout dev_#50
$ git merge dev
このマージはすでにdev_#50で修正されたところに dev分(すなわちdev_#60の修正)をマージしています。 そしてそれがdevへマージされているのがわかります。
これはこれでOKなんですが、devからみると、dev_#60でのマージ履歴はいらない情報だったりしますね。
リベース
そこでリベースです。
$ git checkout dev_#50
$ git rebase dev
このとき競合がでてなんかごっちゃごちゃ言われますが、落ち着いて修正を行い、
$ git add Calc.java
$ git rebase --continue
ってやってリベースのcontinueを選びます。これでリベース完了です。リベースの考えかたに従い、まずdev_#50のコミットがいったん待避され、devの修正が取り込まれて、さらにdev_#50の新規コミットが行われます。当然、もとのdev_#50のコミットとは別のコミットですね。
ちなみに、rebase の競合をキレイにするのっていろいろめんどくさく、その手順は
に丁寧にまとめてありました。感謝します!
さて、さらにdev側にマージします
$ git checkout dev
$ git merge --no-ff dev_#50
今回はマージでなくてリベースを行ったので、devに取り込んだもの(すなわちdev_#60の修正)のつぎに、dev_#50の修正がマージされました。dev_#50が「dev_#60を取り込む前のdev」からブランチした事実は'なかったこと'になってます。
まとめ
マージは、文字通りのマージで、devの修正をdev_#50の「直近状態」にマージしました。最終的には、その事実も含めてdevにマージされていきました。
リベースは文字通り、re-baseすることで、dev_#60を取り込んだdevをベースに、dev_#50の更新分がコミットされ、そしてdevにマージされていきました。
今回みたいなdevを中心にチケット毎のブランチで開発するようなケースだと、devでの更新分を取り込むようなケースでは、リベースをするのが正しそうですね。。。