古いコミットを書き換える: 歴史修正主義者のための git rebase -i 入門

直前のコミットをやり直したいときは、git commit --amend を使うと可能だ。そして、さらに昔のコミットをやり直す(書き換える)ときは、git rebase -i を使う。

git rebase -i を使うと、引数にとったコミット以降のコミット系列に対して、コミットの書き換え、削除、統合を行うことができる。


次の課題をこなすことを目標としながら、git rebase -i の動作を追っていこう。

課題「最新のものから古いほうへ3つ分のコミット(HEAD, HEAD~1, HEAD~2)のログメッセージを書き換えたい」

git rebase -i の起動

まず、変更したいコミットで一番古いものより一つ古いものを引数にして、git rebase -i を実行する。この場合は HEAD~3 である。

$ git rebase -i HEAD~3

すると、エディタが rebase コマンドへの指示ファイル(指示票)を開く。

pick 3agwc04 Add the description of DNS 
pick d5uu0c2 Rewrite the description of Wake-on-LAN
pick 399gee5 Rewrite the description of backup server

# Rebase ecd523e..399gee5 onto ecd523e
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

ここでは、上記のファイルの先頭から3行の pick を edit にして*1、保存・終了する。すると、3agwc04、d5uu0c2、399gee5の3つのコミットを、この順に書き換える作業が開始される。

edit によるコミット書き換え

まず最初に、コミット 3agwc04 にまで状況が戻る(最新のコミットが 3agwc04 になる)。なので、ここでやりたい作業を行ってから、git commit --amend でコミットメッセージを適宜書き換えて、コミットし直す*2。

再コミットが終わったら、 git rebase --continue する。すると、次のコミット d5uu0c2 に移るので、以下同様にコミットの書き換えを行っていく。

最後のコミット(ここでは 399gee5)の書き換えが終わってから git rebase --continue することで、全ての作業が終了する。


以上、コミットの書き換えの説明をしてきた。余談として、以下、コミットの統合と削除について説明する。

余談1: squash によるコミット統合

rebase への指示票で pick を squash に直すと、直前のコミットと自コミットを一つにまとめたコミットが作られる。例えば、上で引用した指示票の3行目の pick を squash にすると、2行目と3行目のコミットが一つのコミットとしてまとめられることになる。

指示票を保存・終了すると、2行目と3行目の二つのコミットのログメッセージをまとめるための指示票をエディタが開く。それを適当に編集して保存、終了すれば、コミットの統合は完了する。

余談2: コミットの削除

rebase への指示票で、あるコミットの行を削除すると、そのコミットをなかったことにできる(削除できる)。例えば、上で引用した指示票の2行目を削除して保存・終了すると、コミット d5uu0c2 はなかったことになる。

*1:e でもよい。

*2:今回の課題は「コミットメッセージを書き換える」ということなので、いきなり git commit --amend するだけでよい。