
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Every developer eventually faces the panic of a missing commit after a forced reset, accidental branch deletion, or failed rebase. The good news is that Git rarely deletes work immediately; it simply removes the reference pointing to it. You can recover lost commits with git reflog because this command exposes the hidden history of HEAD movements that standard logs ignore. Before you accept data loss or rewrite history blindly, understanding this safety net is essential for any team managing production code or complex feature branches.
git reflog to list all HEAD positions, identify the target commit hash, and restore it using git reset --hard <hash> or git cherry-pick <hash>. This works because Git retains unreachable objects for 30+ days before garbage collection purges them.How do you recover lost commits with git reflog safely?
The most common scenario I see in production environments involves a developer running git reset --hard to clean up a messy branch, only to realize they discarded a critical fix. Standard version control training often treats this as irreversible, but Git’s internal architecture keeps these objects alive. When working with teams across Nepal and global clients, I emphasize that safety comes from verification before restoration. Never blindly reset to a hash without inspecting it first.
Step-by-step recovery workflow
- Run
git reflogto display the local history of HEAD updates. Each entry shows a hash, a relative timestamp, and the action that created it. - Identify the commit message or hash corresponding to your lost work. Note the
HEAD@{n}reference or the full SHA. - Verify the content before restoring: run
git show <hash>orgit diff <current-HEAD> <hash>to confirm it contains the expected changes. - Choose your recovery method based on context:
- Full branch restore:
git reset --hard <hash>moves HEAD and the current branch pointer back to that state. Use only if you want to discard everything after that point. - Selective recovery:
git cherry-pick <hash>applies just that commit onto your current branch, preserving subsequent work. - Create a safety branch:
git branch recovery-<timestamp> <hash>creates a new branch at the lost commit without altering your current working tree. This is the safest first step.
- Full branch restore:
- After recovery, run
git statusandgit log --oneline -5to verify the state matches expectations before pushing or continuing development.
A common mistake is assuming reflog entries persist forever. Git’s garbage collector (git gc) prunes unreachable objects after a configurable grace period, typically 30 days for reflog entries and 2 weeks for other unreachable objects. If you’re auditing infrastructure or managing compliance-ready repositories as discussed in my infrastructure as code guide, document recovery procedures before teams hit that window.
What is the difference between git reflog and git log?
Understanding this distinction prevents confusion during high-pressure incidents. git log traverses the commit graph starting from branch tips; it only shows reachable commits connected to current references. git reflog is a local, per-reference log of position updates to HEAD (or other refs), regardless of whether those commits are still part of any branch. Think of git log as the official project history and git reflog as your personal undo journal.
| Criteria | git log | git reflog |
|---|---|---|
| Scope | Reachable commits from branch tips | All HEAD movements, including orphaned commits |
| Persistence | Permanent (until explicitly rewritten) | Local only; expires after gc.pruneExpire (default 30 days) |
| Visibility | Shared across clones via fetch/pull | Never pushed; unique to each repository clone |
| Use case | Reviewing project history, release notes | Recovering lost commits with git reflog, debugging resets |
| Includes resets/amends? | No (rewritten commits disappear) | Yes, every HEAD update is recorded |
In practice, I use git log --all --graph --oneline alongside reflog during incident response. The graph view sometimes reveals dangling commits that reflog alone might miss if multiple branches were involved. For teams adopting CI/CD pipelines like those in my GitLab CI pipeline tutorial, remember that CI runners have their own ephemeral reflogs; recovery must happen on the developer’s local machine or a persistent build artifact store.
Can you recover commits after git gc or expiration?
This is where many engineers hit a wall. Once git gc --prune=now runs or the reflog expiry window passes, unreachable objects are permanently deleted. However, "expired" doesn’t always mean "gone." Before declaring defeat, run git fsck --lost-found. This command scans the object database for dangling commits and blobs not referenced by any ref or reflog entry. If found, they appear in .git/lost-found/commit/ with their full SHA as filenames.
# Search for dangling commits after reflog expiration
git fsck --lost-found --no-reflogs
# Inspect a recovered dangling commit
git show <dangling-sha>
# Restore if valid
git cherry-pick <dangling-sha> In regulated environments requiring SOC 2 or ISO 27001 compliance, I configure gc.reflogExpire and gc.pruneExpire to longer values (e.g., 90 days) in shared repositories. This extends the recovery window without significant storage overhead. For teams hosting on AWS EC2 or similar infrastructure covered in my EC2 beginners guide, ensure backup strategies include periodic .git directory snapshots, as cloud volume snapshots provide an additional recovery layer beyond Git’s internal mechanisms.
How do you prevent future commit loss in team workflows?
Recovery is valuable, but prevention scales better. After years of debugging lost work across distributed teams, I’ve found that procedural safeguards outperform individual heroics. Implement these practices systematically:
- Enable automatic backups: Configure pre-push hooks or CI jobs to tag nightly snapshots of active branches. Even simple cron-based
git bundleexports create portable recovery points. - Use feature branches rigorously: Never commit directly to main/develop. Isolated branches limit blast radius when resets go wrong.
- Audit reflog retention: Run
git config --get gc.reflogExpirequarterly. Default 30 days may be insufficient for long-running feature branches or compliance review cycles. - Document recovery runbooks: Include reflog recovery steps in your team’s incident response playbook. Link to authoritative sources like this guide rather than relying on tribal knowledge.
- Leverage stash with messages: Replace anonymous
git stashwithgit stash push -m "description". Stashes appear in reflog but descriptive labels prevent confusion during recovery.
For teams adopting containerized development environments as described in my Docker containerization guide, mount volumes persistently and avoid ephemeral containers for active development. Ephemeral environments destroy reflog history on restart, making recovery impossible. Treat your local .git directory with the same care as production databases.
Mastering recover lost commits with git reflog for production resilience
The ability to recover lost commits with git reflog separates confident engineers from those paralyzed by fear of destructive operations. Treat reflog as a standard tool in your debugging arsenal, not an emergency-only feature. Verify before restoring, understand expiration boundaries, and implement preventive workflows so recovery becomes rare rather than routine. If your team struggles with Git safety nets, compliance-ready version control, or needs hands-on training for Nepali or distributed engineering teams, reach out to discuss tailored DevOps support.