gitで複数のコミットを1つにまとめる


2010年 11月 30日

gitでは様々な方法でコミットログを書き換えることができます。
その一例として複数のコミットを1つにまとめる方法を紹介します。

問題

先日紹介した
gitでコミットの順序を入れ替える
例ですが、
そこでは以下のコミットログを:

$ git log -n 4 --oneline --reverse
0000001 Add a neat feature X into the library
0000002 Update to use X
0000003 Fix a typo in X
0000004 Fix a bug in X

以下のような形になるよう順序を入れ替えました:

$ git rebase -i HEAD~4
Successfully rebased and updated refs/heads/topic-x.
$ git log -n 4 --oneline --reverse
0000001 Add a neat feature X into the library
a000003 Fix a typo in X
a000004 Fix a bug in X
a000002 Update to use X

ここで0000002はアプリケーション側に対する変更で、
それ以外の3つはライブラリ側に対する変更です。

順序的にはこれで良くなったのですが、
このコミットログはコミットログでやはり悲しいものがあります。
人間、誰しも見栄っ張りなので、
対外的にはしょうもないタイポやミスの修正コミットは見せたくありません。
この変更はまだまだどこにも公開していないので、
いまのうちにコミットログを書き換えることにしましょう。

この場合、しょうもない修正コミットはa000003とa000004です。
この2つを0000001に合成して最初から完璧なコミットができていたことにすれば
よりコミットログが美しくなります。
しかし具体的にはどうすればよいでしょうか。

解決方法

gitではこのような複数のコミットを1つにまとめることができます。
方法は色々とありますが、
git rebase
を使うのが一番簡単です。
まずは次のコマンドを実行しましょう:

$ git rebase -i HEAD~4

これを実行すると
あなたの好きだったエディタ
が起動し、以下のようなテキストが表示されます:

pick 0000001 Add a neat feature X into the library
pick a000003 Fix a typo in X
pick a000004 Fix a bug in X
pick a000002 Update to use X

# Rebase 0000000..a000002 onto 0000000
#
# 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
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

pick の行は git rebase -i での操作対象の各コミットを表します。
行頭にある pick はコミットに対する指示を表します。
pick の場合は「このコミットをそのまま取り込む」ことを意味します。
コミットに対する指示は上から順番に処理されるので、
pick の行を入れ替えればコミットの順序を入れ替えることができました。

pick 以外にも様々な指示ができます。
例えば fixup は「このコミットでの変更内容を直前のコミットに合成する」ことを意味します。
今回の場合は0000001にa000003とa000004を混ぜて1つにしたいので、
以下のように編集します:

pick 0000001 Add a neat feature X into the library
fixup a000003 Fix a typo in X
fixup a000004 Fix a bug in X
pick a000002 Update to use X

この編集内容を上書き保存してエディタを終了すると、
先程エディタで編集した内容の通りコミットの合成が始まります:

$ git rebase -i HEAD~4
[detached HEAD b000001] Add a neat feature X into the library
8 files changed, 94 insertions(+), 9 deletions(-)
Successfully rebased and updated refs/heads/topic-x.

なにやらメッセージが表示されていますが成功したようです。結果を確認してみましょう:

$ git log -n 2 --oneline --reverse
b000001 Add a neat feature X into the library
b000002 Update to use X

最初よりはすごくまともなコミットログになりました。やりましたね。

補足

なお、 fixup の代わりに squash を使うこともできます。

  • squash の場合は合成対象の各コミット
    (この例の場合は0000001とa000003とa000002)のログを編集することができます。
  • fixup の場合はベースとなるコミット(この例の場合は0000001)のログをそのまま使い、
    合成される各コミット(この例の場合はa000003とa000002)のログは捨てられます。

今回のように複数のコミットを1つにまとめる場合は
「小さな変更をパッチのようにコミットしておいて後から綺麗な順序に並び変えて合成する」
という用途が多いので、合成される各コミットのログは不要なことがほとんどです。
なので fixup を使う方法が便利です。

(続く)