git rebaseとは・git rebaseの使い所・リセット方法

## git rebaseとは・git rebaseの使い所

### Mergeとの共通点

二つのブランチの内容を一つのブランチにまとめるという点でマージと同様。

また、両者とも実行時にコンフリクトが生じる可能性がある。

### Mergeとの違い

- マージは二つのブランチを川の流れのように合流させる。特にひねりがなく直感的には最もわかりやすいと思う。
- リベースはgitの歴史を書き換えて、二つのブランチが枝分かれし始めた地点（ベース）を最新の地点にまで移動させる事で、結果枝分かれした事実そのものをなかった事にするようなもの。 

### ケーススタディ

 - 支流の内容を本流に取り込みたい時（feature-XXXXX -> develop　等）
    - 例えば、feature-XXXXXでの開発が一段落し、レビューも完了したので本流となるdevelopブランチに取り込みたい時などに該当する。 
    

-> この場合は本流(develop)に支流(feature-XXXXX）をマージして取り込む。
   ※ 本流（develop）を支流（feature-XXXXX）にリベースはしてはいけない。なぜならdevelopは複数の人が参照しているのに、履歴が書き変わってしまうからである。

- 本流の内容を支流に取り込みたい時(develop -> feature-XXXXX 等)
   - 例えば、developにある重要な変更を今の作業ブランチ（feature-XXXXX）でも使いたいのでdevelopをfeature-XXXXXに取り込みたい時など。

-> 支流（feature-XXXXX）を本流（develop)上にリベースする。
-> developをfeature-XXXXにマージしてもいいが、そうするとコミット履歴で、コミットが時間順に並ぶのでfeature-XXXXXとdevelopの区別がなく分かりにくくなる。rebaseだと手間がかかりすぎる時などにするくらいか。

## rebaseの手順

feature-XXXXブランチをdevelopブランチ上にリベースしたいとする。

0: 当たり前だが、リモートにdevelopブランチがある場合は、その最新の変更をローカルに持ってきておく事(origin はリモートのgitリポジトリの名前)。
```
git pull origin develop
```

1: feature-XXXXブランチを複製しておく。リベースは履歴を書き換えるため、失敗した時に元に戻すのが手間になってしまうため。なお、直す手順としては、`git reflog`を使うこともできるので絶対ではない。git reflogについては記事最下部参照

```
git checkout -b feature-XXXX-copy
```

2: feature-XXXXブランチにチェックアウトする。その後いきなりリベースしてもいいのだが、コミットを一つ一つdevelopブランチ上に重ねていく事になるため、何度もマージコンフリクトを解消しなくてはならず面倒くさいことが多い。そのため、まず`rebase -i`でfeature-XXXXブランチのコミットを一つにまとめる。

```
git checkout feature-XXXX
git rebase -i 5634f2e
```

`5634f2e`とは、feature-XXXXとdevelopブランチの共通点（分岐点）となっているコミットのハッシュ値。


3: するとviが起動し、以下のように表示される。

なおコミットの流れは`5634f2e`(developブランチとの分岐点) -> `3bf6995` -> `15717d1` -> `6a36aed`の順となっている。

```: 
pick 3bf6995 Entity, Repositoryを改修した。
pick 15717d1 正しいアイコン画像を設定した。
pick 6a36aed 不要な記述を消した。

# Rebase 5634f2e..6a36aed onto 5634f2e (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's


```

画面に記載してある通りだが、ここではまず`3bf6995` -> `15717d1` -> `6a36aed`の3つを一つにまとめて、developブランチとの分岐点である`5634f2e`からfeature-XXXXブランチにコミットが一つだけ残るようにしていく。

なお、やっぱり辞めたい場合は`:q!`でviを終了すれば、特に変更なくrebaseが終了する。

4: コミットをまとめる

gitではなくviの操作法になるが、`a`キー、`i`キーなどを押して編集モードに入る。
普通のテキストエディタのような感じで、文字列を下記のように変更する。

```:
pick 3bf6995 Entity, Repositoryを改修した。
squash 15717d1 正しいアイコン画像を設定した。
squash 6a36aed 不要な記述を消した。

# Rebase 5634f2e..6a36aed onto 5634f2e (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
```

2行目・３行目の行頭を「squash」に変えたのがお分かりいただけるだろうか。これは、一つ目のコミット「3bf6995」はそのまま残し、「15717d1」「6a36aed」に含まれる変更を「3bf6995」に合体させることを意味する。

編集終了したら`:wq!`で保存してviを終了する。

すると、今度はコミットメッセージ編集画面へ行く。

```:
# This is a combination of 3 commits.
# This is the 1st commit message:

Entity, Repositoryを改修した。

# This is the commit message #2:

正しいアイコン画像を設定した。

# This is the commit message #3:

不要な記述を消した。

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Thu Jul 16 13:10:41 2020 +0900
#
# interactive rebase in progress; onto 5634f2e

```

一まとめにしたことがわかるようにメッセージを入力し、再び`:wq!`で閉じる。これでコミットのまとめが完了する。

5 developブランチ上にrebaseしていく

一まとめになったコミットをdevelopにリベースする。

```:
git rebase develop
```

コンフリクトが生じた場合は、それを解消する。これでリベース終了となる。

## rebase が完了したが、やっぱり元に戻す（追記 2020/10/16）

feature-XXXXのコピーのブランチを作っていた場合は、そのブランチを正とする形で元にもどすことができる。

コピーのブランチを作っていなかった場合でも、`git reflog`が使える。`git reflog`とは、HEADがどこを指していたかの記録をログとして残したもので、当然rebaseでHEADがどこに移動したかも記録に残っている。そのため、rebaseもリセットすることができる。

```
git checkout feature-XXXX
git reflog feature-XXXX
```

上のコマンドを打ち込むと、reflogが表示される。

```
<commid id> (HEAD -> feature-XXXX) feature-XXXX@{0}: rebase finished: refs/heads/feature-XXXX onto <commit id>
<commid id> feature-XXXX@{1}: <commit message>
```

ここでは、`feature-XXXX@{0}`がリベース完了時のログ、`feature-XXXX@{1}`がリベース前のログである。そのため、この場合は次のコマンドでリベースを元に戻すことができる。

```
git reset --hard feature-XXXX@{1}
```

## マージコミットも含めてrebaseしたい

```
git rebase --rebase-merge upstream
```

参考 [git rebase --rebase-margeについて](https://qiita.com/search?q=rebase)

## 参考
[git rebaseを初めて使った際のまとめ](https://qiita.com/310ma3/items/e0ec74b47c6c219f2a8b)
[Git rebaseの使い方](https://qiita.com/maru53/items/447d5a04740acad1c7a8)
[Linuxサーバー管理入門 - gitのrebaseは作業中のブランチにmasterブランチを取り込むためのよい手段 / Git](https://linux.perlzemi.com/blog/20190224171737.html)
[初心者でもわかる！リベースの使い方を解説します](http://liginc.co.jp/web/tool/79390)
[誤ってリベースしてしまった Git のコミットを元に戻す方法](https://yu8mada.com/2018/05/25/how-to-restore-the-original-history-after-rebasing-git-s-commits-accidentally/)
[git rebase --rebase-margeについて](https://qiita.com/search?q=rebase)