PullRequestベースの開発ではGit Rebase使った方がいい


岩手県立大学ソフトウェア情報学部 Advent Calendar 2015の4日目です。
@otukutunではなく、私が書いてみました。

1年早いですね。
今年はQiita全然かけませんでしたが、Advent Calendarで挽回していきたいと思います。


## みなさん、Git Rebase使ってますか？

私は正直なんか怖いと思っていました。
ログが書き換わるとか、
Pushするときに強制的にしないといけないとか、
なんでそんなことしなきゃいけないの？

と

ずっと謎だったのですが、
最近、一緒に開発している方から教えてもらったので、
共有したいなと思います。

## そもそも なんで Git Rebaseするのか？

はじめに、Rebaseの意味とは、

Re Baseなので、

### **基点を変える**

ということです！

なんでそんなことをするかと言いますと、

### **最新のコミットをPullRequestで送るためです！**

このあと図と実際のコマンドラインで説明していきます。

## よくある Git Rebaseの利用例


例えば、新機能を追加するために、masterからfeatureブランチを作成し、開発を進めたとします。

![rebase1.png](https://qiita-image-store.s3.amazonaws.com/0/19316/738ceb8a-e903-7ce1-d476-770e72bfc008.png "rebase1.png")


しかし、不運にも別の方が後からhotfixでバグを修正してきました。バグなので、先にmasterに取り込まれます。※コミットDはコミットFよりも先にコミットしていました。

![rebase2.png](https://qiita-image-store.s3.amazonaws.com/0/19316/b084331c-318e-0678-59fa-f32b76da8a7a.png "rebase2.png")

ここでmasterがかわります。そのためこのままfeatureがマージされた場合、こんな感じになります。

![空白ページ.png](https://qiita-image-store.s3.amazonaws.com/0/19316/04cabe1a-4250-49a0-701d-8bd39acffbb0.png "空白ページ.png")

### コミットの時系列になってるから別にいいじゃん！という人もいるかと思います。
しかし、ここでFのコミットが一番大変です。DとEのコミットが取り込まれた影響でちゃんと動くのかわからなくなる可能性があります。


### そのため、常に最新のコミットとして取り込むようにしたくなるわけです！


なので、ここでRebaseすると

![空白ページ.png](https://qiita-image-store.s3.amazonaws.com/0/19316/8cfb488f-0f08-cc4e-8ae6-855b81dd51ff.png "空白ページ.png")

### 基点をCのコミットからFのコミットに変えてくれるのです！！

もう一度言います。

### **基点を変える ので、Re Baseです**

あとは、
このままプルリクを送ってあげれば、

![空白ページ.png](https://qiita-image-store.s3.amazonaws.com/0/19316/c991b1ca-d72c-809d-d7fd-bce1f72c596f.png "空白ページ.png")


こんな感じのコミットログになります。
Rebaseを使うことで常に最新のコミットとして取り込むことができます。


## 実際にコード上で試してみるとこんな感じです。
pull request なし版でやってます。

```
$ mkdir rebase_sample
$ cd rebase_sample/
$ git init
$ vim README.md
```

``` README.md
Git rebase sample

1. commit a
```

```
$ git add README.md 
$ git commit -m "a"
$ echo 2. commit b >> README.md 
$ git add README.md 
$ git commit -m "b"
$ echo 3. commit c >> README.md 
$ git add README.md 
$ git commit -m "c"
```

これでコミットA~Cができました。
ここでfeatureブランチを作ってDとEをコミットしていきます。

```
$ git checkout -b feature
$ echo 4. commit d >> README.md
$ git add README.md 
$ git commit -m "d"
$ echo 5. commit e >> README.md
$ git add README.md 
$ git commit -m "e"
```

はい。これでfeatureブランチもできました。
ここで一旦masterにもどって、hotfixブランチを作り先にmasterにマージします。

```
$ git checkout master
$ git checkout -b hotfix
$ echo 6. commit f >> README.md
$ git add README.md 
$ git commit -m "f"
$ git checkout master
$ git merge hotfix
```

これでコミットFが先にmasterにマージされました。
ログを確認してみると、
a -> b -> c -> f
の順番で並んでいます。

```
$ git log
commit 7a69cdc5355ba3ec45d597f2212d0829af8cf755
Author: ganezasan 
Date:   Fri Dec 4 02:42:08 2015 +0900

    f

commit 138e73614cdad9e7b01c0162097b056c916a1770
Author: ganezasan 
Date:   Fri Dec 4 02:10:12 2015 +0900

    c

commit 9ca66e5546d43ddd2662ba3efb4722a72f326333
Author: ganezasan 
Date:   Fri Dec 4 02:09:59 2015 +0900

    b

commit 6809d711638d93788e44efc57c285a0a771f6481
Author: ganezasan 
Date:   Fri Dec 4 02:07:02 2015 +0900

    a
```

このままfeatureをマージすると、

```
$ git merge feature
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
```

まあそうですね。
同じファイルをいじっていたので、コンフリクトが起きます。
とりあえず、このままコンフリクトを気にせずマージしてログをみます。

```
$ git add README.md 
$ git commit 
[master 06fa531] Merge branch 'feature'
$ git log
commit 06fa531cb4f9103badba18946b6b5d5644bf9703
Merge: 7a69cdc abe1ac6
Author: ganezasan 
Date:   Fri Dec 4 02:42:58 2015 +0900

    Merge branch 'feature'

commit 7a69cdc5355ba3ec45d597f2212d0829af8cf755
Author: ganezasan 
Date:   Fri Dec 4 02:42:08 2015 +0900

    f

commit abe1ac66dc1a585be3a806f78f8b3f5e2a21ec82
Author: ganezasan 
Date:   Fri Dec 4 02:41:30 2015 +0900

    e

commit 0f460c79bebd6a4ca79841bab4d19cc838401648
Author: ganezasan 
Date:   Fri Dec 4 02:41:17 2015 +0900

    d

commit 138e73614cdad9e7b01c0162097b056c916a1770
Author: ganezasan 
Date:   Fri Dec 4 02:10:12 2015 +0900

    c

commit 9ca66e5546d43ddd2662ba3efb4722a72f326333
Author: ganezasan 
Date:   Fri Dec 4 02:09:59 2015 +0900

```

はい！先ほど図で説明した通り、
a -> b -> c -> d -> e -> f
のように基点からの時系列で並びます。

一旦fをマージしたところまで戻し、
今度はRebaseをしてみます。

```
$ git reset --hard HEAD^
$ git log
commit 7a69cdc5355ba3ec45d597f2212d0829af8cf755
Author: ganezasan 
Date:   Fri Dec 4 02:42:08 2015 +0900

    f

commit 138e73614cdad9e7b01c0162097b056c916a1770
Author: ganezasan 
Date:   Fri Dec 4 02:10:12 2015 +0900

    c

commit 9ca66e5546d43ddd2662ba3efb4722a72f326333
Author: ganezasan 
Date:   Fri Dec 4 02:09:59 2015 +0900

    b

commit 6809d711638d93788e44efc57c285a0a771f6481
Author: ganezasan 
Date:   Fri Dec 4 02:07:02 2015 +0900

    a
```

コミットFをマージした状態にもどりgましたね。
では、Featureブランチに移動し、Rebaseを行います。

```
$ git checkout feature
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: d
Using index info to reconstruct a base tree...
M	README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Failed to merge in the changes.
Patch failed at 0001 d
The copy of the patch that failed is found in:
   /usr/share/repos/rebase_sample/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
```

まあ、コンフリクトがおこるのでいろいろメッセージがでますが、
気にせずマージを行い、ログを確認します。

```
$ git add README.md 
$ git rebase --continue
Applying: e
$ git log
commit 5c342cd643dff7abc420b90a602aba6d9b9b6a46
Author: ganezasan 
Date:   Fri Dec 4 02:41:30 2015 +0900

    e

commit 8a7c05baa141433b947390f71fe9cea895a43889
Author: ganezasan 
Date:   Fri Dec 4 02:41:17 2015 +0900

    d

commit 7a69cdc5355ba3ec45d597f2212d0829af8cf755
Author: ganezasan 
Date:   Fri Dec 4 02:42:08 2015 +0900

    f

commit 138e73614cdad9e7b01c0162097b056c916a1770
Author: ganezasan 
Date:   Fri Dec 4 02:10:12 2015 +0900

    c

```

はい！これで、dとeのコミットが先頭にきました！
基点が c から f に変わりました。
綺麗ですね〜！マージ担当者もこれで助かるかと思います。

a -> b -> c -> f -> d -> e

あとはmasterにマージします。

```
$ git checkout master
$ git merge feature
$ git log
commit 5c342cd643dff7abc420b90a602aba6d9b9b6a46
Author: ganezasan 
Date:   Fri Dec 4 02:41:30 2015 +0900

    e

commit 8a7c05baa141433b947390f71fe9cea895a43889
Author: ganezasan 
Date:   Fri Dec 4 02:41:17 2015 +0900

    d

commit 7a69cdc5355ba3ec45d597f2212d0829af8cf755
Author: ganezasan 
Date:   Fri Dec 4 02:42:08 2015 +0900

    f

commit 138e73614cdad9e7b01c0162097b056c916a1770
Author: ganezasan 
Date:   Fri Dec 4 02:10:12 2015 +0900

    c

commit 9ca66e5546d43ddd2662ba3efb4722a72f326333
Author: ganezasan 
Date:   Fri Dec 4 02:09:59 2015 +0900

    b
```

はい！これで完了です。
お疲れ様でした。

## まとめ

本来であればpullしながらrebaseを行うため、
git rebase ではなく、git pull --rebaseを使うかと思います。
今回はRebaseってそもそもなんだよ。ところから説明しました。

最新のコミットをプルリクエストで送って、
快適なRebaseライフをおくってください。