Gitのrebase -iで、コミットの順番とメッセージを直す方法

​​Git​コマンドライン

git rebase -i の todo リストで行を並べ替えると、コミットの順番を入れ替えられます。pick を reword に書き換えると、前のコミットのメッセージだけを直せます。プルリクエストを出す前に、コミットの順番を整えたり、過去のコミットメッセージの誤字を直したりしたいときに使う操作です。操作の前と後の git log を並べて、何が変わり、何が変わらないのかを確かめます。

補足

検証環境は Ubuntu 24.04、Git 2.43.0 です。作者の名前とメールアドレスには例示用の値(Neko <neko@example.com>)を設定し、コミットの日時は環境変数で決めました。エディターを手で操作する代わりに、エディターに渡された内容を表示してから書き換える小さなシェルスクリプトを、環境変数 GIT_SEQUENCE_EDITOR(todo リスト用)と GIT_EDITOR(メッセージ用)に指定しています。本文のコマンドでは、この環境変数の指定と、rebase の進み具合の表示(Rebasing (1/3) など、画面では上書きされて残らない行)を省いています。Vim や VS Code などの実物のエディターでの操作、Windows・macOS、変更がぶつかったあとに解決して git rebase --continue で続ける操作、プッシュしたあとのコミットの書き換えは試していません。

操作する前の履歴

最初のコミットの上に、3つのコミットがある状態から始めます。「READMEに使い方を書く」をいちばん先に来るように入れ替え、メッセージの誤字「トップペーシ」(正しくは「トップページ」)を直します。

console
$ git log --oneline
28f6be4 READMEに使い方を書く
eb10425 プロフィールページ追加
4ad541a トップペーシを追加
a1257cd 最初のコミット

todo リストの行を並べ替えて、順番を入れ替える

入れ替えたいコミットが新しいほうから3つなので、git rebase -i HEAD~3 を実行します。エディターには、次の todo リストが開きます(# で始まる説明の行は、途中を省いています)。

console
$ git rebase -i HEAD~3
pick 4ad541a トップペーシを追加
pick eb10425 プロフィールページ追加
pick 28f6be4 READMEに使い方を書く

# Rebase a1257cd..28f6be4 onto a1257cd (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit 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.
(後略)

todo リストは git log --oneline と並びが逆で、上が古いコミットです(Pro Git 日本語版の「歴史の書き換え」にも、この点の注意があります)。説明の行にあるとおり、上の行から順に実行されます。

「READMEに使い方を書く」を最初に適用したいので、3行目を1行目に移して、保存して閉じます。pick の後ろのハッシュは、そのまま残します。git rebase の公式リファレンスによると、Git が見るのはハッシュのほうで、後ろのメッセージは読む人のための表示です。

text
pick 28f6be4 READMEに使い方を書く
pick 4ad541a トップペーシを追加
pick eb10425 プロフィールページ追加

閉じると、3つのコミットが新しい順番で作り直されました。

console
Successfully rebased and updated refs/heads/main.

$ git log --oneline
256ec3d プロフィールページ追加
ec079e9 トップペーシを追加
a09fa8d READMEに使い方を書く
a1257cd 最初のコミット
入れ替える前は READMEに使い方を書く がいちばん上にあり、後はいちばん下の最初のコミットのすぐ上に移っている。3つのコミットのハッシュはすべて変わる
順番を入れ替える前と後の履歴。READMEのコミットが下へ移り、ハッシュは3つとも変わる

「READMEに使い方を書く」が、最初のコミットのすぐ上に移りました。動かしていない2つのコミットも、ハッシュが変わっています。

順番を変えても、ファイルの中身は同じです。検証では、入れ替える前に git tag before-reorder でタグを付けておきました。入れ替える前と後で git diff を取ると、何も出ません。

「READMEに使い方を書く」のコミットは、移したあとも README.md に2行を足す中身のままです。

console
$ git diff before-reorder HEAD

$ git show --stat --format=%s HEAD~2
READMEに使い方を書く

 README.md | 2 ++
 1 file changed, 2 insertions(+)

作者の日時(AuthorDate)は、どのコミットも元のままでした。入れ替えたあとの履歴を日時つきで出すと、新しい順に並べた git log の中で、10:30に作ったコミットが10:10や10:20より下に来ています。

console
$ git log --format="%h %ad %s" --date=format:%H:%M
256ec3d 10:20 プロフィールページ追加
ec079e9 10:10 トップペーシを追加
a09fa8d 10:30 READMEに使い方を書く
a1257cd 10:00 最初のコミット

入れ替える前のブランチの最新のコミットは、ORIG_HEAD に残っていました(git rebase の公式リファレンス)。入れ替え方を間違えたときは、git reset --hard ORIG_HEAD で入れ替える前に戻せます。検証では、戻せたことを確かめてから、入れ替えた後の状態に戻して先へ進めました。

console
$ git log --oneline -1 ORIG_HEAD
28f6be4 READMEに使い方を書く

$ git reset --hard ORIG_HEAD
HEAD is now at 28f6be4 READMEに使い方を書く

$ git log --oneline
28f6be4 READMEに使い方を書く
eb10425 プロフィールページ追加
4ad541a トップペーシを追加
a1257cd 最初のコミット

git reset --hard は、Git が追跡しているファイルの、コミットしていない変更を捨てます(git reset の公式リファレンス)。戻す前に git status で、残したい変更がないかを確かめてください。

reword で、前のコミットのメッセージを直す

次に、「トップペーシを追加」のメッセージを「トップページを追加」に直します。このコミットは最新ではないので、git commit --amend では直せません。もう一度 git rebase -i HEAD~3 を実行し、直したいコミットの行の pick を reword(短く r でも可)に書き換えます。公式リファレンスでは、メッセージだけを直したいときは reword を使う、と説明しています(git rebase の公式リファレンス)。

text
pick a09fa8d READMEに使い方を書く
reword ec079e9 トップペーシを追加
pick 256ec3d プロフィールページ追加

todo リストを閉じると、reword の行のところで、メッセージ用のエディターが開きます。いまのメッセージが入った状態です。

text
トップペーシを追加

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Fri Nov 13 10:10:00 2026 +0900
#
# interactive rebase in progress; onto a1257cd
# Last commands done (2 commands done):
#    pick a09fa8d READMEに使い方を書く
#    reword ec079e9 トップペーシを追加
# Next command to do (1 remaining command):
#    pick 256ec3d プロフィールページ追加
(後略)

1行目を「トップページを追加」に書き換えて保存し、閉じます。残りの pick も進み、rebase が終わりました。

console
[detached HEAD 79dd73b] トップページを追加
 Date: Fri Nov 13 10:10:00 2026 +0900
 1 file changed, 1 insertion(+)
 create mode 100644 index.html

Successfully rebased and updated refs/heads/main.

前と後の履歴を比べます。

console
(直す前)
$ git log --oneline
256ec3d プロフィールページ追加
ec079e9 トップペーシを追加
a09fa8d READMEに使い方を書く
a1257cd 最初のコミット

(直した後)
$ git log --oneline
42e9f35 プロフィールページ追加
79dd73b トップページを追加
a09fa8d READMEに使い方を書く
a1257cd 最初のコミット

メッセージを直したコミット(79dd73b)と、その上のコミット(42e9f35)は、ハッシュが変わりました。直したコミットより下の a09fa8d は、todo リストに入っていましたが、ハッシュはそのままでした。メッセージ以外は変わっていないので、直す前と後の git diff は空です。

console
$ git diff before-reword HEAD

$ git log -1 --format=fuller HEAD~1
commit 79dd73b8135b35eeb42001865413a431d1cc09c8
Author:     Neko <neko@example.com>
AuthorDate: Fri Nov 13 10:10:00 2026 +0900
Commit:     Neko <neko@example.com>
CommitDate: Fri Nov 13 11:10:00 2026 +0900

    トップページを追加

before-reword は、直す前に付けておいたタグです。AuthorDate は元の10:10のままで、コミットした日時(CommitDate)だけが rebase を実行した11:10になっています。

最新のコミットなら git commit –amend で直せる

直したいのがいちばん新しいコミットなら、rebase は要りません。git commit --amend で、最新のコミットを作り直せます(git commit の –amend の説明)。-m で新しいメッセージを渡すと、エディターは開きません。ここでは、「プロフィールページ追加」に抜けていた「を」を足しました。

console
$ git log --oneline -1
42e9f35 プロフィールページ追加

$ git commit --amend -m "プロフィールページを追加"
[main 4c7a930] プロフィールページを追加
 Date: Fri Nov 13 10:20:00 2026 +0900
 1 file changed, 1 insertion(+)
 create mode 100644 profile.html

$ git log --oneline
4c7a930 プロフィールページを追加
79dd73b トップページを追加
a09fa8d READMEに使い方を書く
a1257cd 最初のコミット

--amend の前に git add した変更があると、その変更も最新のコミットに入ります(Pro Git 日本語版の「歴史の書き換え」)。メッセージだけを直したいときは、git status で何も git add していないことを確かめてから実行してください。

近い行を変えたコミットを入れ替えると、途中で止まる

ここまでの入れ替えでは、3つのコミットが別々のファイルを変えていたので、すんなり進みました。同じファイルの隣り合う行を変えたコミットを入れ替えたときは、変更がぶつかって rebase が止まりました。

次の2つのコミットは、どちらも index.html を変えています。「あいさつを書く」で2行目に <p>ようこそ</p> を足し、そのあと「見出しを変える」で1行目の見出しを書き換えました。

console
$ git log --oneline
b1c4d7a トップページの見出しを変える
a426f79 トップページにあいさつを書く
4c7a930 プロフィールページを追加
79dd73b トップページを追加
a09fa8d READMEに使い方を書く
a1257cd 最初のコミット

git rebase -i HEAD~2 の todo リストで、この2行を入れ替えます。

text
pick b1c4d7a トップページの見出しを変える
pick a426f79 トップページにあいさつを書く

閉じると、1つ目を適用するところで CONFLICT が出て、rebase が止まりました。

console
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
error: could not apply b1c4d7a... トップページの見出しを変える
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply b1c4d7a... トップページの見出しを変える

git diff で見ると、<h1>トップ</h1> の1行だけの index.html(HEAD の側)と、「見出しを変える」のコミットの2行が、ぶつかっています。「見出しを変える」は「あいさつを書く」のあとの2行をもとに作ったコミットなので、先に適用すると、<p>ようこそ</p> がまだない状態に当てることになります。

console
$ git diff
diff --cc index.html
index 0d5c7d9,cf2bfe9..0000000
--- a/index.html
+++ b/index.html
@@@ -1,1 -1,2 +1,6 @@@
++<<<<<<< HEAD
 +<h1>トップ</h1>
++=======
+ <h1>猫の手のトップ</h1>
+ <p>ようこそ</p>
++>>>>>>> b1c4d7a (トップページの見出しを変える)

ヒントにあるとおり、ぶつかった箇所を直して git add し、git rebase --continue で続けることもできます(git rebase の公式リファレンス)。入れ替えをやめるなら、git rebase --abort で rebase を始める前の状態に戻ります(git rebase の –abort の説明)。検証では --abort で戻し、履歴が始める前と同じになったことを確かめました。

console
$ git rebase --abort

$ git log --oneline
b1c4d7a トップページの見出しを変える
a426f79 トップページにあいさつを書く
4c7a930 プロフィールページを追加
79dd73b トップページを追加
a09fa8d READMEに使い方を書く
a1257cd 最初のコミット

$ git status --short --branch
## main

プッシュしたコミットは書き換えない

順番の入れ替えでも、reword でも、--amend でも、書き換えたコミットは新しいハッシュのコミットになりました。Pro Git 日本語版の「歴史の書き換え」では、すでに共有のサーバーにプッシュしたコミットは、rebase の対象に含めないように、--amend でも書き換えないように、と注意しています。ここで扱った操作は、手元にしかないコミットを、プッシュする前に整えるためのものです。

​Posted by 86