Git reset --hard で消したコミットを復元できない
git reset --hard で消えたように見えるコミットも、ガベージコレクションが走る前なら git reflog の SHA から git reset --hard <sha> や git branch <name> <sha> で完全に復旧できる。
#reset#reflog#recovery#hard
公開:
要約
git reset --hard で巻き戻したコミットは 履歴から外れただけで即削除されてはいない。
HEAD やブランチが動いた履歴は git reflog にローカル記録されており、その SHA を git reset --hard <sha> に渡せば完全復元できる。
gc 後で reflog から消えた場合でも、git fsck --lost-found でダングリング状態の commit/blob を回収できる。
よくある原因
- 巻き戻し前の SHA を控えていない:
git logは HEAD から到達できるコミットしか出さないため、reset --hard後は普通に検索しても見えない - reflog の存在を知らない: ローカル専用の参照履歴。
clone / push では共有されないため初学者は気付きにくい - gc 後の救出を試みている: 参照を失ったオブジェクトは
git gcの対象になり、既定でおよそ 90 日後に整理される - stash を
--hardで消した:git stashで退避したものを reset で巻き込んだケース。
通常の reflog ではなくgit stash list経由の参照が必要
解決策
1. reflog から SHA を特定して戻す
git reflog
# 9f2a1b3 HEAD@{0}: reset: moving to HEAD~3
# 7c4d8ef HEAD@{1}: commit: WIP feature X
# 1b9e2a0 HEAD@{2}: commit: refactor parser戻したい時点の SHA を見つけたら、いずれかで戻す。
git reset --hard 7c4d8ef # 現在のブランチをそこまで進める
# あるいは破壊せず別名で確保
git branch recover-feature 7c4d8efgit 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-pick か git branch で取り込む。
詳しい挙動は git fsck 公式 を参照。
4. 二度と消さないための予防
- 危険な操作の前に
git tag backup/before-resetでタグ退避する - 共有ブランチには
git revertを使い、--hardはローカル限定に留める
git reset の --soft / --mixed / --hard の違いは 公式リファレンス を参照。