Reflog & Recovery

Course 3 · Ch 3
Reflog and Disaster Recovery
The safety net behind almost every "I think I just destroyed my work" moment — including a bad reset, rebase, or even a force-push

This chapter has been referenced as a safety net throughout the entire series — Course 1's reset --hard warning, Course 2's force-push danger, Course 3's own interactive rebase chapter. Now it gets full treatment: what the reflog actually is, and the step-by-step recovery process for the most common "I broke it" scenarios.

What the Reflog Actually Tracks

Building on Chapter 1's object model: every time a ref (HEAD, a branch) moves — a commit, a checkout, a reset, a rebase step — git records that movement in the reflog, a local, personal log of where your refs have recently pointed. It's not part of the object database itself and isn't shared when you push — purely a local safety net.

$ git reflog
9c8b7a6 HEAD@{0}: commit: Add validation logic
4f3e2d1 HEAD@{1}: reset: moving to HEAD~1
a1b2c3d HEAD@{2}: commit: Add login form HTML
f9e8d7c HEAD@{3}: rebase (start): checkout main

Each line is a snapshot of where HEAD pointed at that moment, most recent first. Crucially, this includes commits that no branch currently points to anymore — exactly the case after a hard reset or a rewritten rebase, where the "old" commits still technically exist in git's object database, just unreachable by any current branch or tag.

This is why a "destroyed" commit usually isn't actually gone
Course 1, Chapter 8 explained that even git reset --hard isn't always unrecoverable — the reflog is the literal mechanism why. The commit object itself (Chapter 1) still exists in .git/objects/ even when no ref points to it anymore; the reflog is how you find its hash again to point something back at it.

Recovery Scenario: Undoing a Bad Hard Reset

$ # Accidentally went too far back
$ git reset --hard HEAD~3
HEAD is now at f9e8d7c Some much older commit

$ # Find where you were before the reset
$ git reflog
9c8b7a6 HEAD@{1}: commit: Add validation logic ← this is what you lost
f9e8d7c HEAD@{0}: reset: moving to HEAD~3

$ # Reset forward again, to the commit you actually wanted
$ git reset --hard 9c8b7a6
HEAD is now at 9c8b7a6 Add validation logic

Recovery Scenario: A Rebase Went Badly Wrong

Interactive rebase (Chapter 2) can be aborted mid-way with --abort — but what if it already finished, and the result is wrong? The reflog still has the pre-rebase state.

$ git reflog
4f3e2d1 HEAD@{0}: rebase (finish): returning to refs/heads/feature
9c8b7a6 HEAD@{4}: rebase (start): checkout HEAD~4 ← look just before this line
b2c1d0e HEAD@{5}: commit: Last commit before the rebase started

$ # Point your branch back at the pre-rebase state
$ git reset --hard b2c1d0e

Recovery Scenario: Recovering After a Force-Push

Course 2, Chapter 9 covered this from the perspective of the person who got overwritten — usually, their own local copy is fine since it was never touched. But if you're the one trying to recover what you pushed over:

🔍 If you have a local clone from before the force-push
Your own reflog has the commits you overwrote — find them and push them back, exactly as in the hard-reset scenario above.
git reflog → git push --force-with-lease
🤝 If a teammate's commits were overwritten
Their own local copy almost certainly still has the commits — the reflog rescue happens on their machine, not yours, since the commits were never touched there.
Ask them to git push their unaffected local branch
⏰ If too much time has passed
The reflog has a default expiry (90 days for reachable, 30 for unreachable commits) and git's periodic garbage collection can eventually prune genuinely unreachable objects — recovery isn't guaranteed forever.
git gc --prune=now destroys this safety net — never run it as a "fix"
git gc and reflog expiry are the real limits of this safety net
The reflog isn't infinite — it expires, and manually running aggressive garbage collection deletes genuinely unreachable objects for good. If you're mid-recovery, avoid running git gc until you've finished — it exists to clean up storage, not to be run during a rescue.

Finding a Specific Lost Commit, Not Just "the previous state"

If you're not sure which reflog entry you need, search by commit message or content rather than scanning the whole log manually:

$ # Search reflog entries by message text
$ git reflog --grep="validation"

$ # Search ALL commit objects (not just reachable ones) by content
$ git fsck --unreachable | grep commit

git fsck --unreachable is the deeper fallback when even the reflog has expired or doesn't have what you need — it lists every commit object still physically present in .git/objects/, including ones with no ref or reflog entry pointing to them at all, as long as garbage collection hasn't pruned them yet.

Command Reference

CommandWhat it does
git reflogShows the local history of where HEAD/branches have pointed recently
git reflog --grep="text"Searches reflog entries by message content
git reset --hard <hash>Points your current branch back at a specific commit found via reflog
git fsck --unreachableLists commit objects with no current ref pointing to them, reflog or not
git gcGarbage collection — can permanently prune unreachable objects; avoid mid-recovery

Chapter 3 Quick Reference

  • The reflog is a local, personal log of where HEAD/branches have pointed — not pushed, not shared
  • Includes unreachable commits — exactly what a hard reset or rewritten rebase leaves behind, still physically present
  • Recovery pattern: git reflog → find the hash you want → git reset --hard <hash>
  • Force-push recovery: your own reflog if you have it; otherwise the original author's untouched local copy
  • The reflog isn't infinite — expires over time, and git gc can permanently prune unreachable objects
  • git fsck --unreachable — deeper fallback for commits the reflog itself no longer references
  • Never run git gc mid-recovery — it's the one command that can make this chapter's techniques stop working
  • Next chapter: submodules and subtrees — proper setup, contrasted with the broken-gitlink trap from earlier courses