Home DevOps Git Reflog — The 90-Day Safety Net That Saved a Deleted Branch
Intermediate 4 min · July 11, 2026
Git Reflog: Recover Lost Commits and Branches

Git Reflog — The 90-Day Safety Net That Saved a Deleted Branch

git reflog records every HEAD movement for 90 days.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 12, 2026
last updated
2,406
articles · all by Naren
Before you start⏱ 15 min
  • Git 2.x installed, basic Git commands (commit, branch, checkout, reset, rebase), understanding of commit history and HEAD, terminal access.
 ● Production Incident
Quick Answer

git reflog shows every HEAD movement. Find the SHA before the disaster, then git reset --hard to restore. Reflog entries expire after 90 days (configurable).

✦ Definition~90s read
What is Git Reflog?

git reflog (reference log) records every change to HEAD — commits, resets, checkouts, merges, rebases, and amends. It's Git's local activity log, stored in .git/logs/HEAD.

Think of git reflog as the black box flight recorder on an airplane.
Plain-English First

Think of git reflog as the black box flight recorder on an airplane. It records every single move the plane (HEAD) makes — every turn, every altitude change, every bump. When something goes wrong, you can rewind the tape and see exactly where things went off course. The regular git log is the passenger manifest (who's on board). Reflog is the flight recorder (how the plane moved). Both are essential, but only reflog can tell you how you GOT to the current state.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

Every Git user eventually has a 'oh no' moment: git reset --hard on the wrong commit, deleted the wrong branch, rebase gone wrong. git reflog is the safety net for all of these. It records every HEAD movement for 90 days, and recovering from almost any disaster is a single reflog lookup away. The catch: reflog is LOCAL. It only records YOUR movements on YOUR machine. If you never had the commit locally, reflog can't help you. This guide covers the core recovery patterns and the incident where reflog saved three weeks of work after a botched rebase.

What Is Git Reflog and Why It Matters

Git reflog is a local safety net that records every movement of HEAD and branch tips. Unlike git log, which shows commit history, reflog tracks operations like commits, resets, checkouts, and merges. It's your last resort when you accidentally delete a branch, reset to the wrong commit, or lose work after a rebase. Reflog entries expire after 90 days by default (30 for unreachable commits), so you have a window to recover. Understanding reflog is essential for any developer who has ever panicked after a destructive git command. It's not a backup for uncommitted changes, but for commits that have been orphaned or overwritten.

terminalBASH
1
2
git reflog
# Output shows HEAD movements with timestamps and actions
Output
abc1234 HEAD@{0}: commit: Fix login bug
def5678 HEAD@{1}: checkout: moving from main to feature-x
9ab0123 HEAD@{2}: reset: moving to HEAD~2
...
🔥Reflog is local only
Reflog is not shared with remotes. It's stored in .git/logs/ and is unique to each clone. You cannot recover lost commits from a remote using reflog.
📊 Production Insight
In production, reflog has saved teams from losing critical hotfix commits after a botched rebase. Always check reflog before declaring a commit lost.
🎯 Key Takeaway
Git reflog records every HEAD movement, enabling recovery of lost commits and branches.
git-reflog THECODEFORGE.IO Git Reflog Internal Architecture Layered structure of reflog entries and their relationships User Commands git reflog | git reflog show | git reflog expire Reflog Entries HEAD reflog | Branch reflogs | Remote reflogs Entry Metadata Commit hash | Timestamp | Action type Storage Layer .git/logs/HEAD | .git/logs/refs/heads/ | .git/logs/refs/remotes/ THECODEFORGE.IO
thecodeforge.io
Git Reflog

Understanding Reflog Entries: HEAD, Branches, and Timestamps

Each reflog entry includes a commit hash, a reference (like HEAD@{n} or branch@{n}), an action description, and a timestamp. HEAD@{0} is the current state, HEAD@{1} is the previous, and so on. Branch reflogs (e.g., main@{0}) track that branch's tip movements. The action description tells you what happened: commit, reset, checkout, merge, rebase, etc. This metadata is crucial for identifying the exact point to recover. You can also use relative references like HEAD@{5.minutes.ago} or HEAD@{yesterday}. Reflog is stored per-reference in .git/logs/refs/heads/ for branches and .git/logs/HEAD for HEAD.

terminalBASH
1
2
git reflog main
# Shows only movements of the main branch
Output
abc1234 main@{0}: commit: Update config
fgh5678 main@{1}: commit: Add tests
...
💡Use time-based references
You can reference reflog entries by time: git show HEAD@{'5 minutes ago'}. This is useful when you know when the mistake happened.
📊 Production Insight
When debugging a production incident, reflog timestamps help correlate git operations with deployment logs to identify when a bad commit was introduced.
🎯 Key Takeaway
Reflog entries are indexed by position and time, with descriptive actions for each movement.

Recovering a Deleted Branch with Reflog

When you delete a branch with git branch -D, the branch's commits become dangling (unreachable) but remain in the reflog. To recover, first find the commit hash from the reflog of HEAD or the branch itself. If the branch was recently checked out, its tip commit appears in HEAD reflog. Use git reflog to locate the commit, then create a new branch at that commit. If the branch was never checked out, you may need to search the reflog of the branch name (e.g., git reflog feature-x) if it existed. Otherwise, use git fsck --lost-found to find dangling commits.

terminalBASH
1
2
3
4
5
6
7
8
# Accidentally deleted branch 'feature-x'
git branch -D feature-x
# Recover from HEAD reflog
git reflog
# Find the commit: abc1234 HEAD@{3}: checkout: moving from feature-x to main
git branch feature-x abc1234
# Or recover from branch reflog if it exists
git reflog feature-x
Output
Branch 'feature-x' recovered from commit abc1234.
⚠ Don't run git gc
After deleting a branch, avoid running git gc (garbage collection) as it may permanently remove dangling commits. Recover immediately.
📊 Production Insight
In CI/CD pipelines, accidental branch deletion can be mitigated by enforcing branch protection rules, but reflog remains the last line of defense for local recovery.
🎯 Key Takeaway
Deleted branches can be recovered by creating a new branch at the commit hash found in reflog.
Reflog vs Git Log: Key Differences When to use each command for commit history Git Reflog Git Log Scope Local repository only All reachable commits History Retention 90 days default, configurable Indefinite (until garbage collected) Includes Deleted Branches Yes, if within expiry No, only existing refs Use Case Recovery and debugging Reviewing commit history THECODEFORGE.IO
thecodeforge.io
Git Reflog

Undoing a Hard Reset with Reflog

A git reset --hard HEAD~2 discards recent commits and changes in the working directory. Reflog saves the previous HEAD state. To undo, find the commit before the reset in reflog (usually HEAD@{1}) and reset back to it. Alternatively, you can cherry-pick lost commits. This technique is faster than restoring from a backup and works even if you've made additional commits after the reset. Always verify the commit you're resetting to with git show before executing.

terminalBASH
1
2
3
4
5
6
7
8
# Accidentally did hard reset
git reset --hard HEAD~2
# Find previous state
git reflog
# Output: abc1234 HEAD@{0}: reset: moving to HEAD~2
#         def5678 HEAD@{1}: commit: Important work
# Restore
git reset --hard def5678
Output
HEAD now at def5678 Important work
⚠ Reset --hard also discards staged changes
Uncommitted changes are lost permanently. Reflog only recovers commits. Use git stash before risky operations.
📊 Production Insight
Automated scripts that perform hard resets (e.g., in deployment pipelines) should log the pre-reset HEAD hash to allow manual recovery if the script fails.
🎯 Key Takeaway
Reflog allows you to undo a hard reset by resetting back to the previous HEAD entry.

Recovering Lost Commits After a Rebase

Rebasing rewrites commit history, often causing old commits to become unreachable. Reflog records the original branch tip before the rebase. After a rebase, the old commits are still in reflog as ORIG_HEAD or as a separate entry. Use git reflog to find the commit before rebase (often HEAD@{1} or ORIG_HEAD). You can then cherry-pick specific commits or reset the branch back to that point. If you've already made additional commits after rebase, use git cherry-pick to selectively restore lost work.

terminalBASH
1
2
3
4
5
6
7
8
# After a rebase that lost commits
git rebase main
# Realize commits are missing
git reflog
# Find pre-rebase commit: abc1234 HEAD@{3}: checkout: moving from feature to main
git checkout -b feature-recovered abc1234
# Or cherry-pick specific commits
git cherry-pick abc1234..def5678
Output
Recovered branch with lost commits.
💡Use ORIG_HEAD
After a rebase, ORIG_HEAD points to the original branch tip. You can use git reset --hard ORIG_HEAD to undo the entire rebase.
📊 Production Insight
In team environments, enforce a policy to never rebase shared branches. If a rebase is necessary, notify the team and provide the reflog recovery steps in the commit message.
🎯 Key Takeaway
Rebase rewrites history, but reflog preserves the original commits for recovery.

Using Reflog to Recover from a Detached HEAD

A detached HEAD occurs when you checkout a specific commit instead of a branch. Any commits made in this state are orphaned when you checkout another branch. Reflog records the detached HEAD commits. To recover, find the commit hash from reflog and create a branch. This is a common scenario during experimentation or bisecting. Always create a branch before making changes in detached HEAD to avoid losing work.

terminalBASH
1
2
3
4
5
6
7
8
# Made commits in detached HEAD
git checkout abc1234
git commit -m "Experiment"
git checkout main
# Lost commits
git reflog
# Find commit: def5678 HEAD@{1}: commit: Experiment
git branch experiment def5678
Output
Branch 'experiment' created from lost commit.
⚠ Detached HEAD is dangerous
Always create a branch with git checkout -b before making changes. Reflog is your safety net, but prevention is better.
📊 Production Insight
CI systems that checkout specific commits for testing should always create a temporary branch to avoid losing build artifacts in detached HEAD.
🎯 Key Takeaway
Commits made in detached HEAD are recoverable via reflog by creating a branch at the commit hash.

Reflog vs Git Log: When to Use Each

Git log shows the commit history as a DAG of reachable commits. Reflog shows the history of HEAD and branch movements, including commits that are no longer reachable. Use git log for understanding the current state and history. Use reflog for recovery and forensics. Reflog is not a replacement for git log; it's a complementary tool. For example, git log won't show a commit that was reset, but reflog will. Understanding both is key to mastering Git.

terminalBASH
1
2
3
# Compare outputs
git log --oneline -5
git reflog -5
Output
git log: abc1234 Fix bug (reachable)
git reflog: abc1234 HEAD@{0}: commit: Fix bug
def5678 HEAD@{1}: reset: moving to HEAD~2 (lost commit)
🔥Reflog is not a backup
Reflog only tracks local operations. For permanent history, push to remote and use tags. Reflog entries expire.
📊 Production Insight
In incident postmortems, combine git log (what happened) with reflog (who did what) to reconstruct the sequence of events leading to a failure.
🎯 Key Takeaway
Use git log for history, git reflog for recovery of lost commits.

Cleaning Up Reflog and Managing Expiry

Reflog entries expire automatically: 90 days for reachable commits, 30 days for unreachable. You can manually expire entries with git reflog expire --expire=now --all. This is useful before cloning or sharing a repository to reduce size. However, be cautious: expiring reflog removes recovery options. In production, consider setting a longer expiry via git config gc.reflogExpire and gc.reflogExpireUnreachable. Regularly cleaning reflog can prevent repository bloat, but only after ensuring no recovery is needed.

terminalBASH
1
2
3
4
5
6
7
# Set custom expiry (in seconds)
git config gc.reflogExpire 90.days
git config gc.reflogExpireUnreachable 30.days
# Manually expire all entries
git reflog expire --expire=now --all
# Force garbage collection
git gc --prune=now
Output
Reflog entries expired. Repository size reduced.
⚠ Expiring reflog is irreversible
Once expired, lost commits cannot be recovered. Only expire reflog after confirming no recovery is needed, e.g., after a release.
📊 Production Insight
Set reflog expiry to 90 days in production repositories to allow recovery of commits from the last quarter, aligning with typical release cycles.
🎯 Key Takeaway
Manage reflog expiry to balance recovery capability and repository size.

Automating Reflog Recovery in Scripts

In CI/CD or automation scripts, you can use reflog to implement safety nets. For example, before a destructive operation, save the current HEAD hash. If the operation fails, restore from reflog. You can also parse reflog output with grep and awk to find specific commits. However, be aware that reflog is local and not available in fresh clones. For remote recovery, rely on push and tags. Scripts should log reflog entries before and after critical operations for audit trails.

deploy.shBASH
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# Save current HEAD before risky operation
BEFORE=$(git rev-parse HEAD)
git pull --rebase
if [ $? -ne 0 ]; then
  echo "Rebase failed, restoring from reflog"
  git reset --hard $BEFORE
fi
# Log reflog for audit
git reflog -5 >> deploy.log
Output
Rebase succeeded. Logged reflog entries.
💡Use git rev-parse for safety
Always capture the current commit hash with git rev-parse HEAD before risky operations. This is more reliable than parsing reflog.
📊 Production Insight
In production deployment scripts, always save the pre-deployment HEAD. If deployment fails, use reflog to rollback to the saved commit, reducing downtime.
🎯 Key Takeaway
Automate reflog-based recovery in scripts to handle failures gracefully.

Common Pitfalls and Best Practices

Common mistakes: relying on reflog for uncommitted changes (it doesn't track them), forgetting that reflog is local, and expiring reflog prematurely. Best practices: always commit before risky operations, use descriptive commit messages to identify commits in reflog, and regularly back up important branches to remote. Also, understand that reflog is not a substitute for proper branching strategy. Use feature branches and avoid force pushes to shared branches. In case of emergency, stay calm and use reflog before attempting other recovery methods.

terminalBASH
1
2
3
4
5
6
7
8
9
# Bad practice: relying on reflog for uncommitted work
git checkout -b temp
# Make changes but don't commit
git checkout main
# Changes are lost, reflog can't help
# Good practice: commit first
git add . && git commit -m "WIP"
git checkout main
# Now reflog can recover
Output
WIP commit saved, recoverable via reflog.
⚠ Reflog does not track uncommitted changes
Always commit or stash changes before switching branches or performing destructive operations.
📊 Production Insight
Enforce pre-commit hooks that require commits before risky operations. This ensures reflog can always recover work in production environments.
🎯 Key Takeaway
Commit early and often; reflog is for recovering commits, not uncommitted work.

Recovering from a Force Push Gone Wrong

Force pushing (git push --force) can overwrite remote history, causing others to lose commits. If you're the one who force pushed, your local reflog still has the old commits. You can recover by resetting your local branch to the pre-push commit and pushing again with --force-with-lease. If you're on the receiving end and your local branch was overwritten, you can use your local reflog to recover your commits and then push them back. Communication is key: coordinate with your team before force pushing.

terminalBASH
1
2
3
4
5
# After a force push that removed commits
git reflog
# Find old commit: abc1234 HEAD@{2}: commit: Important fix
git reset --hard abc1234
git push --force-with-lease origin main
Output
Remote history restored to include the fix.
⚠ Use --force-with-lease
Instead of --force, use --force-with-lease which checks that your remote-tracking branch is up to date, preventing accidental overwrites of others' work.
📊 Production Insight
In shared repositories, disable force push on protected branches. If force push is necessary, use a temporary branch and merge after recovery.
🎯 Key Takeaway
Local reflog can recover from a bad force push by resetting to the pre-push commit.

Reflog in a Team: Sharing Recovery Steps

When a team member loses commits, you can guide them through reflog recovery without sharing the actual reflog (since it's local). Provide clear steps: run git reflog, look for the commit with a specific message or timestamp, and create a branch. Document common recovery scenarios in your team's wiki. For remote recovery, encourage pushing branches frequently. If a commit is lost on the remote, you may need to recover from a team member's local reflog who had fetched the commit before it was lost.

terminalBASH
1
2
3
4
5
# Team member shares recovery steps via chat
# Step 1: git reflog
# Step 2: find commit with message 'Fix critical bug'
# Step 3: git branch recover-bugfix <hash>
# Step 4: git push origin recover-bugfix
Output
Recovered branch pushed to remote.
💡Document recovery procedures
Create a runbook for common reflog recovery scenarios. Include exact commands and expected output. This reduces panic during incidents.
📊 Production Insight
During an incident, have a designated person run reflog recovery while others focus on root cause analysis. This parallelizes effort and speeds up resolution.
🎯 Key Takeaway
Teach your team reflog recovery to minimize downtime when commits are lost.
● Production incidentPOST-MORTEMseverity: high

Deleted Branch With 3 Weeks of Work — Reflog to the Rescue

Symptom
A junior developer accidentally deleted their feature branch with git branch -D instead of -d. The branch had 3 weeks of unmerged work. The developer panicked. A senior engineer ran git reflog | grep feature/payments, recovered the SHA, and recreated the branch in 30 seconds.
Assumption
The developer assumed the work was permanently lost. They started researching file recovery tools.
Root cause
1. Developer ran git branch -D feature/payments to delete an old local branch. 2. The branch contained commits that had never been pushed to remote. 3. Git deleted the branch pointer but NOT the commits. The commits became dangling objects. 4. The developer didn't know about reflog. They assumed the commits were gone forever. 5. Three weeks of work was recoverable — still in Git's object database, referenced by reflog.
Fix
1. Ran git reflog | head -30 to see recent HEAD movements. 2. Found the entry: a1b2c3d HEAD@{14}: checkout: moving from feature/payments to main. 3. The SHA a1b2c3d was the last commit on the deleted branch. 4. Ran git branch feature/payments a1b2c3d to restore the branch pointer. 5. Pushed the recovered branch to remote: git push origin feature/payments.
Key lesson
  • Reflog records every HEAD movement for 90 days.
  • git branch -D deletes the pointer, not the commits.
  • Recovery is always possible if you haven't run git gc.
  • Check reflog before panicking.
  • Push important branches to remote frequently — reflog is local, remote is permanent.
Production debug guideUse this when production is on fire3 entries
Symptom · 01
Accidentally hard-reset and lost commits
Fix
Run git reflog | head -20, find the SHA before the reset, then git reset --hard <sha>
Symptom · 02
Deleted a branch with unmerged work
Fix
Run git reflog | grep <branch-name> to find the tip commit SHA, then git branch recovered-branch <sha>
Symptom · 03
Rebase went wrong and want to undo
Fix
Run git reflog to find ORIG_HEAD or the SHA before rebase, then git reset --hard <sha>
⚙ Quick Reference
10 commands from this guide
FileCommand / CodePurpose
terminalgit reflogWhat Is Git Reflog and Why It Matters
terminalgit reflog mainUnderstanding Reflog Entries
terminalgit branch -D feature-xRecovering a Deleted Branch with Reflog
terminalgit reset --hard HEAD~2Undoing a Hard Reset with Reflog
terminalgit rebase mainRecovering Lost Commits After a Rebase
terminalgit checkout abc1234Using Reflog to Recover from a Detached HEAD
terminalgit log --oneline -5Reflog vs Git Log
terminalgit config gc.reflogExpire 90.daysCleaning Up Reflog and Managing Expiry
deploy.shBEFORE=$(git rev-parse HEAD)Automating Reflog Recovery in Scripts
terminalgit checkout -b tempCommon Pitfalls and Best Practices

Key takeaways

1
Reflog records every HEAD movement
commits, checkouts, resets, rebases
2
Default expiry
90 days for reachable, 30 days for dangling commits
3
Recover deleted branches
git reflog | grep <branch> then git branch <name> <sha>
4
Reflog is LOCAL
never rely on it as your only backup
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Can reflog recover uncommitted changes?
02
How long do reflog entries last?
03
Can I recover a deleted branch if I never checked it out?
04
Is reflog shared with remote repositories?
05
What is the difference between ORIG_HEAD and reflog?
06
Can I use reflog to recover after a git gc?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 12, 2026
last updated
2,406
articles · all by Naren
🔥

That's Git. Mark it forged?

4 min read · try the examples if you haven't

Previous
Git Worktree: Work on Multiple Branches Simultaneously
30 / 47 · Git
Next
Git Merge Strategies: Recursive, Ours, Theirs and Octopus