数回前の要らないコミットを消す方法

# はじめに
作業中、過去のコミットで行った変更を元に戻す場面があった。
そのまま作業を続けてしまったが、コミット履歴がわかりにくくなっていたので、不必要になったコミットだけ削除した。
# git rebase とは
今回は`git rebase`コマンドを使ってコミット履歴を修正した。

**base** は名詞では**土台**、動詞では**基礎付ける**という意味で、これに再起的なニュアンスを表す接頭語 **re** が付いている。
イメージとしては、ある枝を切り離してから別の場所に接木し直す、といった感じ。


```
        C' - D' - E'  feature
       /                    　　      →        - A - B - C - C' - D' - E'  master
- A - B - C  master             rebase を実行  
```

上の例では`-C'-D'-E'`のブランチを master の先端にくっ付けている。

# 今回の状態
私が rebase 機能を使ったとき、作業していた feature ブランチは以下のような状態。

```
- A - B - C - D - E - F  feature
              ↑
     このコミットだけ消したい
```

E と F のコミットは残したまま、不必要な D のコミットだけ削除したかった。

そこで、`-E-F`の部分を `-A-B-C` の先端に移すことにした。

# 実行した手順
まずは`git log`で、上図の **C** のコミットID を取得。


```
git log
commit 69ec236a8acf4659bd0962a47ca843fb9623bchf (HEAD -> feature)
Author: ユーザ名 <メールアドレス>
Date:   Fri Jul 2 10:13:21 2021 +0900

    F のコミット（残したい）

commit ag28517f035f1b143d356335271de88c5c7136ef
Author: ユーザ名 <メールアドレス>
Date:   Fri Jun 25 17:04:23 2021 +0900

    E のコミット（残したい）

commit 52b81060a47202777d94aebd84cad9ae3eda20b8
Author: ユーザ名 <メールアドレス>
Date:   Fri Jun 25 16:53:33 2021 +0900

    D のコミット（不必要）

commit 59e85460e245827455d94oafe84cad9ae3xvm33l ←この文字列をコピー
Author: ユーザ名 <メールアドレス>
Date:   Fri Jun 25 16:53:33 2021 +0900

    C のコミット（ここに付け足したい）
```

ブランチ付け替え先のコミットID が分かったら、以下のコマンドを実行。

```
git rebase -i 取得したコミットID
```

`-i`オプションを付けて実行すると、個々のコミットに対して編集や削除などを行うことができる。
このオプションがない場合、移動元のコミット全てが自動的に移植される。

今回は **D** のコミットを消した上で rebase したいので、`-i`オプションを使用。
すると下のように出力される。

```
pick 52b8106 D のコミット（不必要）
pick ag28517 E のコミット（残したい）
pick 69ec236 F のコミット（残したい）

# Rebase 59e8546..69ec236 onto 59e8546 (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
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
```

**D** のコミットのみ削除したいので、最初の 3行を次のように書き換えた。

```
drop 52b8106 D のコミット（不必要）
pick ag28517 E のコミット（残したい）
pick 69ec236 F のコミット（残したい）
```

コンフリクトが無ければこれで完了。

今回はVSCode を開くとコンフリクトが発生していたので解消した。
コンフリクトを全て解消したら、`git add`コマンドで変更内容をステージングエリアへ。

これで **D** のコミットの削除は完了したので、`git rebase --continue`コマンドで次の **E** のコミットへ移動。
以下のように、残る 2つのコミットのみが表示される。

```
pick ag28517 E のコミット（残したい）
pick 69ec236 F のコミット（残したい）
```

**E** と **F** のコミットは残したかったため、`pick`のまま続行した。
もし **E** や **F** も修正したい場合は、**D** のときと同様`pick`を書き換えれば良い。

コミットを削除ではなく編集する際は、`git add`ではなく`git commit --amend`を使うことに注意。

`git rebase --continue`を続け、全てのコミットの処理が正常に終わると以下のように出力された。

```
Successfully rebased and updated refs/heads/feature.
```

`git log`で履歴を確認すると、期待通り **D** のコミットのみが無くなっていた。

```
git log
commit 69ec236a8acf4659bd0962a47ca843fb9623bchf (HEAD -> feature)
Author: ユーザ名 <メールアドレス>
Date:   Fri Jul 2 10:13:21 2021 +0900

    F のコミット（残したい）

commit ag28517f035f1b143d356335271de88c5c7136ef
Author: ユーザ名 <メールアドレス>
Date:   Fri Jun 25 17:04:23 2021 +0900

    E のコミット（残したい）

commit 59e85460e245827455d94oafe84cad9ae3xvm33l ←この文字列をコピー
Author: ユーザ名 <メールアドレス>
Date:   Fri Jun 25 16:53:33 2021 +0900

    C のコミット（ここに付け足したい）
```


# おわりに
rebase 機能は知識として知っていたつもりだったけれど、実際に使うとなると全く分かってなかった。
あと rebase を同一ブランチ上で実行できることも初めて知った。

# 参考

- https://backlog.com/ja/git-tutorial/stepup/33/
- https://www.atlassian.com/ja/git/tutorials/rewriting-history/git-rebase