Recover from a bad reset
Git rarely throws commits away. Anything your HEAD has ever pointed at — including
the commit you “lost” to git reset --hard or a bad rebase — is kept in the reflog
for about 90 days (the gc.reflogExpire default).
Find the lost commit
Section titled “Find the lost commit”git reflog shows every move of HEAD, newest first:
git reflogOutput looks like:
a1b2c3d HEAD@{0}: reset: moving to HEAD~39f8e7d6 HEAD@{1}: commit: feat: the work you thought was gone5c4b3a2 HEAD@{2}: commit: earlier work...HEAD@{1} is where you were before the reset — that’s the commit to recover.
Restore it
Section titled “Restore it”If you just want HEAD back where it was:
git reset --hard HEAD@{1}If you’d rather keep the current branch as-is and lift the commit onto it:
git cherry-pick 9f8e7d6Or create a rescue branch pointing at the lost tip, then merge/inspect at leisure:
git branch rescue 9f8e7d6Find commits the reflog doesn’t show
Section titled “Find commits the reflog doesn’t show”If the reflog doesn’t cover what you need (e.g. a commit that was never on HEAD,
or it was garbage-collected), look for dangling commits — objects with no
reference left pointing at them:
git fsck --lost-found --no-reflogsEach dangling commit listed is recoverable with git show <sha> / git branch rescue <sha>.
Caveats
Section titled “Caveats”- The reflog is per-clone. A reset on your machine is not in your colleague’s
reflog. If the commit was pushed before the reset, the remote (or a colleague’s
clone) is also a recovery source —
git fetch origin && git log origin/<branch>. - After
gc.reflogExpire(90 days) and agit gc, unreferenced objects can be pruned. Don’t sit on a botched reset for three months. git reflog expire --expire=now --all && git gc --prune=nowwill wipe the reflog immediately — do not run it while you’re trying to recover.