はじめての git rebase

## 概要

- 複数人で開発しているときに起きそうなケースの単純版をやってみる
- master ブランチから2つのブランチを作成してそれぞれのブランチでの修正がコンフリクトするようにする
- リモートリポジトリには GitHub を使用

## 今回の流れ

- リモートのリポジトリをローカルに clone
- master ブランチから foo ブランチを作成
  - ファイルを修正
  - リモートに push
- master ブランチから bar ブランチを作成
  - ファイルを修正
  - リモートに push
- foo ブランチと bar ブランチの Pull Request を作成
- bar ブランチを master ブランチに merge (Pull Request から)
- foo ブランチを master ブランチに rebase (ローカルで)
- foo ブランチをリモートに push
- foo ブランチを master ブランチに merge (Pull Request から)

## 手順

### リポジトリをローカルにクローン

```
$ git clone git@github.com:niwasawa/hello-world.git

$ cd hello-world/
```

今回修正するファイルは README.md

```
$ cat README.md
Hello, world is mine.
```

### master ブランチから foo ブランチを作成

foo ブランチを作成する。

```
$ git checkout -b foo
Switched to a new branch 'foo'
```

ファイルに「foo, foo, foo, foo!」を足す。

```
$ vim README.md

$ cat README.md
Hello, world is mine.
foo, foo, foo, foo!
```

コミットしてリモートに push する。

```
$ git add README.md

$ git commit -m "Modify by foo"
[foo 2e96823] Modify by foo
 1 file changed, 1 insertion(+)

$ git push origin foo
```

### master ブランチから bar ブランチを作成

bar ブランチを作成する。

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

$ git checkout -b bar
Switched to a new branch 'bar'
```

ファイルに「bar, bar, bar, bar!」を足す。

```
$ vim README.md

$ cat README.md
Hello, world is mine.
bar, bar, bar, bar!
```

コミットしてリモートに push する。

```
$ git add README.md

$ git commit -m "Modify by bar"
[bar 765993f] Modify by bar
 1 file changed, 1 insertion(+)

$ git push origin bar
```

### master ブランチから foo ブランチと bar ブランチが作成されている状態

<img width="772" alt="ng1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/961f0283-f7f1-fe4b-54b8-a33ca2c3455e.png">

### foo ブランチと bar ブランチの Pull Request を作成

GitHub 上にてそれぞれのブランチから Pull Request を作成する。

### bar ブランチを master ブランチに merge (Pull Request から)

bar ブランチの Pull Request にて merge を実行する。

<img width="787" alt="bar_pr_merge.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/42639e78-78ca-5167-6a72-e0c99634f080.png">

bar ブランチが master ブランチに合流する。

<img width="771" alt="ng2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/e075dd41-6db2-ebf7-c6ae-f959bf17e407.png">

### foo ブランチでコンフリクト

foo ブランチと bar ブランチとで修正した場所が同じなので、マージの際にコンフリクトが起きる状態になっている。

<img width="792" alt="foo_conflicts.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/5d2863cb-bbcc-06e3-a3b1-44064afd53f3.png">

### リモートの master ブランチをローカルに反映

```
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

$ git pull origin master
```

### foo ブランチを master ブランチに rebase (ローカルで)

foo ブランチに切り替える。

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

master ブランチに rebase すると、メッセージがたくさん表示される。対処方法はメッセージにかかれている。

```
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Modify by foo
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
error: Failed to merge in the changes.
Patch failed at 0001 Modify by foo
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
```

コンフリクトが起きたファイルを確認する。

```
$ cat README.md
Hello, world is mine.
<<<<<<< HEAD
bar, bar, bar, bar!
=======
foo, foo, foo, foo!
>>>>>>> Modify by foo
```

ファイルを修正してコンフリクトを解消する。

```
$ vim README.md

$ cat README.md 
Hello, world is mine.
foo, foo, foo, foo!
bar, bar, bar, bar!
```

コンフリクトが解消されたファイルを git add する。

```
$ git add README.md
```

git rebase を続行して完了させる。

```
$ git rebase --continue
Applying: Modify by foo
```

### foo ブランチをリモートに push

rebase した foo ブランチをそのまま push しようとすると、コミットの歴史が変わっているので拒否される。

```
$ git push origin foo
To github.com:niwasawa/hello-world.git
 ! [rejected]        foo -> foo (non-fast-forward)
error: failed to push some refs to 'git@github.com:niwasawa/hello-world.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```

-f オプション (または --force や --force-with-lease オプション) でリモートリポジトリを強制的に上書きする。

```
$ git push --force-with-lease origin foo
```

### master ブランチの最新から foo ブランチが作成されたような状態に変わる

<img width="772" alt="ng3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/0c927f72-0235-33d6-0a12-ad1f490b73b8.png">

### foo ブランチを master ブランチに merge (Pull Request から)

コンフリクトが解消されたので、 foo ブランチの Pull Request にて merge が実行できる。

## 参考資料
- [Git \- リベース](https://git-scm.com/book/ja/v1/Git-%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E6%A9%9F%E8%83%BD-%E3%83%AA%E3%83%99%E3%83%BC%E3%82%B9)