できない.dev

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 を回収できる。

よくある原因

  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~3
# 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 の違いは 公式リファレンス を参照。

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