GitHubのマージとリベースについて

絶賛 Git 勉強中でして、自分向けのメモ書きとしてマージとリベースの違いを調べて整理してみました。

GitHubにはほかのブランチの更新分を自分のブランチに取り込む際、git merge/git rebaseの二つのコマンドがありますが、その違いを整理してみました。

## やったこと
devブランチから派生した dev_#50, dev_#60があって並行で開発していたとして、dev_#60 がさきにdevにマージされたとします。dev_#50の開発者は、書き換えられたdevの更新分を取り込んだうえで、さらにdevへ自分の更新分をマージしてもらいたいわけですが、devの更新分を「マージ」で取り込んだ場合と「リベース」で取り込んだ場合で、どうなるかって比較をしてみました。

臨場感(?)を出すため、マージする時に修正をコンフリクトするようにしてあります。

```java:Calc.java
public class Calc{
  public double execute(int source){
    return source ;
  }

}
```

こんなソースに、

dev_#60で修正

```java:Calc.java
public class Calc{
  public double execute(int source){
   // 消費税対応1.05倍 #60 2016/12/26
   return source * 1.05;
  }

}
```

こんな修正や

dev_#50で修正

```java:Calc.java
public class Calc{
  public double execute(int source){
   /* 消費税対応1.08倍 #50 2016/12/26 */
   return source * 1.08;
 }

}
```

こんな修正を入れてコンフリクトさせます。

繰り返しですが、dev_#60分はdevに取込済みで、それをふまえてdev_#50の更新をdevへ取り込むケースを考えます。

## 初期設定

```bash
mkdir sample && cd $_
git init
```

Calc.javaを作成し、コミットします

```bash
git add Calc.java  && git commit -m 'initial'
```

利用するブランチを作成します。

```bash
git checkout -b dev && git checkout -b dev_#50 && git checkout -b dev_#60
```

まずdev_#60でソースを修正し、コミットとdevへのマージを行います

```bash
git commit -a -m '消費税対応1.05倍 #60 2016/12/26'
git checkout dev
git merge --no-ff dev_#60
```

つづいて、dev_#50でソースを修正し、コミットまでしておきます。

```bash
git checkout dev_#50
ここでコード修正。
git commit -a -m '消費税対応1.08倍 #50 2016/12/26'
```


ここまでで、dev,dev_#60,dev_#50 の状態は下記のようになりました。

![before.png](https://qiita-image-store.s3.amazonaws.com/0/73777/89f0e42e-25d7-d44b-62e2-2be3e0fa3474.png "before.png")






## マージ
まずは、普通にマージします。

```bash
$ git checkout dev_#50
$ git merge dev
```


このとき、競合が起きますが、落ち着いて修正を行い、

```bash
$ git add Calc.java
$ git commit -m 'merge commit'
```

でマージ完了です。さらに、dev側にマージします

```bash
$ git checkout dev
$ git merge --no-ff dev_#50
```

さて、このマージが完了したdevですが、

```bash
$ git checkout dev_#50
$ git merge dev
```

このマージはすでにdev_#50で修正されたところに dev分(すなわちdev_#60の修正)をマージしています。 そしてそれがdevへマージされているのがわかります。

![merge.png](https://qiita-image-store.s3.amazonaws.com/0/73777/82274740-a344-5fc9-4d68-eace360e0f00.png "merge.png")


これはこれでOKなんですが、devからみると、dev_#60でのマージ履歴はいらない情報だったりしますね。


## リベース
そこでリベースです。

```bash
$ git checkout dev_#50
$ git rebase dev
```

このとき競合がでてなんかごっちゃごちゃ言われますが、落ち着いて修正を行い、

```bash
$ git add Calc.java
$ git rebase --continue
```

ってやってリベースのcontinueを選びます。これでリベース完了です。リベースの考えかたに従い、まずdev_#50のコミットがいったん待避され、devの修正が取り込まれて、さらにdev_#50の新規コミットが行われます。当然、もとのdev_#50のコミットとは別のコミットですね。

ちなみに、rebase の競合をキレイにするのっていろいろめんどくさく、その手順は

[git rebaseでのブランチ融合でコンフリクト解消](http://qiita.com/shizuma/items/82bb439e8899c1bd3114)

に丁寧にまとめてありました。感謝します!


さて、さらにdev側にマージします

```bash
$ git checkout dev
$ git merge --no-ff dev_#50
```

今回はマージでなくてリベースを行ったので、devに取り込んだもの(すなわちdev_#60の修正)のつぎに、dev_#50の修正がマージされました。dev_#50が「dev_#60を取り込む前のdev」からブランチした事実は'なかったこと'になってます。

![rebase.png](https://qiita-image-store.s3.amazonaws.com/0/73777/ff6013e8-db35-43dc-26ad-ffeb7a405e62.png "rebase.png")



## まとめ
マージは、文字通りのマージで、devの修正をdev_#50の「直近状態」にマージしました。最終的には、その事実も含めてdevにマージされていきました。

リベースは文字通り、re-baseすることで、dev_#60を取り込んだdevをベースに、dev_#50の更新分がコミットされ、そしてdevにマージされていきました。

今回みたいなdevを中心にチケット毎のブランチで開発するようなケースだと、devでの更新分を取り込むようなケースでは、リベースをするのが正しそうですね。。。




## 関連リンク
* [使い分けできていますか？マージ（merge）&リベース（rebase）再入門 - The Powerful Code](http://powerful-code.com/blog/2012/11/merge-or-rebase/)
* [基本的なGitコマンドまとめ](http://qiita.com/2m1tsu3/items/6d49374230afab251337)
* [Gitのリベースでベースが変更される際に、コミット番号が変わる件を確認する](https://qiita.com/masatomix/items/d2dfcd0d0bdb06bcc346)