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 reflogOutput:
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 featureEvery 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 mainEach 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
# oops, lost 5 commits
git reset --hard HEAD~5
# find the pre-reset SHA
git reflog
# e4f5g6h HEAD@{1}: commit: ...
git reset --hard e4f5g6h # restoreOr:
git reset --hard HEAD@{1} # one move agoScenario: deleted a branch
git branch -D feature # gone — or is it?
# search all reflogs
git reflog --all
# e4f5g6h refs/heads/feature@{0}: commit: Add ...
# recreate the branch at that SHA
git checkout -b feature e4f5g6hIf the branch’s reflog is gone, search all reflogs:
git reflog --all | grep "<keyword from commit message>"Scenario: rebase went wrong, branch is wrecked
# interactive rebase, finished badly
git rebase main
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
# back to pre-rebase state
git reset --hard a1b2c3dOr use the named alias:
# git records this before rebase/merge/reset
git reset --hard ORIG_HEADORIG_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 mainIf 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 soWhat’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:
# keep reflog entries for 1 year
git config gc.reflogExpire 365
git config gc.reflogExpireUnreachable 90 # keep unreachable commits for 90 daysgit 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 reachabilityUseful 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 modelEven 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 6
- “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.