git revert -nで、複数コミットの取り消しを1つにする方法

​​Git​コマンドライン

プッシュ済みの複数のコミットをまとめて元に戻したいとき、git revert に範囲を渡すと、取り消しのコミットが件数分できます。-n(--no-commit)を付けると、取り消しの変更をステージするだけでコミットは作らないので、自分で1回 git commit すれば、取り消しを1件のコミットにまとめられます。リリースした機能をまるごと取り下げるときのように、関連する複数のコミットを、履歴を書き換えずに1回で取り消したい場面で使えます。

補足

検証環境は Ubuntu 24.04、Git 2.43.0 です。作者の名前とメールアドレスには例示用の値(Neko <neko@example.com>)を設定しました。出力が毎回同じになるよう、コミットの日時は環境変数で固定しています。検証は1つのローカルのリポジトリで行い、リモートへのプッシュ、Windows・macOS、取り消しの途中で起きる競合(コンフリクト)は試していません。

取り消したい3件のコミット

例に使うのは、送料を0円にする、お知らせを出す、バナーを出す、という3件のコミットでセールを始め、そのあとに関係のない「問い合わせ先を追加」のコミットを積んだ履歴です。セールを終えるので、真ん中の3件だけを取り消します。

console
$ git log --oneline
04b3d53 問い合わせ先を追加
a984c33 セールのバナーを追加
ed3823a セールのお知らせを追加
5b4de5f セール中は送料を0円に変更
1a86817 料金表を追加

$ ls
banner.html
contact.txt
news.txt
price.txt

$ cat price.txt
送料: 0円
手数料: 200円

$ git status
On branch main
nothing to commit, working tree clean

取り消す3件は、範囲で HEAD~4..HEAD~1 と書けます。A..B は「B からたどれるコミットのうち、A からもたどれるものを除いたもの」なので、左端の HEAD~4(1a86817)は入らず、HEAD~3・HEAD~2・HEAD~1 の3件になります。範囲の書き方は、コミットの指定方法をまとめた公式リファレンス(gitrevisions)に載っています。

-n を付けないと、取り消しのコミットが3件できる

比べるために、まず -n を付けずに実行しました。--no-edit は、エディターを開かずに自動のメッセージでコミットする指定です。

console
$ git revert --no-edit HEAD~4..HEAD~1
[main 48cc932] Revert "セールのバナーを追加"
 Date: Sat Nov 14 10:00:00 2026 +0900
 1 file changed, 1 deletion(-)
 delete mode 100644 banner.html
[main bbe3b44] Revert "セールのお知らせを追加"
 Date: Sat Nov 14 10:00:00 2026 +0900
 1 file changed, 1 deletion(-)
 delete mode 100644 news.txt
[main 4ea5c3e] Revert "セール中は送料を0円に変更"
 Date: Sat Nov 14 10:00:00 2026 +0900
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git log --oneline
4ea5c3e Revert "セール中は送料を0円に変更"
bbe3b44 Revert "セールのお知らせを追加"
48cc932 Revert "セールのバナーを追加"
04b3d53 問い合わせ先を追加
a984c33 セールのバナーを追加
ed3823a セールのお知らせを追加
5b4de5f セール中は送料を0円に変更
1a86817 料金表を追加

新しいものから順に1件ずつ取り消され、Revert "…" のコミットが3件並びました。ここでは、このあとの検証のために git reset --hard HEAD~3 で3件の取り消しコミットを消し、元の履歴に戻しました。このあとの各節も、04b3d53 で作業ツリーがきれいな状態から始めています。

git revert -n で、取り消しの変更をステージする

同じ範囲を、-n を付けて取り消します。

console
$ git revert -n HEAD~4..HEAD~1

$ git log --oneline
04b3d53 問い合わせ先を追加
a984c33 セールのバナーを追加
ed3823a セールのお知らせを追加
5b4de5f セール中は送料を0円に変更
1a86817 料金表を追加

何も表示されず、履歴も増えていません。git revert の公式リファレンスの -n の説明どおり、取り消しの変更を作業ツリーとインデックス(ステージ)に反映するだけで、コミットは作りません。git status で見ると、3件分の取り消しがまとめてステージされています。

console
$ git status
On branch main
You are currently reverting commit 5b4de5f.
  (all conflicts fixed: run "git revert --continue")
  (use "git revert --skip" to skip this patch)
  (use "git revert --abort" to cancel the revert operation)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    banner.html
	deleted:    news.txt
	modified:   price.txt

$ git diff --cached --stat
 banner.html | 1 -
 news.txt    | 1 -
 price.txt   | 2 +-
 3 files changed, 1 insertion(+), 3 deletions(-)

$ ls
contact.txt
price.txt

$ cat price.txt
送料: 500円
手数料: 200円

バナーとお知らせのファイルが消え、送料が500円に戻りました。git status には「all conflicts fixed: run "git revert –continue"」と出ますが、競合は起きていません。このあとは、ふつうに git commit すればコミットできます。

自分でメッセージを書いて、1件のコミットにする

git commit にメッセージを渡します。-m を2回書くと、2つのメッセージが空行をはさんで入り、1つ目が件名、2つ目が本文になります(git commit の公式リファレンス)。本文には、何を取り消したかが後から分かるよう、元のコミットを書いておきました。

console
$ git commit -m "セールを終了する" -m "次の3件のコミットをまとめて取り消す。
a984c33 セールのバナーを追加
ed3823a セールのお知らせを追加
5b4de5f セール中は送料を0円に変更"
[main 626d789] セールを終了する
 3 files changed, 1 insertion(+), 3 deletions(-)
 delete mode 100644 banner.html
 delete mode 100644 news.txt

$ git log --oneline
626d789 セールを終了する
04b3d53 問い合わせ先を追加
a984c33 セールのバナーを追加
ed3823a セールのお知らせを追加
5b4de5f セール中は送料を0円に変更
1a86817 料金表を追加

$ git status
On branch main
nothing to commit, working tree clean

取り消しのコミットは 626d789 の1件だけです。元の3件は履歴に残ったままで、履歴は書き換えていません。

-n を付けない場合は取り消しのコミットが3件、-n を付けてから git commit した場合は1件だけ増えることを、2つの履歴を縦に並べて比べた図
上は -n なしで実行したあと、下は -n を付けて git commit したあとの履歴。灰色の3件が取り消す対象

セールを始める前の 1a86817 と比べると、違いは、あとから足した contact.txt だけです。3件の変更がきれいに打ち消されていることが分かります。

console
$ git diff 1a86817 HEAD --stat
 contact.txt | 1 +
 1 file changed, 1 insertion(+)

メッセージを書かずにコミットすると、1件分の説明しか入らない

-n のあとに -m を付けずに git commit すると、エディターに最初から入っているメッセージは、最後に取り消した1件(ここでは一番古い 5b4de5f)のものだけでした。エディターに何が渡されるかを見るため、エディターの代わりに cat を使って中身を表示しています。

console
$ GIT_EDITOR=cat git commit
Revert "セール中は送料を0円に変更"

This reverts commit 5b4de5f1687de76fd4f333fc434b1cef5aed24a1.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
# You are currently reverting commit 5b4de5f.
#
# Changes to be committed:
#	deleted:    banner.html
#	deleted:    news.txt
#	modified:   price.txt
#
[main 2f3060c] Revert "セール中は送料を0円に変更"
 3 files changed, 1 insertion(+), 3 deletions(-)
 delete mode 100644 banner.html
 delete mode 100644 news.txt

中身は3件分の取り消しなのに、件名は送料の1件だけを取り消したように読めます。エディターで書き直すか、-m でメッセージを渡します。公式リファレンスも、取り消す理由をメッセージに書くことを強く勧めています。

続いていないコミットは、1つずつ並べる

範囲ではなく、取り消したいコミットを並べても指定できます。送料とバナーの2件だけを取り消し、お知らせは残す例です。

console
$ git revert -n 5b4de5f a984c33

$ git status --short
D  banner.html
M  price.txt

$ cat price.txt
送料: 500円
手数料: 200円

$ git commit -q -m "送料とバナーをセール前に戻す"

$ git show --stat --format="%h %s" HEAD
9841f5a 送料とバナーをセール前に戻す

 banner.html | 1 -
 price.txt   | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

この場合も、2件の取り消しが1件のコミットになりました。

コミットする前にやめるときは –abort

-n で取り消しをステージしたあと、コミットせずにやめるときは git revert --abort を使います。実行する前の状態に戻りました。

console
$ git revert -n HEAD~4..HEAD~1

$ git status --short
D  banner.html
D  news.txt
M  price.txt

$ git revert --abort

$ git status
On branch main
nothing to commit, working tree clean

ステージ済みの変更は、取り消しと一緒に混ざる

公式リファレンスによると、-n を付けたときは、インデックスが HEAD と一致していなくてもかまいません。取り消しは、そのときのインデックスの状態に重ねて行われます。そのため、関係のない変更をステージしたまま実行すると、それも同じコミットに入ります。

console
$ echo "来月の予定" > memo.txt && git add memo.txt

$ git status --short
A  memo.txt

$ git revert -n HEAD~4..HEAD~1

$ git status --short
D  banner.html
A  memo.txt
D  news.txt
M  price.txt

memo.txt を入れたくないときは、コミットする前に git restore --staged でステージから外します。外したあとは、取り消しの3ファイルだけがコミットされました。

console
$ git restore --staged memo.txt

$ git status --short
D  banner.html
D  news.txt
M  price.txt
?? memo.txt

$ git commit -q -m "セールを終了する"

$ git show --stat --format="%h %s" HEAD
cb68e34 セールを終了する

 banner.html | 1 -
 news.txt    | 1 -
 price.txt   | 2 +-
 3 files changed, 1 insertion(+), 3 deletions(-)
注意

ステージ済みの変更があるまま git revert -n を実行し、そのあと --abort すると、取り消しの変更と一緒に、ステージしてあった変更も消えました。検証環境では、新しく作って git add しただけの memo.txt が、ファイルごとなくなりました(上のコミットを git reset --hard HEAD~1 で消し、memo.txt をもう一度ステージしてから試しています)。

console
$ git add memo.txt

$ git status --short
A  memo.txt

$ git revert -n HEAD~4..HEAD~1

$ git revert --abort

$ git status --short

$ cat memo.txt
cat: memo.txt: No such file or directory

-n を使う前に、関係のない変更はステージに残さないようにします(--abort で消えたのは、ステージしてあった memo.txt でした)。

一方、-n を付けないときは、ステージ済みの変更があると取り消しが始まりませんでした。

console
$ echo "来月の予定" > memo.txt && git add memo.txt

$ git revert --no-edit HEAD~4..HEAD~1
error: your local changes would be overwritten by revert.
hint: commit your changes or stash them to proceed.
fatal: revert failed

このとき履歴は変わっていませんが、git status に「Revert currently in progress.」が残りました。git revert --quit で、この途中の状態を消せます。確かめたあとは、memo.txt をステージから外して消しています。

取り消すファイルにコミットしていない変更があると、途中で止まる

取り消すファイル(ここでは price.txt)を、ステージせずに書き換えたまま実行すると、途中でエラーになりました。

console
$ echo "送料無料キャンペーン" >> price.txt

$ git revert -n HEAD~4..HEAD~1
error: Your local changes to the following files would be overwritten by merge:
	price.txt
Please commit your changes or stash them before you merge.
Aborting
fatal: revert failed

$ git status
On branch main
You are currently reverting commit ed3823a.
  (all conflicts fixed: run "git revert --continue")
  (use "git revert --skip" to skip this patch)
  (use "git revert --abort" to cancel the revert operation)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    banner.html
	deleted:    news.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   price.txt

ステージされているのは、バナーとお知らせの2件分の取り消しだけで、送料の取り消しは入っていません。新しい順に取り消していき、price.txt を書き換える送料のコミットのところで止まったためです。You are currently reverting commit ed3823a. の ed3823a は、お知らせのコミットです。

エラーの表示(Please commit your changes or stash them)のとおり、取り消すファイルに手を入れているときは、その変更を先にコミットするか退避してから git revert -n を実行します。

途中で止まったこの状態で git revert --abort を実行すると、ステージされていた2件分の取り消しはなくなり、price.txt に書き足した行は残りました。

console
$ git revert --abort

$ git status --short
 M price.txt

$ cat price.txt
送料: 0円
手数料: 200円
送料無料キャンペーン

​Posted by 86