Reflog and Recovery
The reflog is git’s safety net. Every time HEAD or a branch pointer moves, git records it locally. Even after a “destructive” operation (hard reset, rebase, force-push, branch delete), the commits are still reachable for ~90 days via reflog.
If you remember nothing else: before panicking about lost commits, run git reflog.
What the reflog records
git reflog
Output:
a1b2c3d HEAD@{0}: reset: moving to HEAD~3
e4f5g6h HEAD@{1}: commit: Add user serializer
i7j8k9l HEAD@{2}: commit: Add user model
m0n1o2p HEAD@{3}: pull: Fast-forward
q3r4s5t HEAD@{4}: checkout: moving from main to feature
Every operation that moved HEAD is logged with:
- The commit SHA HEAD pointed to.
- A reference name
HEAD@{N}(N = how many moves ago). - What kind of operation (commit, reset, checkout, rebase, etc.).
- Sometimes a message.
Per-branch reflog
git reflog show feature # reflog for the feature branch
git reflog show main # reflog for main
Each branch has its own reflog. Useful when “the branch was at X yesterday, what is it now?”
Recovering lost commits
Scenario: hard reset went too far
git reset --hard HEAD~5 # oops, lost 5 commits
git reflog # find the pre-reset SHA
# e4f5g6h HEAD@{1}: commit: ...
git reset --hard e4f5g6h # restore
Or:
git reset --hard HEAD@{1} # one move ago
Scenario: deleted a branch
git branch -D feature # gone — or is it?
git reflog --all # search all reflogs
# e4f5g6h refs/heads/feature@{0}: commit: Add ...
git checkout -b feature e4f5g6h # recreate the branch at that SHA
If the branch’s reflog is gone, search all reflogs:
git reflog --all | grep "<keyword from commit message>"
Scenario: rebase went wrong, branch is wrecked
git rebase main # interactive rebase, finished badly
git reflog
# d4e5f6g HEAD@{0}: rebase finished: returning to refs/heads/feature
# c1b2a3z HEAD@{8}: rebase: ...
# b9d8e7f HEAD@{9}: rebase: checkout main
# a1b2c3d HEAD@{10}: commit: original commit before rebase ← here
git reset --hard a1b2c3d # back to pre-rebase state
Or use the named alias:
git reset --hard ORIG_HEAD # git records this before rebase/merge/reset
ORIG_HEAD is set automatically before destructive operations — a quick way back without searching the reflog.
Scenario: force push wiped the remote
The remote is gone, but your local clone still has the commits. Push them back:
git push --force-with-lease origin main
If your local was also reset, find the SHA via reflog and reset there first.
If you have no local copy of the commits (no one has them), they’re truly gone — git’s distributed nature means there’s no central recovery.
Scenario: stash got dropped
git stash drop (or git stash pop on a stash that conflicted and you discarded) leaves orphaned commits.
git fsck --no-reflog | grep "dangling commit"
# dangling commit a1b2c3d4...
git show a1b2c3d4 # is this it?
git stash apply a1b2c3d4 # apply if so
What’s NOT in the reflog
- Untracked files. Reflog tracks commits, not files.
git clean -fdwipes untracked files with no recovery. - Working-tree changes never committed. If you lose unstaged changes, only your editor’s undo or filesystem snapshots can save you.
- Operations on a different machine. Reflog is local. A coworker’s reset won’t appear in your reflog.
Reflog expiration
Default policy:
- Reachable commits: kept indefinitely.
- Unreachable commits: kept for 90 days (
gc.reflogExpireUnreachable). - Reflog entries themselves: kept for 90 days (
gc.reflogExpire).
Tweakable per repo:
git config gc.reflogExpire 365 # keep reflog entries for 1 year
git config gc.reflogExpireUnreachable 90 # keep unreachable commits for 90 days
git gc (garbage collection) eventually removes the orphaned commits. By default it runs automatically when there are too many loose objects.
git fsck — find dangling objects
git fsck --lost-found # report all dangling objects, save to .git/lost-found/
git fsck --no-reflog # ignore reflog when checking reachability
Useful for true forensics — recovering from a gc that ran before you noticed the loss.
ORIG_HEAD, FETCH_HEAD, MERGE_HEAD
Special refs git sets automatically:
| Ref | Meaning |
|---|---|
ORIG_HEAD |
where HEAD was before the last rebase/merge/reset (your “undo” target) |
FETCH_HEAD |
where the last git fetch ended up |
MERGE_HEAD |
the other parent of an in-progress merge (only set during merge) |
HEAD@{N} |
reflog reference; N moves ago |
HEAD@{2.hours.ago} |
reflog reference by time |
branch@{upstream} (or @{u}) |
the upstream branch this tracks |
git diff @{u} = “show difference between my branch and its upstream.”
Stash recovery
Stashes are commits in disguise. Listed:
git stash list
# stash@{0}: WIP on feature: a1b2c3d Add user model
Even after git stash drop stash@{0}, the underlying commit lives in object storage until garbage collection. Recover via git fsck --no-reflog | grep dangling.
Practical workflow
When you panic:
- Don’t run more git commands. Especially not
git gc. git reflog— find the SHA you want.git reset --hard <sha>orgit checkout -b recovery <sha>to retrieve it.- If reflog doesn’t have it,
git fsck --lost-foundfor dangling objects.
Common interview confusions
- “Git permanently deletes commits with
--hard.” — no, they’re orphaned but kept ~90 days. Reflog finds them. - “Reflog is shared across machines.” — purely local. Coworker’s reflog is on their machine.
- “Force-push deletes commits forever.” — only on the remote. Local clones still have them; you can push them back if you act before they GC.
- “There’s no way to recover a dropped stash.” —
git fsck --no-reflog | grep danglingandgit stash apply <sha>usually finds it.
Interview angle
- “What’s the reflog and when do you use it?” — local log of every HEAD/branch movement. Use it to recover from destructive operations: hard reset, bad rebase, deleted branch, dropped stash.
- “You hard-reset and lost 3 commits. How do you recover?” —
git reflogto find the pre-reset SHA,git reset --hard <sha>. Orgit reset --hard ORIG_HEADif it’s the operation you just did. - “How long does the reflog keep dropped commits?” — ~90 days for unreachable commits, then
git gcmay collect them. Configurable viagc.reflogExpireUnreachable. - “You force-pushed over a teammate’s work — can they recover?” — yes, if their local clone still has the commits (their reflog has them). They can push back. If only the remote had them, they’re gone.
- “What’s
ORIG_HEAD?” — git automatically sets it before destructive operations (rebase, merge, reset). Quick way back without searching the reflog. - “How do you find a dropped stash?” —
git stash list(might still be there), orgit fsck --no-reflog | grep danglingand inspect each dangling commit.