Git Reflog
Your safety net. Every HEAD movement is recorded — recover from almost anything.
Understanding Reflog
git reflog
The reflog records every time HEAD changes: commits, resets, rebases, checkouts, merges, cherry-picks. It is local only — never pushed, never shared. It is your undo history for git itself.
git reflog --date=relative
git reflog --date=iso
When reconstructing what happened during an incident, ISO timestamps let you correlate git operations with logs and chat history.
git reflog show feature/auth-module
Each branch has its own reflog. Use this to see only the operations that affected a particular branch, filtering out unrelated checkout noise.
Recovery — Bad Reset
git reset --hardgit reflog
Find the commit hash from before the reset (the line will say something like HEAD@{2}: commit: feat: add auth handler), then:
git reset --hard abc1234
This is why --hard is recoverable within the reflog window. The commits aren’t deleted — they’re just unreachable until you point HEAD back at them.
Recovery — Deleted Branch
git reflog | grep "feature/deleted-branch"
git branch feature/recovered abc1234
When you git branch -D feature/experiment and immediately regret it, the reflog still has the hash. The commits exist until garbage collection runs (default: 90 days for reachable, 30 days for unreachable).
Recovery — Bad Rebase
git reflog | grep "rebase"
Look for the entry just before the first rebase line — that’s your clean state.
git reset --hard ORIG_HEAD
Git sets ORIG_HEAD before operations that move HEAD dramatically (rebase, merge, reset). If you haven’t done anything else since the rebase, ORIG_HEAD points to exactly where you were.
git reflog
# Find: abc1234 HEAD@{5}: rebase (start): checkout main
# The entry at HEAD@{6} is your pre-rebase state
git reset --hard HEAD@{6}
Recovery — Bad Merge
git reset --hard HEAD~1
git revert -m 1 HEAD
| Reverting a merge commit is not the same as undoing it. If you later want to re-merge the same branch, you’ll need to revert the revert first. This is a common trap. |
Searching the Reflog
git log -g --diff-filter=M -- path/to/file.py
The -g flag tells git log to walk the reflog instead of the commit graph. Combined with --diff-filter=M (modified), this finds when a file was changed, even across resets and rebases.
git reflog --grep-reflog="association engine"
git show HEAD@{5}
git diff HEAD@{3}
Useful to answer: "what changed between now and three operations ago?"
Reflog Maintenance
git config gc.reflogExpire
git config gc.reflogExpireUnreachable
Defaults: 90 days for reachable entries, 30 days for unreachable. These are your recovery windows.
git reflog expire --expire=60.days --all
git reflog expire --expire=30.days refs/heads/main
You’d do this before git gc --aggressive to reclaim disk space in a repo with heavy rebase history. In normal operation, leave the defaults alone — the safety net is worth the disk.
Reflog-Based Time Travel
git show HEAD@{yesterday}
git show HEAD@{2026-04-08.15:30:00}
git diff HEAD@{1.week.ago}
These time-based reflog expressions are powerful for answering "when did this break?" without knowing specific commit hashes.