Meet git reflog

You run git reset --hard one commit too far. Or you git branch -D a branch you were sure was already merged, and it wasn’t. The commit history you just lost feels gone for good, git log doesn’t show it anymore.

It isn’t gone. Git records every place HEAD and your branches have pointed to, commits, resets, checkouts, rebases, branch deletions, all of it, in the reflog. As long as Git hasn’t garbage-collected the underlying objects (default grace period is 90 days), you can get your commit back.

How it works

  • git reflog (short for git reflog show HEAD) — lists every move HEAD has made, newest first
  • git reflog show <branch> — same thing for a specific branch ref
  • each line looks like <sha> HEAD@{n}: <action>: <description>, where HEAD@{n} is a relative reference you can use anywhere a commit is expected
  • once you’ve found the commit you want back: git reset --hard <sha> (or HEAD@{n}) to move a branch back to it, or git branch <name> <sha> to recreate a branch that pointed there
  • reflog entries expire after 90 days by default (gc.reflogExpire), and unreachable objects get pruned by git gc after 30 days (gc.pruneExpire), so this isn’t a forever-undo, but it comfortably covers “I did something dumb five minutes ago”

Example: recovering from a bad reset --hard

mkdir reflog-demo && cd reflog-demo
git init -b main
echo "v1" > notes.txt
git add notes.txt
git commit -m "Add notes"

echo "v2" > notes.txt
git add notes.txt
git commit -m "Update notes to v2"

echo "v3" > notes.txt
git add notes.txt
git commit -m "Update notes to v3"

git log --oneline
027eae4 Update notes to v3
f091e9f Update notes to v2
3849dbf Add notes

Now the “oops”: you meant to undo your uncommitted changes, but typed one ~1 too many and blew away a real commit.

git reset --hard HEAD~1
HEAD is now at f091e9f Update notes to v2
git log --oneline
f091e9f Update notes to v2
3849dbf Add notes

Update notes to v3 is gone from the log. Check the reflog:

git reflog
f091e9f HEAD@{0}: reset: moving to HEAD~1
027eae4 HEAD@{1}: commit: Update notes to v3
f091e9f HEAD@{2}: commit: Update notes to v2
3849dbf HEAD@{3}: commit (initial): Add notes

There it is, HEAD@{1}, right before the reset. Recover it:

git reset --hard HEAD@{1}
HEAD is now at 027eae4 Update notes to v3
git log --oneline
027eae4 Update notes to v3
f091e9f Update notes to v2
3849dbf Add notes

Back to normal, nothing lost.

Example: recovering a deleted branch

This time, a feature branch gets deleted before it was merged.

git checkout -b feature/export-csv
echo "export logic" > export.py
git add export.py
git commit -m "Add CSV export"

echo "more export logic" >> export.py
git add export.py
git commit -m "Handle empty rows in CSV export"

git log --oneline
ce52587 Handle empty rows in CSV export
fac30c7 Add CSV export
027eae4 Update notes to v3
f091e9f Update notes to v2
3849dbf Add notes

Switch back to main and force-delete the branch, assuming it was already merged:

git checkout main
git branch -D feature/export-csv
Deleted branch feature/export-csv (was ce52587).

Note: Git actually tells you the last commit sha right there in the deletion message. If you still have that message in your terminal scrollback, you don’t even need the reflog. But scrollback disappears, this doesn’t.

git branch -a
* main

The branch is gone. Its own reflog file went with it, but HEAD’s reflog still remembers every commit made while that branch was checked out:

git reflog
027eae4 HEAD@{0}: checkout: moving from feature/export-csv to main
ce52587 HEAD@{1}: commit: Handle empty rows in CSV export
fac30c7 HEAD@{2}: commit: Add CSV export
027eae4 HEAD@{3}: checkout: moving from main to feature/export-csv
...

ce52587 is the tip of the deleted branch. Recreate the branch pointing at it:

git branch feature/export-csv ce52587
git branch -a
* main
  feature/export-csv
git log --oneline feature/export-csv
ce52587 Handle empty rows in CSV export
fac30c7 Add CSV export
027eae4 Update notes to v3
f091e9f Update notes to v2
3849dbf Add notes

Fully back, both commits intact.

What reflog won’t save you from

  • It’s local, the reflog lives in .git/logs and was never pushed anywhere, so it doesn’t help a teammate who force-pushed over your commits on a shared branch. If the commit ever made it to your machine, though, your own reflog may still have it.
  • It expires. Don’t treat it as permanent history, it’s a safety net measured in weeks, not a backup strategy.
  • Fresh clones have no reflog history at all, it only accumulates from the moment a repository exists locally.

Conclusion

git reflog is the tool for the five-minutes-ago disaster: a reset gone too far, a branch deleted a little too early, a rebase that ended in the wrong place. git log shows you where you are; git reflog shows you everywhere you’ve been, and that’s usually enough to get back.