Understanding Reflog
Reflog (reference log) records when branch tips and HEAD change. It's your safety net for recovering from almost any Git mistake.
Viewing Reflog
# View HEAD reflog
git reflog
# View specific branch reflog
git reflog show main
# View with timestamps
git reflog --date=relative
# View with full commit info
git reflog --format=fuller
# View reflog for specific ref
git reflog show stash
Reflog Output
abc1234 HEAD@{0}: commit: Add new feature
def5678 HEAD@{1}: checkout: moving from main to feature
ghi9012 HEAD@{2}: reset: moving to HEAD~3
jkl3456 HEAD@{3}: commit: Previous work
mno7890 HEAD@{4}: merge feature: Fast-forward
Recovering Lost Commits
# After accidental reset
git reset --hard HEAD~3
# Find lost commits
git reflog
# Find: abc1234 HEAD@{1}: commit: Important work
# Recover
git reset --hard abc1234
# Or create new branch with lost commit
git branch recovered abc1234
Recovering Deleted Branch
# Accidentally deleted branch
git branch -D feature-branch
# Find the last commit
git reflog | grep feature-branch
# Or: git reflog | grep checkout.*feature
# Recreate branch
git branch feature-branch abc1234
Undoing a Bad Rebase
# After disastrous rebase
git rebase main
# Creates mess...
# Find pre-rebase state
git reflog
# Look for: HEAD@{5}: rebase started
# Reset to before rebase
git reset --hard HEAD@{5}
# Or use ORIG_HEAD (set before rebase)
git reset --hard ORIG_HEAD
Recovering Stash
# Accidentally dropped stash
git stash drop
# Find in reflog
git fsck --unreachable | grep commit
# Or search stash reflog
git reflog show stash
# Recover stash
git stash apply abc1234
Finding Lost Work
# Find all unreachable commits
git fsck --lost-found
# Commits are in .git/lost-found/commit/
# Search for specific content
git log --all --full-history -- "**/deleted-file.js"
# Search commit messages
git log --all --oneline | grep "keyword"
# Search in reflog
git reflog | grep "keyword"
Recovery Scenarios
# Scenario 1: Recover from bad merge
git reflog
git reset --hard HEAD@{1}
# Scenario 2: Recover amended commit
git reflog
# Old commit is HEAD@{1}
git branch recovered-original HEAD@{1}
# Scenario 3: Recover from reset --hard
git reflog
git reset --hard HEAD@{n} # n = steps back
# Scenario 4: Recover file version
git checkout HEAD@{5} -- path/to/file.js
Reflog Expiration
# Default: 90 days for reachable, 30 for unreachable
# View expiration settings
git config gc.reflogExpire
git config gc.reflogExpireUnreachable
# Change expiration
git config gc.reflogExpire "180 days"
git config gc.reflogExpireUnreachable "90 days"
# Expire old entries manually
git reflog expire --expire=90.days.ago --all
# Never expire (not recommended)
git config gc.reflogExpire never
ORIG_HEAD and Other Refs
# ORIG_HEAD - set before dangerous operations
git reset --hard ORIG_HEAD
# MERGE_HEAD - during merge
cat .git/MERGE_HEAD
# FETCH_HEAD - last fetch
git log FETCH_HEAD
# CHERRY_PICK_HEAD - during cherry-pick
cat .git/CHERRY_PICK_HEAD
Preventive Measures
# Create backup branch before risky operations
git branch backup
# Use --dry-run when available
git clean -n # Instead of git clean -f
# Check what will be affected
git log HEAD..origin/main # Before pull
git log origin/main..HEAD # Before push
# Enable rerere for conflict resolution memory
git config rerere.enabled true
When Reflog Can't Help
- Uncommitted changes lost with
reset --hard - Reflog entries expired (after gc)
- Working with bare repositories
- Files never staged or committed
Best Practices
- Commit often, even WIP commits
- Create branches before experiments
- Know your reflog before doing destructive operations
- Consider longer reflog expiration for important repos