git revert — 打ち消すコミットを足す
コミットを取り消す方法は2つあります。前の回の git reset は、ブランチの付箋を後ろに戻して、コミットを歴史から外します。この回の git revert は逆で、変更を打ち消す新しいコミットを足します。歴史は1つも消えず、ブランチは前に進みます。この回では、revert で何が足されるのかを見てから、reset との違いをグラフで比べます。
- 上のターミナルは、実際に打ったときと同じ出力です(Git は英語で答えます)
- 下の中で起きていることは、コマンドの結果、Git の中の何が変わったかを図にしたものです。丸がコミット、黒い札がブランチ、ピンクの札が HEAD です
- 右の説明をスクロールして読み進めると、左のターミナルでコマンドが1つずつ打たれます。
→キーでも1つずつ進みます(←で1つ戻ります。スマホでは「次へ」ボタン)。最後まで進んでから→を押すと次の区間へ移ります。黄色く光っているのが、そのコマンドで変わった場所です
1. git revert HEAD — 直前のコミットを打ち消す
git revert コミット は、指定したコミットの変更を逆向きにした変更を作り、それを新しいコミットとして足します。1行足したコミットを revert すると、その1行を消すコミットができます。
| 項目 | git revert で起きること |
|---|---|
| 足されるもの | 打ち消しのコミットが1つ(親は今の HEAD) |
| メッセージ | Revert "元のメッセージ" |
| ブランチ | 新しいコミットへ1つ進む |
| 元のコミット | 歴史に残ったまま |
git revert HEAD — 直前のコミットを打ち消す
最後のコミット「アイスを追加」は、入れるべきではありませんでした。このコミットを取り消したい、という場面です。
git revert HEADは、HEAD のコミットの変更を 逆向きにした新しいコミット を作ります。メッセージはRevert "アイスを追加"です。mainは後ろに下がらず、1つ 前に進みました。$ git revert HEAD [main c8b9add] Revert "アイスを追加" 1 file changed, 1 deletion(-)ファイルからは「アイスを買う」が消え、「パンを追加」の時点と同じ中身になりました。
$ cat todo.txt 牛乳を買う 卵を買う パンを買う新しいコミットの中身を見ると、「アイスを買う」の行を 消す 変更になっています。元のコミットの足し算を、引き算で打ち消したわけです。
$ git show HEAD commit c8b9add (HEAD -> main) Revert "アイスを追加" diff --git a/todo.txt b/todo.txt --- a/todo.txt +++ b/todo.txt -アイスを買う「アイスを追加」のコミットは、歴史に 残ったまま です。その上に「取り消した」という記録が1つ足されています。revert は歴史を消さずに、前に進みながら打ち消します。
$ git log --oneline c8b9add (HEAD -> main) Revert "アイスを追加" a7c3e90 アイスを追加 e5c0a62 パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
ふつうに git revert を打つと、Git はメッセージを確かめるためにエディタを開きます。閉じれば、Revert "…" のメッセージのまま確定します。エディタを開かずに確定するには --no-edit を付けます。
2. reset と revert — 戻るか、進むか
同じ「アイスを追加」を取り消すのでも、reset と revert ではグラフの動きが逆です。ファイルの結果は同じでも、reset は付箋を後ろに戻し、revert は前に進みながら打ち消す、と覚えてください。
git reset --hard HEAD~1 | git revert HEAD | |
|---|---|---|
| ブランチの付箋 | 1つ前のコミットへ戻る | 新しいコミットへ進む |
| 取り消したコミット | ブランチから辿れなくなる(歴史から外れる) | 歴史に残る |
| コミットの数 | 増えない | 1つ増える |
| 未コミットの変更 | 消える(--hard の場合) | あると止まることがある(4節) |
reset と revert をグラフで比べる
同じ「アイスを追加」を、今度は
resetとrevertの両方で取り消して、グラフの動きを比べます。まず
resetです。mainの付箋が1つ前の「パンを追加」へ 戻り、「アイスを追加」はmainから辿れなくなりました。歴史から外したことになります。$ git reset --hard HEAD~1 HEAD is now at e5c0a62 パンを追加mainの歴史には、もう「アイスを追加」が出てきません。$ git log --oneline e5c0a62 (HEAD -> main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加比べるために、コミット ID を指定して
mainを「アイスを追加」へ戻します。付箋を貼り直しただけなので、コミットは元のまま使えます。$ git reset --hard a7c3e90 HEAD is now at a7c3e90 アイスを追加今度は
revertです。mainは後ろに下がらず、打ち消しのコミットを足して 前に進みます。ファイルの中身は、reset のときと同じ「パンを追加」の時点になりました。$ git revert HEAD [main c8b9add] Revert "アイスを追加" 1 file changed, 1 deletion(-)ファイルの結果は同じでも、歴史は違います。reset は付箋を戻して歴史から外す。revert は歴史を残したまま、打ち消しを足す。
$ git log --oneline c8b9add (HEAD -> main) Revert "アイスを追加" a7c3e90 アイスを追加 e5c0a62 パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
3. 前のほうのコミットを revert する
revert できるのは直前のコミットだけではありません。git revert HEAD~2 のように、2つ前や、もっと前のコミットも指定できます。打ち消されるのは指定したコミットが変えた分だけで、そのあとのコミットはそのまま残ります。
reset ではこうはいきません。reset は付箋を戻すので、取り消したいコミットより後ろのコミットまで、まとめて歴史から外れてしまいます。
2つ前のコミットを revert する
revertは直前のコミットに限りません。ここでは、2つ前の「日曜に変更」だけを取り消します。そのあとの2つのコミットは残したままにします。HEAD~2が「日曜に変更」です。$ git log --oneline 71d9b3e (HEAD -> main) バターを追加 e5c0a62 パンを追加 c41e7a2 日曜に変更 8b41d07 卵を追加 3f2a9c1 牛乳を追加「日曜に変更」が変えたのは
notes.txtだけです。あとの「パンを追加」「バターを追加」はtodo.txtしか変えていません。$ git show --stat HEAD~2 commit c41e7a2 日曜に変更 notes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)notes.txtの変更だけを打ち消すコミットが、いちばん上に足されました。あとのコミットが同じファイルに触れていないので、すんなり打ち消せます。$ git revert HEAD~2 [main 571d9ae] Revert "日曜に変更" 1 file changed, 1 insertion(+), 1 deletion(-)notes.txtは「土曜に行く」に戻りました。$ cat notes.txt 土曜に行くtodo.txtは「バターを買う」まで残っています。取り消したのは、指定したコミットの変更だけ です。reset で取り消そうとすると「日曜に変更」より前のHEAD~3まで戻すことになり、パンとバターも一緒に外れてしまいます。$ cat todo.txt 牛乳を買う 卵を買う パンを買う バターを買う
この場面がすんなり通ったのは、あとのコミットが notes.txt に触れていなかったからです。あとのコミットが同じファイルの近くの行を変えていると、打ち消しがぶつかって途中で止まることがあります(コンフリクト)。ぶつかったときの直し方は、第3部の merge の回で扱います。
4. 書きかけの変更があると止まる
revert は新しいコミットを作るので、作業ツリーとステージを書き換えます。そのため、コミットしていない変更とぶつかると、何もせずに止まります。checkout の回で見た「書きかけを勝手に消さない」のと同じ考え方です。
書きかけの変更があると revert は止まる
todo.txtにコミットしていない1行を書いたまま、「アイスを追加」を revert してみます。$ echo "チーズを買う" >> todo.txt止まりました。 revert は
todo.txtを書き換えて新しいコミットを作るので、書きかけの「チーズを買う」とぶつかります。Git は何もせずに止まり、グラフも動いていません。$ git revert HEAD error: Your local changes to the following files would be overwritten by merge: todo.txt Please commit your changes or stash them before you merge. Aborting fatal: revert failed書きかけはそのまま残っています。revert の前に、この変更を片付ける必要があります。
$ git status On branch main 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: todo.txt no changes added to commit (use "git add" and/or "git commit -a")ここでは試しに書いた1行なので、
git restoreで捨てます。残したいときは、次の回のgit stashで退避します。$ git restore todo.txt作業ツリーがきれいになったので、今度は revert できました。
$ git revert HEAD [main f03ac52] Revert "アイスを追加" 1 file changed, 1 deletion(-)
本物の Git が止まるのは、ステージに変更があるときと、revert が書き換えるファイルに未 add の変更があるときです。別のファイルの書きかけだけなら、そのまま revert できることもあります。迷ったら、作業ツリーをきれいにしてから revert するのが確実です。書きかけを残したまま片付けたいときは、次の回の git stash を使います。
5. 共有した歴史は revert で直す(予告)
reset と revert のどちらを使うかは、そのコミットを他の人と共有したかどうかで決まります。
- まだ自分の手元にしかないコミットなら、reset で付箋を戻してかまいません。Claude Code がよく打つ
git reset --soft HEAD~1(直前のコミットをやり直す)も、この場面の道具です - すでに push して他の人も持っているコミットは、reset で消すと、相手の手元の歴史と食い違います。共有した歴史は reset せず、revert で打ち消しを足して直します
なぜ食い違うと困るのかは、リモートを扱う第2部(push・pull)と、歴史を書き換える第3部(force push)で、グラフを見ながら確かめます。
6. Claude Code の許可画面で見かけたら
この回で見たコマンドを、「何が消えうるか」で並べるとこうなります。
| コマンド | 何が起きるか | 消えうるもの |
|---|---|---|
git log / git show | 見るだけ | なし |
git revert コミット / git revert --no-edit コミット | 打ち消しのコミットを足す。元のコミットは残る | なし(未コミットの変更とぶつかるなら止まる) |
git reset --soft HEAD~1 | 付箋を1つ戻す。変更はステージに残る | 注意 push 済みのコミットなら、共有した歴史と食い違う |
git reset --hard HEAD~1 | 付箋を1つ戻し、ステージと作業ツリーもその時点にする | 未コミットの変更すべて(外れたコミットは reflog から戻せる) |
見分け方は、ブランチが後ろに戻るか、前に進むかです。revert は前に進むだけなので、何も消えません。取り消したいコミットがもう push されているなら、許可する前に、それが reset ではなく revert になっているかを確かめてください。
次の回では、書きかけの変更をいったん退避して作業ツリーをきれいにする git stash を扱います。
まとめ
git revert コミットは、そのコミットの変更を打ち消す新しいコミットを足す。メッセージはRevert "元のメッセージ"- 元のコミットは歴史に残る。ブランチは後ろに戻らず、前に進む
- 前のほうのコミットも指定できる。打ち消されるのは、そのコミットが変えた分だけ
- コミットしていない変更とぶつかると、revert は何もせずに止まる。作業ツリーをきれいにしてから打つ
- reset は付箋を戻して歴史から外す。まだ共有していないコミットには reset、共有したコミットには revert