Skip to content

Recover from a bad reset

  • troubleshooting

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).

git reflog shows every move of HEAD, newest first:

Terminal window
git reflog

Output looks like:

a1b2c3d HEAD@{0}: reset: moving to HEAD~3
9f8e7d6 HEAD@{1}: commit: feat: the work you thought was gone
5c4b3a2 HEAD@{2}: commit: earlier work
...

HEAD@{1} is where you were before the reset — that’s the commit to recover.

If you just want HEAD back where it was:

Terminal window
git reset --hard HEAD@{1}

If you’d rather keep the current branch as-is and lift the commit onto it:

Terminal window
git cherry-pick 9f8e7d6

Or create a rescue branch pointing at the lost tip, then merge/inspect at leisure:

Terminal window
git branch rescue 9f8e7d6

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:

Terminal window
git fsck --lost-found --no-reflogs

Each dangling commit listed is recoverable with git show <sha> / git branch rescue <sha>.

  • 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 a git 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=now will wipe the reflog immediately — do not run it while you’re trying to recover.