Git reflog is your time machine and safety net rolled into one. When you think you've lost commits, accidentally deleted branches, or made a terrible mistake, reflog is there to save the day. It records every change to your repository's HEAD, creating a complete history of where you've been.
Understanding Git Reflog
Reflog (reference log) tracks when the tips of branches and other references were updated in your local repository. Unlike the commit history, reflog includes:
- Commits that were abandoned
- Branches that were deleted
- Commits lost during rebases
- Any HEAD movement in your repository
Think of it as Git's "undo" history—a record of everything you've done, even things not in your commit graph anymore.
Basic Reflog Commands
Viewing the Reflog
# Show reflog for HEAD
git reflog
# or
git reflog show
# Sample output
abc1234 HEAD@{0}: commit: Fix payment bug
def5678 HEAD@{1}: checkout: moving from feature to main
ghi9012 HEAD@{2}: commit: Add payment feature
jkl3456 HEAD@{3}: checkout: moving from main to feature
Reflog for Specific References
# Reflog for specific branch
git reflog show main
git reflog show feature-branch
# Reflog for stash
git reflog show stash
# Detailed reflog with dates
git reflog --date=relative
git reflog --date=iso
Common Recovery Scenarios
1. Recovering Lost Commits
You accidentally reset your branch and lost commits:
# Oops! Lost your last 3 commits
git reset --hard HEAD~3
# Don't panic! Check reflog
git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: Important feature <-- Want this back!
# Recover the lost commit
git reset --hard HEAD@{1}
# or
git reset --hard def5678
2. Recovering Deleted Branches
Accidentally deleted a branch with important work:
# Deleted a branch
git branch -D feature-important
# Find the branch's last commit
git reflog | grep feature-important
# ghi9012 HEAD@{5}: checkout: moving from feature-important to main
# Recreate the branch
git branch feature-important ghi9012
# Or checkout directly
git checkout -b feature-important ghi9012
3. Undoing a Bad Merge
Merged the wrong branch and want to undo:
# Bad merge happened
git merge feature-experimental
# Conflicts, problems, chaos!
# Check reflog to find state before merge
git reflog
# abc1234 HEAD@{0}: merge feature-experimental: Merge made
# def5678 HEAD@{1}: commit: Last good commit
# Reset to before the merge
git reset --hard HEAD@{1}
4. Recovering from Failed Rebase
Rebase went wrong and you want to abort:
# Rebase gone wrong
git rebase main
# Conflicts everywhere!
# If still in rebase
git rebase --abort
# If rebase completed but went badly
git reflog
# Find commit before rebase started
git reset --hard HEAD@{5} # Or appropriate reflog entry
Advanced Reflog Techniques
Time-Based Recovery
# Find HEAD position at specific time
git reflog --date=iso
git reflog --since="2 hours ago"
git reflog --until="yesterday"
# Recover to specific time
git reset --hard HEAD@{2.hours.ago}
git reset --hard HEAD@{yesterday}
git reset --hard HEAD@{2024-01-15.14:30:00}
Searching Reflog
# Search reflog messages
git reflog | grep "payment"
# Search with context
git reflog --grep="feature" --walk-reflogs
# Find specific operations
git reflog | grep -E "merge|rebase|reset"
Reflog Expiration
# Check reflog expiration settings
git config gc.reflogExpire
git config gc.reflogExpireUnreachable
# Manually expire old reflog entries
git reflog expire --expire=30.days --all
# Keep reflog forever (not recommended for large repos)
git config gc.reflogExpire never
Practical Recovery Workflows
The "Oh No!" Recovery Process
#!/bin/bash
# recovery-helper.sh
echo "Git Recovery Helper"
echo "==================="
echo ""
echo "Recent reflog entries:"
git reflog -10 --date=relative
echo ""
echo "What did you lose?"
echo "1) Commits after reset"
echo "2) Deleted branch"
echo "3) Bad merge"
echo "4) Failed rebase"
echo "5) Stashed changes"
read -p "Select option (1-5): " option
case $option in
1)
echo "Last 5 resets:"
git reflog | grep -E "reset:" | head -5
read -p "Enter SHA or HEAD@{n} to recover to: " target
git reset --hard $target
;;
2)
read -p "Enter branch name: " branch
sha=$(git reflog | grep "checkout: moving from $branch" | head -1 | awk '{print $1}')
git checkout -b $branch $sha
;;
3)
before_merge=$(git reflog | grep -B1 "merge" | head -1 | awk '{print $1}')
git reset --hard $before_merge
;;
4)
before_rebase=$(git reflog | grep -B1 "rebase" | head -1 | awk '{print $1}')
git reset --hard $before_rebase
;;
5)
git stash list
git reflog show stash
;;
esac
Creating Safety Checkpoints
# Before risky operation
git branch backup-before-rebase
# Or tag it
git tag backup-$(date +%Y%m%d-%H%M%S)
# After operation, if successful
git branch -D backup-before-rebase
# If failed, recover
git reset --hard backup-before-rebase
Reflog and Remote Repositories
Important: Reflog is Local Only
# Reflog doesn't sync with remote
git push # Reflog stays local
# Each clone has its own reflog
git clone repo new-clone
cd new-clone
git reflog # Empty except for clone operation
Recovering Pushed Then Lost Commits
# If you pushed before losing
git fetch origin
# Find in remote tracking branches
git log origin/main
# Cherry-pick or merge lost commits
git cherry-pick origin/main~2..origin/main
Reflog for Different Operations
Stash Reflog
# View stash reflog
git reflog show stash
# Recover dropped stash
git reflog show stash | grep "WIP on"
# stash@{2}: WIP on main: abc1234 Important work
# Apply old stash entry
git stash apply stash@{2}
Branch Reflog
# See specific branch history
git reflog show feature-branch
# Recover branch to previous state
git checkout feature-branch
git reset --hard feature-branch@{2}
Tag Recovery
# Accidentally deleted tag
git tag -d v1.0.0
# Find in reflog
git reflog | grep "tag: v1.0.0"
# abc1234 HEAD@{10}: commit: Release v1.0.0
# Recreate tag
git tag v1.0.0 abc1234
Combining Reflog with Other Commands
Reflog + Bisect
# Find when bug was introduced
git bisect start
git bisect bad HEAD
git bisect good HEAD@{30} # Known good from reflog
Reflog + Cherry-pick
# Cherry-pick lost commits
git reflog | grep "feature work"
git cherry-pick HEAD@{5}
git cherry-pick HEAD@{7}..HEAD@{10}
Reflog + Diff
# See what changed between reflog entries
git diff HEAD@{5} HEAD@{0}
# See files changed
git diff --name-only HEAD@{10} HEAD
Best Practices
1. Regular Reflog Maintenance
# Check reflog size
du -sh .git/logs
# Clean old entries (default 90 days unreachable, 30 days reachable)
git reflog expire --expire=now --all
git gc --prune=now
2. Descriptive Commit Messages
# Good messages make reflog more useful
git commit -m "feat: Add payment processing with Stripe"
# Better than
git commit -m "stuff"
# Reflog becomes self-documenting
3. Create Recovery Aliases
# Useful reflog aliases
git config --global alias.undo "reset --hard HEAD@{1}"
git config --global alias.find "!git reflog | grep"
git config --global alias.recovery-point "!git branch recovery-$(date +%s)"
Troubleshooting with Reflog
Finding When File Was Deleted
# When was file.txt deleted?
git reflog --all -- file.txt
# Or search through history
for ref in $(git reflog --format="%h"); do
git ls-tree -r $ref --name-only | grep -q "file.txt" && echo "$ref has file.txt"
done
Debugging Mysterious Changes
# When did this line appear?
git reflog | while read sha rest; do
git show $sha:file.txt 2>/dev/null | grep -q "mysterious line" && echo "$sha: $rest"
done
Recovery Verification
# Verify recovery was successful
before_recovery=$(git rev-parse HEAD)
git reset --hard HEAD@{5}
after_recovery=$(git rev-parse HEAD)
if [ "$before_recovery" != "$after_recovery" ]; then
echo "Recovery successful"
git log --oneline -5
else
echo "No changes made"
fi
Common Pitfalls
1. Reflog Expires
# Default expiration
# - 90 days for unreachable commits
# - 30 days for reachable commits
# Extend expiration for important repos
git config gc.reflogExpire "365 days"
git config gc.reflogExpireUnreachable "365 days"
2. Forced Garbage Collection
# This removes reflog entries!
git gc --prune=now --aggressive
# Safer approach
git gc --auto
3. Shallow Clones
# Shallow clones have limited reflog
git clone --depth=1 repo
# Convert to full clone for complete reflog
git fetch --unshallow
Real-World Recovery Examples
Example 1: Production Hotfix Gone Wrong
# Made changes directly on production branch
git checkout production
# ... made changes ...
git reset --hard origin/production # Lost local changes!
# Recovery
git reflog
# Find your commits
git cherry-pick HEAD@{2}..HEAD@{5}
Example 2: Interactive Rebase Disaster
# Messed up during interactive rebase
git rebase -i HEAD~10
# Accidentally deleted important commits
# Find pre-rebase state
git reflog | grep "rebase -i (start)"
# Reset to before rebase
git reset --hard HEAD@{15}
Example 3: Wrong Branch Merge
# Merged experimental branch to main instead of develop
git checkout main
git merge experimental-feature # Wrong!
# Quick recovery
git reflog
git reset --hard HEAD@{1} # Before merge
git checkout develop
git merge experimental-feature # Correct branch
Conclusion
Git reflog is your insurance policy against Git mishaps. It's the safety net that lets you experiment fearlessly, knowing you can always go back. While it shouldn't replace good Git practices, understanding reflog transforms "oh no!" moments into "no problem!" solutions.
Remember: in Git, very little is truly lost. Between reflog, the object database, and Git's robust architecture, your code is safer than you might think. Master reflog, and you'll never fear Git commands again.
The key is acting quickly—reflog entries do expire. When disaster strikes, don't panic. Check the reflog, find your lost work, and recover with confidence.
Share this article
David Childs
Consulting Systems Engineer with over 10 years of experience building scalable infrastructure and helping organizations optimize their technology stack.