Git Reflog: The Safety Net Most Developers Don't Know They Have
by Eric Hanson, Backend Developer at Clean Systems Consulting
The Command You Should Know Before You Break Something
Before the next time you run git reset --hard, git rebase, or git branch -D, you should understand reflog. Not because you'll need it right now — but because the next time you do something irreversible-looking and feel your stomach drop, you'll know exactly where to look.
Reflog (reference log) is a per-repository, per-developer log that records every movement of HEAD and branch pointers. It is stored locally in .git/logs/. It is not shared with the remote. It is your personal time machine for Git operations.
What Reflog Records
Every time HEAD moves — because you checked out a branch, made a commit, rebased, merged, reset, or ran almost any Git command that changes position — reflog records an entry:
git reflog
# Output:
a3f9d24 (HEAD -> main) HEAD@{0}: commit: Add payment idempotency key
b7c12e1 HEAD@{1}: rebase (finish): returning to refs/heads/main
f4d8a09 HEAD@{2}: rebase (pick): Refactor PaymentGatewayClient
9e1b3c7 HEAD@{3}: rebase (pick): Add retry logic
c2a8f17 HEAD@{4}: rebase (start): checkout main
1d5e3b9 HEAD@{5}: commit: WIP payment retry
8f2a6c4 HEAD@{6}: checkout: moving from feature/payment to main
7b1d4e2 HEAD@{7}: commit: Add PaymentService
The HEAD@{N} notation refers to where HEAD was N moves ago. HEAD@{0} is current. HEAD@{1} is one operation back. You can use these references anywhere Git accepts a commit SHA.
The Reflog After a Disaster
Scenario: you ran git reset --hard HEAD~3 and lost three commits you needed.
git reflog
# a3f9d24 HEAD@{0}: reset: moving to HEAD~3
# d8b7c12 HEAD@{1}: commit: The third commit you need
# e4f9a83 HEAD@{2}: commit: The second commit you need
# 9c1b2d7 HEAD@{3}: commit: The first commit you need
The commits you "lost" are at HEAD@{1}, HEAD@{2}, and HEAD@{3}. To recover:
# Move HEAD back to where you were before the reset
git reset --hard HEAD@{1}
# or using the SHA directly
git reset --hard d8b7c12
Your branch now points to where it was before you ran the reset. The commits are back.
Reading Reflog for a Specific Branch
HEAD isn't the only thing reflog tracks. Each branch has its own reflog:
# Reflog for a specific branch
git reflog show feature/payment-retry
# Useful when you deleted a branch and need its last commit
git reflog show feature/payment-retry | head -1
# f4d8a09 feature/payment-retry@{0}: commit: Final payment retry implementation
Even after you delete a branch, the branch reflog may still be readable briefly. The objects it references remain in the store until garbage collection.
Reflog With Time-Based Filtering
If you know approximately when the operation you're trying to undo happened, reflog accepts time qualifiers:
# Where was HEAD yesterday?
git reflog --since="yesterday"
# Where was HEAD before the meeting this morning?
git reflog --since="8 hours ago"
# Show entries from a specific time range
git reflog --since="2026-04-24 09:00" --until="2026-04-24 10:00"
This is useful when the list of reflog entries is long and you know roughly when the disaster happened.
Reflog and Garbage Collection
Reflog entries don't last forever. Git's garbage collector (git gc) runs automatically and cleans up objects and reflog entries based on these defaults:
git config gc.reflogExpire # default: 90 days
git config gc.reflogExpireUnreachable # default: 30 days
"Reachable" objects are those pointed to by a branch, tag, or current reflog entry. "Unreachable" objects — like commits that were reset away and are no longer pointed to by any branch — expire in 30 days by default.
In practice, this means you have a generous window to recover from most disasters. But it is a window, not infinite time. Don't wait three months to recover the commits you lost last week.
Reflog Is Not Synced to the Remote
This is the most important limitation: reflog is local. If your laptop is destroyed, reflog is gone too. If you git clone a repository fresh, you have no reflog for operations that happened before the clone.
This is why the pattern of "commit early, commit often" matters even on personal branches. Commits that exist in the remote object store are recoverable even without reflog (via git fsck --lost-found). Uncommitted work that was only in your working tree is not.
The Practical Habit
Before any operation that feels risky — a rebase you haven't done before, a reset you're not sure about, a complex merge — run git reflog and note the current HEAD@{0} SHA. Write it on a sticky note if necessary. After the operation, if something went wrong, you know exactly where to return to.
Or more simply: git reflog | head -5 before anything scary. That five-second habit has saved hours of recovery work.
Reflog is there. It's always watching. You just have to know to look at it.