git rebase -i でコミットをまとめて履歴を綺麗にしよう!

最終更新:2018-06-17 by Joe

Gitで、不用意にたくさん作ってしまったコミットを1つにまとめて、履歴をきれいにする「git rebase -i」についてです。

コミットをまとめる「git rebase -i」

ローカルで開発中は、バックアップの目的を兼ねて、ついついgit commitを連発してしまいます。そうすると、自然にどんどんコミット履歴が乱れていきます。

そんな場合でも、「git rebase -i」 を実行すれば、コミット履歴を後から修正することができます。

git rebase -i の「-i」は、「--interactive」の略式オプションです。

// インタラクティブモードでリベース
$ git rebase -i <コミット>

このコマンドにより、現在のブランチの先頭から、指定したコミットまでの歴史を一つずつ整理することができるのです。

git rebase -i 実行時の流れ

それでは、実際に順を追って見ていきましょう。下記は現在チェックアウトしているブランチです。

コミットAの後に続く複数のコミットをまとめたい場合、「git rebase -i <commit-A>」を実行します。A移行のコミットがひとつずつ、順番に適応し直されていきます。

この時、古いコミットから1つずつ、Aに対してリベースされていきますが「インタラクティブ」が表すように、1つずつどのように処理するかを指定することができるのです。

【実践】コミットをまとめてみる

それでは、実際に試してます。

1git log でリベース対象コミットを確認

まずコミットログを確認して、どのコミットにリベースをかけていくか、確認しましょう。

// ログをツリーで出力
$ git log --graph --oneline

// 出力
* 8274a1c (HEAD -> refs/heads/master) Add the email field to the contact form.
* 14776e2 Fix the bugs in the mobile UI.
* 02f1a92 Add the link to the admin panel.
* 3e89043 Add ga event to the mobile UI.
* 307028b Merge branch 'feature/topic-a' into master

さて、上記の状態から、マージ直後のコミット「307028b」に対してリベースをかけます。

2git rebase -i でリベースを実行

実際の「git rebase -i 307028b」を実行します。テキストエディタが起動しますが、コミットの順番がさきほどのツリーと逆向きになります。

上から下に向かって、コミットをリベースする順番で並ぶので、古いコミットから、新しいコミットの順番に並んでいる事に注意して下さい。

// 実行
$ git rebase -i 307028b

// テキストエディタが起動して、以下を編集。
pick 3e89043 Add analytic event to the mobile UI.
pick 02f1a92 Add the link to the admin panel.
pick 14776e2 Fix the bugs in the mobile UI.
pick 8274a1c Add the email field to the contact form.

# Rebase 307028b..8274a1c onto 307028b (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# 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.
#
# Note that empty commits are commented out

あとは、それぞれの行の指定を好きなように書き換えて、エディタを保存して終了すればOKです。ヘルプテキストも一緒に出力されますので、操作にそれほど迷うことは無いかと思います。

指定説明日本語
p, pickuse commitコミットを利用する
r, reworduse commit, but edit the commit messageコミットを利用する、でもコミットメッセージを編集
e, edituse commit, but stop for amendingコミットを利用する、でもコミットを修正する(ステージにあるが、git commit されていない状態)
s, squashuse commit, but meld into previous commitコミットを利用する、でも直前のコミットに統合
f, fixuplike "squash", but discard this commit's log messagesquashと似ているが、コミットメッセージをを破棄する
x, execrun command (the rest of the line) using shellこの行の末尾に指定したシェルコマンドを実行
d, dropremove commitコミットを破棄する

3まとめるコミットを指定して、エディタを保存する

今回の「コミットをまとめる」という目的においては「fixup」もしくは「squash」を指定するのが適しています。

この2つはそれほど大きな違いはありませんが、squash を指定すると、一度テキストエディタを保存した跡に、指定したコミットのコミットメッセージを書き直すようにエディタが改めて起動するのですが、2つのとコミットメッセージがデフォルトでエディタ内に記載されています(不要な方を消せばOK)。

一方で fixup を指定したコミットに対しては、そのコミットの直前のコミットメッセージしかありません。

まとめた履歴を git push するときは、コンフリクトに注意。

もし、rebase -i によって修正する前の履歴をすでにリモートに Push 済だったときは、少し注意が必要です。修正前の履歴と、修正後の履歴が、コンフリクトしてしまうからです。

もし、リモートの歴史の書き換えが問題ない状況の時は、下記のコマンドで強制的にリモートを書き換えてしまいましょう。チームで開発している方は、このような操作の前に必ずチームメンバーと相談して下さい。

// 強制的にPushする
git push -f origin <Branch name>

以上になります。

簡単ですね。

参考文献

git rebase の仕様です。