Git reflog Recovery Tutorial
Use git reflog to recover lost commits, branches, and stashes after rebases, resets, and bad merges. A practical walkthrough of how Git remembers where HEAD has been.
What you'll learn
- ✓What the reflog actually records and where
- ✓How to recover from reset --hard and bad rebases
- ✓Restoring a branch you deleted by accident
- ✓Finding lost stashes and detached commits
- ✓How reflog expiry and gc can eat your safety net
Prerequisites
- •Basic Git: commit, branch, checkout, reset, rebase
What and Why
Almost every “I just lost two days of work” Git story ends with somebody discovering git reflog. It is a local log of every move HEAD and your branches have made: every commit, checkout, reset, rebase, merge, and stash. Even when commits are no longer reachable from any branch, they exist in the object database and the reflog still points at them.
If you have not run git gc and the entries have not expired, your work is almost certainly recoverable. You just need to know how to ask.
Mental Model
The reflog is not part of the public history. It is a per-repository, per-ref log stored under .git/logs/. There is one for HEAD (.git/logs/HEAD) and one per branch (.git/logs/refs/heads/<branch>).
Each entry records: the previous SHA, the new SHA, who made the change, when, and a one-line description of the action. Time-based references like HEAD@{2} mean “the value of HEAD two moves ago”; HEAD@{yesterday} works too.
Important: the reflog is local. Cloning, pushing, and pulling do not propagate it. If you nuke a branch and re-clone, the reflog is gone.
Hands-on Example
Imagine you just ran git reset --hard HEAD~3 on a branch and your three commits seem to be gone.
$ git reflog
2f1c0aa HEAD@{0}: reset: moving to HEAD~3
9b7d4e2 HEAD@{1}: commit: fix off-by-one in pagination
1a3cf80 HEAD@{2}: commit: add cache headers
7e22f51 HEAD@{3}: commit: extract pagination helper
3d8aa90 HEAD@{4}: checkout: moving from main to feature/cache
The three commits you wanted are HEAD@{1} through HEAD@{3}. Bring them back:
git reset --hard HEAD@{1}
Now your branch tip is the original commit. Same idea for a botched rebase:
git reflog
# find the line ending in "rebase (start): checkout ..."
git reset --hard HEAD@{8} # one step before the rebase started
Deleted a branch? The branch ref is gone, but the commits are not. Find the tip from HEAD’s reflog or the deleted branch’s log file if it still exists, then recreate the branch:
git reflog | grep 'feature/payments'
git branch feature/payments 9b7d4e2
Lost a stash you cleared? Stashes live as commits referenced by refs/stash, and that ref has its own reflog:
git fsck --no-reflogs --unreachable | grep commit
git stash apply <sha>
time ->
work, work, work
C1 -- C2 -- C3 on feature
^ HEAD@{3}
git reset --hard HEAD~2
C1 branch tip
^ HEAD@{0}
reflog:
HEAD@{0} reset: ... -> C1
HEAD@{1} commit: ... -> C3 <- still in object db
HEAD@{2} commit: ... -> C2
HEAD@{3} commit: ... -> C1
recovery:
git reset --hard HEAD@{1} branch tip jumps back to C3 Common Pitfalls
- Running
git gc --prune=now: deletes unreachable objects immediately, including everything your reflog used to protect. Avoid it after a mistake until you have recovered. - Re-cloning: throws away the local reflog. If something is broken, do recovery on the existing clone first.
- Confusing
HEAD@{n}withHEAD~n: the first is “n moves ago in time,” the second is “n commits back in graph”. They are completely different. - Expecting reflog on shared remotes: GitHub and friends do not expose your collaborators’ reflogs. Push tags or branches to share recovery points.
- Reflog expiry: by default, reachable entries expire after 90 days and unreachable after 30. Long-dormant disasters may be unrecoverable.
Practical Tips
- Before any risky operation, note the current SHA:
git rev-parse HEADand paste it into a scratch file. - Use
git reflog show <branch>to see only that branch’s history of moves. git log -gwalks the reflog like a normal log, with diff support.- Tweak retention with
gc.reflogExpireandgc.reflogExpireUnreachableif you want a longer safety net. - Treat
git reset --hardandgit rebaselike surgery: focused, deliberate, and reversible because of the reflog.
Wrap-up
The reflog is Git’s local undo journal. As long as you act before gc cleans up, almost any mistake involving commits is reversible. Memorize three commands: git reflog, git reset --hard HEAD@{n}, and git branch <name> <sha>. They will save you more times than you can count.
Related articles
- Git Git Cherry-pick and Revert Tutorial
Learn how to copy specific commits across branches with cherry-pick and how to safely undo merged changes with revert, including conflict handling and recovery.
- Git Git Large File Storage (LFS) Tutorial
Set up Git LFS to version large binaries like images, models, and datasets without bloating your repository, including tracking, migration, and CI tips.
- Git Git Rebase vs Merge: When to Use Which
A clear, practical guide to choosing between git rebase and git merge, with safe workflows for feature branches, shared branches, and pull requests.
- Git Git Stash Tutorial: Saving Work in Progress
Learn how to use git stash to safely shelve uncommitted changes, switch contexts, and recover work using push, pop, apply, and branch workflows.