Recover Lost Commits with git reflog

Khimananda Oli 7 min read Virtualization
Recover Lost Commits with git reflog

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 History vs. Reflog TimelineABCLost CommitDCurrent HEADReflog EntriesHEAD@{0}: commit: D (current)HEAD@{1}: reset: moving to BHEAD@{2}: commit: C (lost)HEAD@{3}: commit: BHEAD@{4}: commit: AReflog preserves C even though branch history skips it
Visualizing how recover lost commits with git reflog works by exposing orphaned HEAD states invisible to git log

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

  1. Run git reflog to display the local history of HEAD updates. Each entry shows a hash, a relative timestamp, and the action that created it.
  2. Identify the commit message or hash corresponding to your lost work. Note the HEAD@{n} reference or the full SHA.
  3. Verify the content before restoring: run git show <hash> or git diff <current-HEAD> <hash> to confirm it contains the expected changes.
  4. 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.
  5. After recovery, run git status and git log --oneline -5 to 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.

Criteriagit loggit reflog
ScopeReachable commits from branch tipsAll HEAD movements, including orphaned commits
PersistencePermanent (until explicitly rewritten)Local only; expires after gc.pruneExpire (default 30 days)
VisibilityShared across clones via fetch/pullNever pushed; unique to each repository clone
Use caseReviewing project history, release notesRecovering 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.

Recovery Method Decision FlowFound lost commit hashNeed entire branch state restored?YesNogit reset --hard <hash>(Discards later work)Only specific commit needed?YesUnsuregit cherry-pick <hash>git branch safe-<hash>Always verify with git show before executing destructive commands
Decision framework for selecting the right recovery command when you recover lost commits with git reflog

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 bundle exports 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.reflogExpire quarterly. 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 stash with git 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.

Recovery Success by Practice & Time Window0%50%100%<24h99%1-30d95%30-90d60%>90d20%Post-gc<5%Success rate assumes default gc settings; extend reflogExpire for higher retention
Recovery probability declines sharply after 30 days without extended reflog configuration or external backups

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.

Frequently Asked Questions

Git reflog records updates to branch tips and HEAD, allowing you to recover lost commits after destructive operations like reset or rebase by referencing previous SHA hashes.

Run git reflog show HEAD to list recent position changes with timestamps and SHAs, or use git log -g for a more detailed commit-centric view of reference updates.

Yes, if the branch tip was recently updated. Find the last known SHA via git reflog show --all, then recreate the branch using git branch .

Only if unreachable objects remain unpruned. By default, git gc keeps dangling commits for two weeks; run git fsck --unreachable to verify availability before recovery.

Default expiration is 90 days for reachable refs and 30 days for unreachable ones. Configure gc.reflogExpire and gc.reflogExpireUnreachable in .git/config to adjust retention periods.

Reflog tracks all HEAD movements automatically, while stash saves temporary working directory states. Use reflog for lost commits; use stash for shelving uncommitted changes safely.

Yes, locally. The remote loses history, but your local reflog retains pre-force-push SHAs. Recover with git reset --hard before pushing again cautiously.

No, bare repos lack working trees and typically disable reflogs. Enable core.logAllRefUpdates true during clone or init to maintain reflogs in server-side bare repositories.

Identify the target SHA via git reflog, then run git checkout -- path/to/file to extract that specific file version without altering current branch state.

Cloning creates fresh refs without local history. Reflogs are local-only metadata; they are never transferred via fetch or push. Only new local operations generate entries.

Yes, individual pre-squash commits remain accessible via reflog until pruned. Locate original SHAs and cherry-pick them if you need granular history before the squash.

Potentially, since it retains references to commits containing secrets even after rewriting history. Audit with git reflog expire --expire=now --all and prune immediately after credential leaks.

Run git reflog expire --expire=30.days.ago --all followed by git gc --prune=now to remove stale entries and reclaim disk space from unreachable objects safely.

Usually not, as shallow clones omit reflogs. Configure full fetch depth and enable core.logAllRefUpdates in pipeline setup if post-build forensic recovery is required.

Simply check reflog again to find the prior correct SHA and reset once more. Reflog itself is append-only, so missteps remain recoverable until expiration occurs.