できない.dev

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

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

#commit#reset#revert#undo

公開: 更新:

要約

「コミットを取り消す」には大きく 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)が正常に作成された。

== versions ==
git version 2.52.0
== run ==
--- 現在の履歴 ---
5830a40 feat: v2 (取り消したい)
43b11da feat: v1
--- 解決1: ローカルのみ → git reset --soft HEAD~1(変更はステージに残る)---
reset の終了コード: 0
log:
43b11da feat: v1
status(変更はステージに残っている):
M  app.txt
--- 取り消した変更でコミットし直す ---
049d64e feat: v2 (やり直し)
43b11da feat: v1
--- 解決2: 共有済み想定 → git revert で打ち消しコミットを作る ---
ain 1e30369] Revert "feat: v2 (やり直し)"
 Date: Sat Jun 13 02:39:26 2026 +0000
 1 file changed, 1 deletion(-)
revert の終了コード: 0
1e30369 Revert "feat: v2 (やり直し)"
049d64e feat: v2 (やり直し)
43b11da feat: v1
--- 解決3: git reflog で消したコミットを sha 経由で救出できることを確認 ---
1e30369 HEAD@{0}: revert: Revert "feat: v2 (やり直し)"
049d64e HEAD@{1}: commit: feat: v2 (やり直し)
43b11da HEAD@{2}: reset: moving to HEAD~1
5830a40 HEAD@{3}: commit: feat: v2 (取り消したい)
43b11da HEAD@{4}: commit (initial): feat: v1

— 2026-06-13 時点の出力

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