できない.dev

Git reset --hard で消したコミットを復元できない

git reset --hard で消えたように見えるコミットも、ガベージコレクションが走る前なら git reflog の SHA から git reset --hard <sha> や git branch <name> <sha> で完全に復旧できる。

公開: 更新:

実行例あり(2026-08-02 に実環境で検証)

要約

git reset --hard で巻き戻したコミットは 履歴から外れただけで即削除されてはいない
HEAD やブランチが動いた履歴は git reflog にローカル記録されており、その SHA を git reset --hard <sha> に渡せば完全復元できる。
gc 後で reflog から消えた場合でも、git fsck --lost-found でダングリング状態の commit/blob を回収できる。

よくある原因

  1. 巻き戻し前の SHA を控えていない: git log は HEAD から到達できるコミットしか出さないため、reset --hard 後は普通に検索しても見えない
  2. reflog の存在を知らない: ローカル専用の参照履歴。
    clone / push では共有されないため初学者は気付きにくい
  3. gc 後の救出を試みている: 参照を失ったオブジェクトは git gc の対象になり、既定でおよそ 90 日後に整理される
  4. stash を --hard で消した: git stash で退避したものを reset で巻き込んだケース。
    通常の reflog ではなく git stash list 経由の参照が必要

解決策

1. reflog から SHA を特定して戻す

git reflog
# 9f2a1b3 HEAD@{0}: reset: moving to HEAD~2
# 7c4d8ef HEAD@{1}: commit: WIP feature X
# 1b9e2a0 HEAD@{2}: commit: refactor parser

戻したい時点の SHA を見つけたら、いずれかで戻す。

git reset --hard 7c4d8ef         # 現在のブランチをそこまで進める
# あるいは破壊せず別名で確保
git branch recover-feature 7c4d8ef

git reflog 公式ドキュメント(新しいタブで開く) のとおり、ブランチごとの reflog は git reflog show <branch> で個別に確認できる。

2. stash も reflog で探す

git stash list
# 一覧が空でも参照だけ残っている場合
git fsck --no-reflog | grep commit
git stash apply <sha>

3. gc 後で reflog から消えたとき

git fsck --lost-found
# Checking object directories: 100% (256/256), done.
# dangling commit 7c4d8ef...

.git/lost-found/commit/ 配下にダングリングコミットの SHA が並ぶので、git show <sha> で内容を確認し git cherry-pickgit branch で取り込む。
詳しい挙動は git fsck 公式(新しいタブで開く) を参照。

4. 二度と消さないための予防

  • 危険な操作の前に git tag backup/before-reset でタグ退避する
  • 共有ブランチには git revert を使い、--hard はローカル限定に留める

git reset--soft / --mixed / --hard の違いは 公式リファレンス(新しいタブで開く) を参照。

実行例

実際に alpine/git 環境で上記の手順を走らせると、git reset --hard HEAD~2 で C2・C3 が git log から消えた後も、git reflog には両コミットの SHA が残り、git reset --hard HEAD@{1} によって終了コード 0 で完全に復元されることを確認できる。

$ git log --oneline
93d8e95 C3 refactor parser
fde5b97 C2 WIP feature
fa8a68d C1 init
$ git reset --hard HEAD~2
HEAD is now at fa8a68d C1 init
$ git log --oneline
fa8a68d C1 init
$ git reflog
fa8a68d HEAD@{0}: reset: moving to HEAD~2
93d8e95 HEAD@{1}: commit: C3 refactor parser
fde5b97 HEAD@{2}: commit: C2 WIP feature
fa8a68d HEAD@{3}: commit (initial): C1 init
$ git reset --hard HEAD@{1}
HEAD is now at 93d8e95 C3 refactor parser
reset の終了コード: 0
$ git log --oneline
93d8e95 C3 refactor parser
fde5b97 C2 WIP feature
fa8a68d C1 init

— 2026-08-02 時点の出力

検証環境

検証日
実行環境
alpine/git

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

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