Git Reflog: Your Safety Net When Everything Goes Wrong¶
How the reflog saved my work after a disastrous reset, and why I now consider it the most underrated Git feature.
The Day I Thought I Lost a Week of Work¶
I was cleaning up my branch before a pull request. I ran git reset --hard HEAD~5 to undo my last 5 commits and redo them properly. Except I miscounted. I actually reset past a merge commit that contained three days of my colleague's integrated work. Panic. git log showed no trace of those commits. The branch looked like it was a week behind.
Then I remembered the reflog. Five minutes later, everything was restored. No data was ever actually lost.
What Is the Reflog?¶
The reflog (reference log) is git's private diary. Every time HEAD moves — commit, checkout, reset, rebase, merge, pull — git records where HEAD was before and after. It's a chronological list of every state your repository has been in.
# View the reflog
git reflog
# Output looks like:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~5
# e4f5g6h HEAD@{1}: commit: Add integration tests
# h7i8j9k HEAD@{2}: commit: Implement user validation
# l0m1n2o HEAD@{3}: commit: Add API endpoint
# ...
Each entry shows:
- The commit SHA at that point
- The position index (HEAD@{0} is current, HEAD@{1} is previous)
- The operation that caused the move
Recovering From Disastrous Resets¶
The recovery pattern is always the same:
# 1. Look at the reflog to find where you were before the mistake
git reflog
# 2. Find the commit you want to return to (before the bad reset)
# e4f5g6h HEAD@{1}: commit: Add integration tests ← that's the one
# 3. Reset back to it
git reset --hard e4f5g6h
# Or equivalently:
git reset --hard HEAD@{1}
That's it. Your commits, your files, your working state — all restored. The "destructive" reset didn't actually destroy anything. It just moved a pointer.
Understanding Why Nothing Is Truly Lost¶
This is the key insight: git reset --hard doesn't delete commits. It moves the branch pointer and updates the working directory. The old commits still exist in the object database. They become "unreachable" — no branch or tag points to them — but they're still there.
The reflog keeps references to these orphaned commits for 90 days by default (30 days for unreachable commits). Only after that does git gc actually remove them.
# See how long reflog entries are kept
git config gc.reflogExpire # default: 90 days
git config gc.reflogExpireUnreachable # default: 30 days
Reflog Recipes I Use Regularly¶
Recovering a dropped stash:
# Stashes are also recorded in the reflog
git fsck --unreachable | grep commit
# Or more directly:
git log --walk-reflogs --grep="WIP on" --all
Finding a commit from a deleted branch:
# You deleted a branch, now you need it back
git reflog | grep "checkout.*branch-name"
# Find the SHA, recreate the branch
git branch recovered-branch <sha>
Undoing a bad rebase:
# After a rebase went wrong
git reflog
# Find the entry BEFORE the rebase started
# Usually marked as "rebase (start)"
git reset --hard HEAD@{N} # N = the index before rebase
Recovering from git clean -fd:
Unfortunately, the reflog cannot help here. git clean removes untracked files — files git never knew about. This is the one truly destructive operation. I always run git clean -n (dry run) first.
Branch Reflogs vs. HEAD Reflog¶
There are actually two types of reflogs:
# HEAD reflog (follows wherever you are)
git reflog
# Branch-specific reflog (follows a specific branch's history)
git reflog show main
git reflog show feature/auth
The branch reflog is useful when HEAD has been jumping between branches. It shows only the movements of that specific branch pointer.
Reflog as an Audit Trail¶
Beyond recovery, I use the reflog to understand what happened:
# What did I do in the last 2 hours?
git reflog --since="2 hours ago"
# What happened to main today?
git reflog show main --since="today"
# Show actual diffs between reflog entries
git diff HEAD@{3} HEAD@{0}
When a colleague says "something changed but I don't know what I did," I ask them to check their reflog. It's a complete record of their local operations.
Protecting Yourself: Reflog Best Practices¶
-
Never run
git gc --prune=nowunless you're sure. It removes unreachable objects immediately, bypassing the 30-day grace period. -
Before destructive operations, note your current SHA:
-
Use
--keepflag with reset when unsure: -
Set longer reflog expiry for important repos:
Key Takeaway¶
The reflog is proof that git is designed with a safety net. Almost nothing is truly destructive — resets, rebases, and branch deletions all leave recoverable traces for at least 30 days. Once you internalize this, git becomes less frightening. You can experiment boldly, knowing that git reflog will always show you the way back.
Tags: git, reflog, recovery, safety, undo