Gitの履歴を壊してしまった時の復旧テクニック5選

Gitの履歴を壊してしまった時の復旧テクニック5選

git reset --hardを実行した直後に「あ、消したらだめだった」と気づく経験は、エンジニアなら一度はある。Gitはデータを消しているように見えて、実はほとんどの場合は復旧できる。前職で後輩が1週間分のコミットを吹っ飛ばしたのを5分で復旧させたこともある。焦る前に知っておくべき復旧テクニックをまとめる。

1. git reflog で消えたコミットを探す

Gitの最強の安全網がreflog。HEADの移動履歴がすべて残っている。

git reflog

出力例:

a1b2c3d HEAD@{0}: reset: moving to HEAD~2
e4f5g6h HEAD@{1}: commit: feat: ユーザー認証を追加
i7j8k9l HEAD@{2}: commit: fix: バリデーションのバグ修正

HEAD@{1}が消したかったコミットより前の状態なら:

git checkout -b recovery e4f5g6h

新しいブランチとして復元できる。reflogのエントリはデフォルト90日間保持される。

2. git reset –hard で消したコミットを戻す

git reset --hard HEAD~3で3コミット前に戻してしまった場合。

# まずreflogで消えたコミットのハッシュを確認
git reflog

# 消す前の状態のハッシュにリセット
git reset --hard e4f5g6h

--hardで消したコミットも、GCが走るまではオブジェクトDBに残っている。reflogで見つけてそのままreset --hardで戻せる。

3. git stash で消えた変更を取り出す

git stash popでコンフリクトが起きて変更が消えた場合。

# stashの一覧を確認
git stash list

# 特定のstashの内容を確認
git stash show stash@{0} -p

# 取り出す
git stash apply stash@{0}

stash popは適用後にstashを削除するが、適用に失敗した場合はstashリストに残ったままになる。git stash listで確認すれば見つかることが多い。

4. git cherry-pick で特定コミットだけ救出する

ブランチを間違えてコミットしてしまった場合や、リベース後に特定コミットだけ必要な場合。

# 正しいブランチに切り替える
git checkout main

# 必要なコミットだけ持ってくる
git cherry-pick a1b2c3d

# 複数コミットをまとめて持ってくる
git cherry-pick a1b2c3d..e4f5g6h

cherry-pickはコミットの内容だけをコピーして新しいコミットとして作成する。元ブランチには影響しない。

5. git fsck で孤立したオブジェクトを探す

reflogにも残っていない場合の最終手段。

git fsck --lost-found

実行すると.git/lost-found/ディレクトリに孤立したオブジェクトが保存される。

# 孤立したコミットの一覧
git fsck --lost-found | grep "dangling commit"

# コミットの内容を確認
git show a1b2c3d

dangling blobはファイルの中身が単体で残っているもの。テキストファイルなら内容を直接読める。

git cat-file -p a1b2c3d

復旧できないケース

正直に言っておく。以下のケースは復旧が難しい:

  • git gc --prune=nowを実行後(オブジェクトが物理削除される)
  • reflogの保持期間(90日)を過ぎた場合
  • git clone --depth 1のシャロークローンで一部履歴が最初からない場合

予防策

復旧より予防が大事。

大きな操作の前に必ずバックアップブランチを作る:

git branch backup/before-rebase-$(date +%Y%m%d)

--force--hardは慎重に:

# 代わりに --force-with-lease を使う(リモートの変更を上書きするのを防ぐ)
git push --force-with-lease origin feature-branch

まとめ

Gitの復旧テクニックをまとめると:

状況 コマンド
reset –hard で戻しすぎた git refloggit reset --hard {hash}
間違ったブランチにコミット git cherry-pick {hash}
stash の内容が消えた git stash listgit stash apply
reflog にもない git fsck --lost-found

Gitはほとんどの場合、データを完全には消さない。焦らずgit reflogから始めれば、たいていの事故は5分以内に解決できる。