4 minutes
Git Reflog: The Undo Button You Didn’t Know You Had
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 forgit reflog show HEAD) — lists every moveHEADhas made, newest firstgit reflog show <branch>— same thing for a specific branch ref- each line looks like
<sha> HEAD@{n}: <action>: <description>, whereHEAD@{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>(orHEAD@{n}) to move a branch back to it, orgit 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 bygit gcafter 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/logsand 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.