Reflog
One-liner¶
Git reflog records every position HEAD has been at, letting you recover from almost any destructive operation.
Why it matters¶
When you run git reset --hard, delete a branch, or mess up a rebase, git log no longer shows the lost commits. They seem gone. But git quietly recorded every move in the reflog. It's your undo history — a safety net that proves almost nothing in git is truly permanent.
Core concept¶
Imagine your browser history. Even if you close all tabs and clear your bookmarks, the browser history still shows every page you visited. You can go back to any of them. The reflog is git's browser history — every time HEAD moves (commit, checkout, reset, rebase, merge, pull), git notes where it was before and after.
The key insight: "destructive" operations like reset --hard don't delete commits. They move pointers. The old commits remain in the object database, and the reflog keeps references to them for at least 30 days.
How it works¶
- Every HEAD movement is recorded — commits, checkouts, resets, rebases, merges all create reflog entries.
- Entries have indices —
HEAD@{0}is current,HEAD@{1}is the previous position, etc. - Each entry stores the SHA — you can always get back to any recorded state.
- Entries expire — 90 days for reachable commits, 30 days for unreachable ones (configurable).
- Recovery — find the desired SHA in the reflog, then
git reset --hard <sha>orgit branch <name> <sha>.
Key terms¶
- Reflog (reference log) — per-ref chronological record of pointer movements
- HEAD reflog — records every time HEAD changes (most commonly used)
- Branch reflog — records movements of a specific branch pointer (
git reflog show main) HEAD@{N}— syntax to reference the Nth previous HEAD position- Unreachable commit — a commit no branch/tag points to; still exists in object database until
gcruns
Common misconceptions¶
- "git reset --hard permanently destroys commits" — It only moves the branch pointer. The commits still exist and are recoverable via reflog for 30+ days.
- "git gc immediately deletes unreachable objects" — By default,
gcpreserves unreachable objects for 30 days. Onlygit gc --prune=nowis immediately destructive. - "The reflog is shared across clones" — It's local only. Each clone has its own reflog. Pushing to a remote doesn't push reflog entries.
When to use / not use¶
✓ Use when: you accidentally reset past important commits, deleted a branch you still need, botched a rebase, or need to audit what operations were performed on a branch.
✗ Don't use when: the commits were lost more than 30 days ago (reflog entries expired), the loss involves untracked files (git clean — truly gone), or you need to recover work from another machine (reflog is local).
Example¶
# See recent HEAD movements
git reflog --oneline -10
# Recover from a bad reset
git reset --hard HEAD@{2}
# Restore a deleted branch
git reflog | grep "checkout.*feature/auth"
git branch recovered-auth <sha-from-reflog>
Next steps¶
- Enable
git rererealongside reflog awareness — together they make rebases and conflict resolution fully recoverable - Set longer reflog expiry for critical repos:
git config gc.reflogExpire "180 days" - Learn
git fsck --unreachableto find objects that even the reflog doesn't reference