できない.dev

Git でコミットを取り消せない

「直前のコミットを消したい」と「push 済みのコミットを戻したい」では使うコマンドが違う。
reset と revert を取り違えると共有ブランチの履歴が壊れる。

公開: 更新:

要約

「コミットを取り消す」には大きく reset(履歴を巻き戻す)と revert(打ち消しコミットを作る)の 2 系統がある。
push 済みなら revert、ローカルだけなら reset を使うのが原則。

よくある原因

  1. reset と revert の混同: どちらも「取り消し」と呼ばれるが、履歴に与える影響が真逆
  2. push 済みに reset --hard: 後で push --force が必要になり、共有ブランチで事故る
  3. --soft / --mixed / --hard の選択ミス: 変更を残したいのに --hard で全消し
  4. reflog を知らない: --hard で消えたと思い込み、復旧できると気付かない

解決策

1. ローカルだけの直前コミットを取り消す

git reset --soft HEAD~1   # 変更はステージに戻る
git reset --mixed HEAD~1  # 変更は作業ツリーに戻る(既定)
git reset --hard HEAD~1   # 変更ごと完全消去

--soft を選べばすぐ作り直せる。

2. 共有済みのコミットを打ち消す

git revert <sha>
git push origin <branch>

revert は 新しい打ち消しコミットを作る ため、履歴を破壊しない。
プルリクのレビュー後マージしたコミットを戻す時もこれ。

3. 完全に消したい(ローカル限定)

git reset --hard <safe-sha>

push 済みのブランチで実行すると 共同作業者の履歴が壊れる
共有ブランチでは行わない。

4. 消したコミットを救出する

git reflog
# 各操作の HEAD 位置が表示される
git reset --hard <sha-from-reflog>

--hard で消えたコミットも、ガベージコレクションが走る前なら reflog から復元できる。

実行例

実際に上記の手順を alpine/git(Git 2.52.0)で動かすと、git reset --soft HEAD~1 の終了コード 0 を確認でき、変更がステージに残った状態でコミットし直した後、git revert によって打ち消しコミット(1e30369)が正常に作成された。

$ git log --oneline
7f09b4b feat: v2 (取り消したい)
1eb839b feat: v1
$ git reset --soft HEAD~1
reset の終了コード: 0
$ git log --oneline
1eb839b feat: v1
$ git status --short
M  app.txt
$ git commit -m "feat: v2 (やり直し)"
[main 1f7d8e1] feat: v2 (やり直し)
 1 file changed, 1 insertion(+)
$ git log --oneline
1f7d8e1 feat: v2 (やり直し)
1eb839b feat: v1
$ git revert --no-edit HEAD
[main 22bef47] Revert "feat: v2 (やり直し)"
 Date: Sun Aug 2 08:42:41 2026 +0000
 1 file changed, 1 deletion(-)
revert の終了コード: 0
$ git log --oneline
22bef47 Revert "feat: v2 (やり直し)"
1f7d8e1 feat: v2 (やり直し)
1eb839b feat: v1
$ git reflog -n 6
22bef47 HEAD@{0}: revert: Revert "feat: v2 (やり直し)"
1f7d8e1 HEAD@{1}: commit: feat: v2 (やり直し)
1eb839b HEAD@{2}: reset: moving to HEAD~1
7f09b4b HEAD@{3}: commit: feat: v2 (取り消したい)
1eb839b HEAD@{4}: commit (initial): feat: v1

— 2026-08-02 時点の出力

この記事は役立ちましたか?