Git Reflog — The 90-Day Safety Net That Saved a Deleted Branch
git reflog records every HEAD movement for 90 days.
20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.
- ✓Git 2.x installed, basic Git commands (commit, branch, checkout, reset, rebase), understanding of commit history and HEAD, terminal access.
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).
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.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Deleted Branch With 3 Weeks of Work — Reflog to the Rescue
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.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.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.- Reflog records every HEAD movement for 90 days.
git branch -Ddeletes 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.
git reflog | head -20, find the SHA before the reset, then git reset --hard <sha>git reflog | grep <branch-name> to find the tip commit SHA, then git branch recovered-branch <sha>git reflog to find ORIG_HEAD or the SHA before rebase, then git reset --hard <sha>| File | Command / Code | Purpose |
|---|---|---|
| terminal | git reflog | What Is Git Reflog and Why It Matters |
| terminal | git reflog main | Understanding Reflog Entries |
| terminal | git branch -D feature-x | Recovering a Deleted Branch with Reflog |
| terminal | git reset --hard HEAD~2 | Undoing a Hard Reset with Reflog |
| terminal | git rebase main | Recovering Lost Commits After a Rebase |
| terminal | git checkout abc1234 | Using Reflog to Recover from a Detached HEAD |
| terminal | git log --oneline -5 | Reflog vs Git Log |
| terminal | git config gc.reflogExpire 90.days | Cleaning Up Reflog and Managing Expiry |
| deploy.sh | BEFORE=$(git rev-parse HEAD) | Automating Reflog Recovery in Scripts |
| terminal | git checkout -b temp | Common Pitfalls and Best Practices |
Key takeaways
git reflog | grep <branch> then git branch <name> <sha>Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.
That's Git. Mark it forged?
4 min read · try the examples if you haven't