git commitを取り消す方法 - push前/push後で変わる正しい手順
git commitの取り消し方は、そのコミットをまだpushしていないか、すでに共有したかで変わります。ローカルだけの履歴なら git reset、共有済みの履歴なら原則として git revert を使います。
The safe way to undo a Git commit depends on whether it has been pushed. Reset is suitable for private local history, while revert adds a new inverse commit without rewriting shared history.
1. まずpush前かpush後かを確認する
取り消す前に git status と git log --oneline --decorate -5 で対象を確認します。変更を残す場合と変更自体を捨てる場合では選ぶコマンドが異なります。共有済みか不明なら、履歴を書き換えないrevertが安全側です。
2. push前ならgit resetで直前のコミットを戻す
# 変更をステージ済みで残す
git reset --soft HEAD~1
# 変更を未ステージで残す(--mixedはデフォルト)
git reset --mixed HEAD~1
git reset HEAD~1--soft はコミットを取り消しても変更をステージ済み(staged)のまま残します。--mixed は変更をワーキングディレクトリへ未ステージ(unstaged)で戻します。mixedは git reset のデフォルトです。自分のローカルにしかない履歴ならresetは安全です。
3. resetとrevertの違いを比較する
| 操作 | HEAD | ステージ | 作業ツリー | 用途 |
|---|---|---|---|---|
reset --soft | 移動 | 残す | 残す | commitの作り直し |
reset --mixed | 移動 | 解除 | 残す | ファイルの選び直し |
reset --hard | 移動 | 戻す | 戻す | 変更も破棄 |
revert | 新規commitへ進む | 完了後clean | 打ち消す | 共有済み履歴 |
resetはブランチの参照を過去へ移し、revertは元コミットを残して逆向きの差分を記録します。
4. --hardは変更内容そのものを破棄する
git status
git diff
git reset --hard HEAD~1--hard はコミットだけでなく変更内容そのものを破棄します。データ消失のリスクがあるため、変更が不要だと確認できた場合以外は実行しないでください。 変更を残すならsoftかmixedを使います。
5. push後・共有ブランチではgit revertを使う
git log --oneline
git revert <commit>git revert は対象を打ち消す内容の新しいコミットを追加します。既存履歴を書き換えないため、push済みの共有ブランチではrevertが基本です。共有履歴をresetしてforce pushすると、他の人がpull済みの場合に履歴が食い違い、混乱やコンフリクトを招きます。
競合したら修正してadd後に git revert --continue、中止は git revert --abort です。
6. 直前より古いコミットを取り消す
HEAD~3 は第一親を3回たどったコミットです。変更を残すなら git reset --mixed HEAD~3、共有履歴の特定コミットなら git revert a1b2c3d とします。logで確認したハッシュなら対象を明示できます。
連続範囲は git revert --no-commit HEAD~3..HEAD で反映し、確認後にcommitできます。左端を含まず右端を含みます。merge commitのrevertでは -m が必要なので、先にshowで親を確認します。
7. amendは直前のコミットを修正する
git add forgotten-file.ts
git commit --amend
git commit --amend -m "正しいメッセージ"git commit --amend は直前のコミットメッセージやファイルの追加漏れを修正します。revertやresetとは目的が異なります。amendも新しいハッシュを作るため、共有済みコミットには使わないのが原則です。
8. 実例:本番設定を誤ってpushした場合
本番設定をpushしたら、まず秘密情報を無効化・ローテーションします。その後 git revert a1b2c3d で打ち消し、ファイルを残すなら git rm --cached config/production.env で追跡から外し、.gitignoreへ追加してcommit・pushします。
# 1. 管理画面などで秘密情報を無効化する
# 2. 共有済みコミットを打ち消す
git revert a1b2c3d
# 3. ローカルファイルは残して追跡から外す
git rm --cached config/production.env
# .gitignoreにconfig/production.envを追加
git add .gitignore
git commit -m "Stop tracking production configuration"
git push origin mainrevert後も秘密情報は過去の履歴に存在します。完全除去はホスティング事業者の手順とチーム合意に従います。ローテーションは省略できません。
9. git reflogでreset後のコミットを探す
git reflog --date=local はHEADやブランチ参照の移動を表示します。hard reset直後なら移動前のハッシュをshowで確認し、git branch recovery/a1b2c3d a1b2c3d で救出用ブランチを作れる可能性があります。
git reflog --date=local
# a1b2c3d HEAD@{1}: commit: add payment validation
git show a1b2c3d
git branch recovery/a1b2c3d a1b2c3d
git switch recovery/a1b2c3dreflogは主にローカルの記録です。コミットも直ちに消えるとは限りませんが、reflogの期限切れやガベージコレクション後は復元できない場合があります。未コミット変更も必ず戻せるわけではありません。
10. 共有履歴の書き換えとGUI操作
git push --force-with-lease origin <branch>チームで合意して履歴を書き換える場合は、単純な --force ではなく --force-with-lease を使います。リモートが自分の想定と異なる状態なら失敗するため、他者の更新を意図せず上書きする危険を減らせます。ただし履歴を書き換える事実は変わりません。
VSCodeのSource Controlパネルにある「Undo Last Commit」は、変更をステージ済みで残すsoft resetと同等です。push後の共有コミット向けではなく、ローカル履歴に使います。
GitHub Desktopなどにも同種の操作があります。名称やステージ状態は製品・バージョンで異なるため、実行後はstatusとlogを確認してください。
よくある間違い
- --hardでも変更が残ると思う: 追跡対象の変更が破棄されます。
- push後もresetする: 共有ブランチではrevertを使います。
- --force-with-leaseなら常に安全と思う: チーム合意は必要です。
- amendを取り消し専用と思う: 直前のcommitを置き換えます。
- HEAD~3の範囲を確認しない: logで対象を確認します。
- revertで元commitが消えると思う: 元の履歴は残ります。
- 秘密情報をGitから消せば安全と思う: ローテーションが最優先です。
- reflogを永続バックアップと思う: 保持期限やGCに依存します。
English summary
Before pushing, use soft reset to keep changes staged or mixed reset to keep them unstaged. Hard reset discards tracked changes. Use HEAD~3 or a verified hash for older commits.
After sharing, prefer revert. Reflog may locate a commit after an accidental reset, but retention and garbage collection mean it is not a permanent backup. Rotate exposed secrets before cleaning history.