Git で複数のコミットを一つにまとめる（git rebase -i）【Git Tips!】

# はじめに

ローカルで細かくコミットしていたものをプッシュする前にまとめてきれいにしたい、みたいなシーンってありますよね。そんな時に重宝するのが `git rebase` コマンドです。

# 手順

### まずはコミットを確認

`git log` コマンドで確認します。`--oneline` オプションで各ログを１行で表示し、`-n` オプションでログの個数を指定できます。ここではこの５つのコミットをまとめたいとします。

```
$ git log --oneline -n 5
o91ref1 (HEAD -> #117_お気に入り機能対応) refs #117 パ
7148d3d refs #117 パ(ry
kc5345f refs #117 パッケージのたm(ry
c231b7f refs #117 パッケージのためにいったんコミット
sb5ex3b refs #117 パッケージを作るためにいったんコミット
```

### コミットを一つにまとめる

`git rebase` コマンドで一つにまとめます。`git rebase -i HEAD~5` を実行すると、以下のようにテキストエディタが開きます。`HEAD~5` としてすることで最新から５つ分を対象としています。
 

```vim
pick sb5ex3b refs #117 パッケージを作るためにいったんコミット
pick c231b7f refs #117 パッケージのためにいったんコミット
pick kc5345f refs #117 パッケージのたm(ry
pick 7148d3d refs #117 パ(ry
pick o91ref1 refs #117 パ

# Rebase 1a0f7bd..o91ref1 onto 1a0f7bd (5 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
# 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
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
```

ここで大切なのは上の５行の部分です。今回は５つを一つにまとめたいので以下のように変更します。
vim の場合、`i` を押すことで編集モード（`-- INSERT --`）になります。

```vim
pick sb5ex3b refs #117 パッケージを作るためにいったんコミット
s c231b7f refs #117 パッケージのためにいったんコミット
s kc5345f refs #117 パッケージのたm(ry
s 7148d3d refs #117 パ(ry
s o91ref1 refs #117 パ
```

まとめる元となるコミットを `pick`、それ以外を `s`(=`squash`)にします。
編集が完了したら `Esc` キーで編集モードを終了し、`:wq` と入力して編集を保存し終了します。

次はコミットメッセージの編集です。同じ手順でやっていきましょう。

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

refs #117 パッケージを作るためにいったんコミット

# This is the commit message #2:

refs #117 パッケージのためにいったんコミット

# This is the commit message #3:

refs #117 パッケージのたm(ry

# This is the commit message #4:

refs #117 パ(ry

# This is the commit message #5:

refs #117 パ

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# ...(以降コミットについての説明なので割愛)
```

以下のように編集しました。`#` が付いている行はコメントとして扱われないのでそのままで大丈夫です。

```vim
refs #117 お気に入り機能対応
```

編集が完了したら、再び `:wq` で保存します。これでコミットが一つにまとめられました。

### 操作の結果を確認

`git log` コマンドで確認すると以下のように表示され、コミットが一つになっていることが確認できます。

```
$ git log --oneline -n 5
7455ff1 (HEAD -> #117_お気に入り機能対応) refs #117 お気に入り機能対応
1a0f7bd ...
11er987 ...
```

# 最後に

複数のコミットを一つにまとめる方法は以上となります。

頻繁に行う作業ではないのでつい手順を忘れてしまいがちですよね。
そんな時に参考にしていただければ幸いです。