コードレビュー前に git rebase -i してコミット履歴をきれいにする

# はじめに

レビュワーにとってコミット履歴は追いやすさはとても大事だと思う一方で、最初からきれいな履歴を保つのはけっこう難しいです。修正が漏れたり、あとから必要な箇所が分かったりして、コミット履歴が予想より膨らむことも多々あるのが実際の開発です。

https://qiita.com/_mi/items/f477e95a864474187e3d

# あとから履歴を書き換える

そんなときはこのコマンド（`git rebase -i`）です。

平たく言うと、コミット履歴の編集、統合、順番の変更などができるコマンドです。

https://tracpath.com/docs/git-rebase/

# 使い方

例えば以下画像のような、「前のコミットで修正漏れてたのでしゃーなしワンモアコミットしちゃった」みたいなケースを考えます。

![rebaseの動機.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3388832/e6ba3494-4d63-860a-2b85-09d9711e28e6.png)


## step１. rebaseを開始したいコミットハッシュを特定する

今回は、`hash ddddd` を `hash ccccc` に統合したいので、直前のコミットである `hash bbbbb` のコミットハッシュを取得します。

```terminal: git log で commit hash を探す など
$ git log

commit fffff (HEAD -> <branch名>, origin/<branch名>)
Author: _mi
Date:   Sun Sep 22 18:38:29 2024 +0900

    fix: 可読性の向上のため、テストクラスのリファクタリングをする

commit eeeee
Author: _mi
Date:   Sun Sep 22 18:38:09 2024 +0900

    fix: テストをpassするようにhogeメソッドを修正

commit ddddd
Author: _mi
Date:   Sun Sep 22 18:37:49 2024 +0900

    fix: テストメソッド名をtypoしてたので修正

commit ccccc
Author: _mi
Date:   Sun Sep 22 18:37:25 2024 +0900

    test: hogeメソッドの振る舞いを網羅するため、テストを１つ追加

commit bbbbb
Author: _mi
Date:   Sun Sep 22 18:37:02 2024 +0900

    add: テストがpassするよう、hogeメソッドを実装

commit aaaaa
Author: _mi
Date:   Sun Sep 22 18:36:19 2024 +0900

    test: hogeメソッド（未実装）の失敗するテストクラスを作成
:
```

## Step２. rebase を開始する

rebase コマンドを使って、どのコミットから変更を開始するか指定します。

```bash:
$ git rebase -i <base commit hash>
```

:::note warn
**注意点**
`git rebase` の実行前にローカルの変更差分を消しておきましょう。
```terminal: 未コミットの変更が残っているときに git rebase を実行しようとするとエラーになる
error: cannot rebase: You have unstaged changes.
error: Please commit or stash them.
```
:::


## Step３. コミット履歴を編集する

Git のデフォルトの設定では Vim がノーマルモードで開きます。（`git rebase` で `hash bbbbb` を指定したので、`hash ccccc` から編集できる）

```vim: git rebase -i で開く Vim
pick ccccccc test: hogeメソッドの振る舞いを網羅するため、テストを１つ追加
pick ddddddd fix: テストメソッド名をtypoしてたので修正
pick eeeeeee fix: テストをpassするようにhogeメソッドを修正
pick fffffff fix: 可読性の向上のため、テストクラスのリファクタリングをする

# Rebase bbbbbbb..fffffff onto bbbbbbb (4 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 [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
:
```

今回やりたいのは、typoしたコミット（`hash ddddd`）をその直前のコミット（`hash ccccc`）に統合することです。なので、Vim をインサートモードに切り替え（`i` キーなど押下）た後に、`pick` の部分を以下のように修正します。

```vim: 
pick ccccccc test: hogeメソッドの振る舞いを網羅するため、テストを１つ追加
squash ddddddd fix: テストメソッド名をtypoしてたので修正
pick eeeeeee fix: テストをpassするようにhogeメソッドを修正
pick fffffff fix: 可読性の向上のため、テストクラスのリファクタリングをする
```

その後、ノーマルモード（`Esc` キー押下）に切り替え、`:wq` を押下し保存・終了します。

:::note info
git rebase -i のコマンド
Vim を開いたときに出てくる `Commands:` 以下の記載のようにほかにもできる操作がいろいろあるので、興味あれば調べてみてください。
私は基本 `squash`(`s`) しか使いません！
:::

## Step４. コミットメッセージを編集する

`:wq` 押下後、コミット統合後のコミットメッセージを編集できるようになります。

```vim: コミットメッセージの編集
# This is a combination of 2 commits.
# This is the 1st commit message:

test: hogeメソッドの振る舞いを網羅するため、テストを１つ追加

# This is the commit message #2:

fix: テストメソッド名をtypoしてたので修正

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
~
```

typo修正時のコミットメッセージは不要になるので、「fix: テストメソッド名をtypoしてたので修正」を `dd` を押下し削除した後、`:wq` を押下し保存・終了します。

```vim: コミットメッセージの編集
# This is a combination of 2 commits.
# This is the 1st commit message:

test: hogeメソッドの振る舞いを網羅するため、テストを１つ追加

# This is the commit message #2:

（ddでここを消す）

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
~
```

## Step５. force push する

リベースが完了するとローカルのブランチがリモートリポジトリのブランチと異なる歴史を持つため、変更をリモートに反映するためにforce pushする必要があります。

```bash:
$ git push origin <branch name> --force
```

あるいは

```bash:
$ git push origin <branch name> -f
```

## 結果

コミットが統合されました。（`fix: テストメソッド名をtypoしてたので修正` のコミットが `test: hogeメソッドの振る舞いを網羅するため、テストを１つ追加` のコミットに混ぜ込まれている）

![rebaseした結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3388832/4a51fdc0-4e2a-80e0-8d85-9b67cef3a9a9.png)



# 注意点

`rebase -i` はめちゃくちゃ便利なのですが、 `force push` は要注意です。

- リポジトリのブランチ保護を設定する（`Block force pushes`）
- 自身にしか影響のないブランチでのみ利用する

など、気を付けて使ってください。

# 参考

https://tracpath.com/docs/git-rebase/

https://qiita.com/shira-shun/items/29c7f36179117022cb6d

https://zenn.dev/mikaneko/articles/0fe1daf2e8a987