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 — 直前のコミットを打ち消す

  1. 最後のコミット「アイスを追加」は、入れるべきではありませんでした。このコミットを取り消したい、という場面です。

  2. git revert HEAD は、HEAD のコミットの変更を 逆向きにした新しいコミット を作ります。メッセージは Revert "アイスを追加" です。main は後ろに下がらず、1つ 前に進みました。

    $ git revert HEAD
    [main c8b9add] Revert "アイスを追加"
     1 file changed, 1 deletion(-)
  3. ファイルからは「アイスを買う」が消え、「パンを追加」の時点と同じ中身になりました。

    $ cat todo.txt
    牛乳を買う
    卵を買う
    パンを買う
  4. 新しいコミットの中身を見ると、「アイスを買う」の行を 消す 変更になっています。元のコミットの足し算を、引き算で打ち消したわけです。

    $ git show HEAD
    commit c8b9add (HEAD -> main)
    
        Revert "アイスを追加"
    
    diff --git a/todo.txt b/todo.txt
    --- a/todo.txt
    +++ b/todo.txt
    -アイスを買う
  5. 「アイスを追加」のコミットは、歴史に 残ったまま です。その上に「取り消した」という記録が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~1git revert HEAD
ブランチの付箋1つ前のコミットへ戻る新しいコミットへ進む
取り消したコミットブランチから辿れなくなる(歴史から外れる)歴史に残る
コミットの数増えない1つ増える
未コミットの変更消える(--hard の場合)あると止まることがある(4節)

reset と revert をグラフで比べる

  1. 同じ「アイスを追加」を、今度は reset と revert の両方で取り消して、グラフの動きを比べます。

  2. まず reset です。main の付箋が1つ前の「パンを追加」へ 戻り、「アイスを追加」は main から辿れなくなりました。歴史から外したことになります。

    $ git reset --hard HEAD~1
    HEAD is now at e5c0a62 パンを追加
  3. main の歴史には、もう「アイスを追加」が出てきません。

    $ git log --oneline
    e5c0a62 (HEAD -> main) パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加
  4. 比べるために、コミット ID を指定して main を「アイスを追加」へ戻します。付箋を貼り直しただけなので、コミットは元のまま使えます。

    $ git reset --hard a7c3e90
    HEAD is now at a7c3e90 アイスを追加
  5. 今度は revert です。main は後ろに下がらず、打ち消しのコミットを足して 前に進みます。ファイルの中身は、reset のときと同じ「パンを追加」の時点になりました。

    $ git revert HEAD
    [main c8b9add] Revert "アイスを追加"
     1 file changed, 1 deletion(-)
  6. ファイルの結果は同じでも、歴史は違います。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 する

  1. revert は直前のコミットに限りません。ここでは、2つ前の「日曜に変更」だけを取り消します。そのあとの2つのコミットは残したままにします。

  2. HEAD~2 が「日曜に変更」です。

    $ git log --oneline
    71d9b3e (HEAD -> main) バターを追加
    e5c0a62 パンを追加
    c41e7a2 日曜に変更
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加
  3. 「日曜に変更」が変えたのは notes.txt だけです。あとの「パンを追加」「バターを追加」は todo.txt しか変えていません。

    $ git show --stat HEAD~2
    commit c41e7a2
    
        日曜に変更
    
     notes.txt | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
  4. notes.txt の変更だけを打ち消すコミットが、いちばん上に足されました。あとのコミットが同じファイルに触れていないので、すんなり打ち消せます。

    $ git revert HEAD~2
    [main 571d9ae] Revert "日曜に変更"
     1 file changed, 1 insertion(+), 1 deletion(-)
  5. notes.txt は「土曜に行く」に戻りました。

    $ cat notes.txt
    土曜に行く
  6. todo.txt は「バターを買う」まで残っています。取り消したのは、指定したコミットの変更だけ です。reset で取り消そうとすると「日曜に変更」より前の HEAD~3 まで戻すことになり、パンとバターも一緒に外れてしまいます。

    $ cat todo.txt
    牛乳を買う
    卵を買う
    パンを買う
    バターを買う

この場面がすんなり通ったのは、あとのコミットが notes.txt に触れていなかったからです。あとのコミットが同じファイルの近くの行を変えていると、打ち消しがぶつかって途中で止まることがあります(コンフリクト)。ぶつかったときの直し方は、第3部の merge の回で扱います。

4. 書きかけの変更があると止まる

revert は新しいコミットを作るので、作業ツリーとステージを書き換えます。そのため、コミットしていない変更とぶつかると、何もせずに止まります。checkout の回で見た「書きかけを勝手に消さない」のと同じ考え方です。

書きかけの変更があると revert は止まる

  1. todo.txt にコミットしていない1行を書いたまま、「アイスを追加」を revert してみます。

    $ echo "チーズを買う" >> todo.txt
  2. 止まりました。 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
  3. 書きかけはそのまま残っています。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")
  4. ここでは試しに書いた1行なので、git restore で捨てます。残したいときは、次の回の git stash で退避します。

    $ git restore todo.txt
  5. 作業ツリーがきれいになったので、今度は 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