rebaseをちゃんと理解して使えるようになろう！

## 簡単なrebaseの説明

簡単なレベルであれば、`サル先生のGit入門`で十分なのでこちらを参照ください。
[サル先生のGit入門: rebase](https://backlog.com/ja/git-tutorial/stepup/13/)

## mergeとの違い

rebase、mergeはどちらもブランチを統合するコマンドになります。
実際の作業ベースではどんな違いがあるのでしょうか？

## コミットが改変される？

### merge
- 単純な統合なので、基本はマージコミット１つ追加される

```
aブランチ
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
に
bブランチ
        * f２３vd４３ Eコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
をマージすると

⬇️

aブランチ(fast-forward場合)
        * f２３vd４３ Eコミット
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
または
aブランチ(non-fast-forward場合)
        * fas34vs Fコミット(マージコミット)
        |\ 
        | * f２３vd４３ Eコミット
        |/ 
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
```

### rebase
- rebaseはコミットが改変されることがある。

```
bブランチ
        * f２３vd４３ Eコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
を
aブランチ
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
でリベースすると

⬇️

bブランチ
        * ３４jfil3 Eコミット
        * e750862 Dコミット
        * 2f6c625 Cコミット
        * cf890e0 Bコミット
        * 8721950 Aコミット
```

rebaseの場合はEコミットのハッシュ値が書き換わってしまいました。
コミットメッセージが一緒なだけで、別物のコミットと認識されてしまいます。


## rebaseしたら何が変わる？
先ほどはハッシュ値が変わることを説明しました。
変わるのは他にもあります。

`CommitDate `です。
コミットには`AuthorDate`と`CommitDate`が管理されています。

`git log`で出力される方は`AuthorDate`になります。


## rebaseのリスク
### もとに戻せないかも

ブランチ統合しようとしたら、なんかよくわからない状況になってしまった。(初心者あるある)
mergeはコミットが追加されるだけなので、もし失敗した場合は`reset`を使って元に戻すことができます。
しかし、rebaseはコミットが改変されてしまうので、バックアップを取っておかないと、取り返しのつかないことになってしまうかも知れません...

これが、理由でrebaseを使わないGit初心者がちらほら...

### rebaseした後は強制プッシュ必須

一度プッシュしたブランチでrebaseを行うとコミットが改変されるため、再度プッシュできなくなります。
rebase前のコミットとrebase後のコミットは異なるものと判断されるため`non-fast-forward`な状態になってしまいます。

`git push -f`は危険なので`git push --force-with-lease`を使って再度プッシュする癖をつけましょう。

### 他の開発者のコミットも改変させてしまうかも

git flowでいう`develop`や`master`ブランチ(複数人のコミットが入ったブランチ)で同様なことを行うと、他のメンバーのコミットをいつの間にか変えてしまうかも知れません。

## なぜrebaseするの?

複数人で開発する際はこのような事象が発生すると思います。
例： rebaseしない場合
1. Aさんは13時に実装を終えてコミット、プルリクエストを出した。
2. Bさんは14時に実装を終えてコミット、プルリクエストを出した。
3. Bさんは指摘箇所なしで、当日の夕方にマージされた。
4. Aさんは指摘された箇所が多すぎて、マージできたのは翌日の朝でした。 

rebaseをしないと以下のようなログの順序になります。

```
* Aさんのマージコミット 9:00
* Aさんの指摘取り込み用コミット（その3） 21:00
* Bさんのマージコミット 18:00
* Aさんの指摘取り込み用コミット（その2） 17:00
* Aさんの指摘取り込み用コミット（その１） 16:00
* Bさんの実装コミット 14:00
* Aさんの実装コミット 13:00
```

もうすでに複雑ですよね...
あとで見返した時にどこを確認すれば良いかわからなくなってしまいます。
また、指摘取り込み用コミットがいくつも入っています。
このコミットのせいでAさんのどんな実装をしたか一目ではわかりません。

例： rebaseを使った場合
1. Aさんは13時に実装を終えてコミット、プルリクエストを出した。
2. Bさんは14時に実装を終えてコミット、プルリクエストを出した。
3. Bさんは指摘箇所なしで、当日の夕方にマージされた。
4. Aさんは指摘された箇所が多すぎて、LGTMされたのは翌日の朝でした。
5. Aさんはrebaseしてコミットを１つにしてから、マージしてもらった。

rebaseを使うと以下のようなログの順序になります。

```
* Aさんのマージコミット 9:00
* Aさんの実装コミット 13:00
* Bさんのマージコミット 18:00
* Bさんの実装コミット 14:00
```

綺麗になりました。
これなら、Aさんがどんな実装をしたかは`Aさんの実装コミット 14:00`を確認すれば良いと一目でわかります。

rebaseにはこのようなメリットがあります。



## 安全にrebaseを使うには

### プロテクトブランチを活用しよう
GitHubやGitLabにはプロテクトブランチという機能があります。
`develop`や`master`ブランチではプッシュを許容しないように設定すれば、rebaseして壊れたブランチを間違ってプッシュしたとしてもリジェクトされるので保護されます。

### 自分のコミットだけが枝分かれしているときだけrebaseしよう

基本、featureブランチを作成し開発してコミットしますよね？
そうであれば、枝分かれしたコミットは自分のものだけなハズです。

こういう時だけrebaseを使うように心がけましょう。
それでも不安な場合はバックアップ用のブランチを作成してチャレンジしましょう。

### sandboxなリポジトリをつかってrebaseを試して見よう

重要なリポジトリで初めてrebaseするのはやはりリスクがあります。
汚してもいいリポジトリを作成してrebaseを試してみましょう。

### Git職人に付いてもらってrebaseしてみよう

隣にGit職人がいれば安心です。


### まとめ

今回rebaseのメリットについてあまり触れませんでしたが、もちろんログが綺麗になるという大きなメリットがあります。

個人的には

- リモートリポジトリではmerge(プルリク)
- ローカルリポジトリではrebase(最新情報を取得)

なイメージを持っています。

ログを綺麗に保つことは後々ログを見返すと気に役立ちますので、
今後意識してGit使ってもらえれば....と思います。