← Back to DevBytes

Git Reflog Recovery

Understanding the Git Reflog

The Git reflog (reference log) is a hidden safety net that records every movement of HEAD and branch tips in your local repository. Think of it as a journal that quietly tracks where your HEAD has been, what commits you've checked out, and every branch creation, deletion, rebase, or reset you've performed — even the destructive ones. Unlike Git's commit history which only records permanent snapshots, the reflog captures transient pointer movements, giving you a way to rewind time when things go wrong.

By default, the reflog is local to your clone and is not pushed to remotes. It lives in the .git/logs/ directory and retains entries for 90 days by default (configurable via gc.reflogExpire). This makes it a powerful, private undo mechanism that works even when your commit history appears to have lost commits entirely.

What Exactly Does the Reflog Track?

The reflog tracks every time a ref (like HEAD or a branch name) is updated to point to a new commit. Specifically, it records:

Each reflog entry contains a timestamp, the action performed, and the before-and-after commit hashes. This gives you a chronological audit trail of every significant pointer change in your repository.

Why the Reflog Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

The reflog is arguably Git's most important recovery tool. Here are the disaster scenarios where it saves the day:

In all these cases, the commits themselves often still exist as dangling objects in Git's object database. The problem is you no longer have a branch or tag pointing to them. The reflog gives you the commit hashes you need to resurrect them.

Viewing the Reflog

The primary command for inspecting the reflog is git reflog. Running it without arguments shows the reflog for HEAD:

git reflog

This outputs something like:

a1b2c3d HEAD@{0}: commit: Add payment validation
e4f5g6h HEAD@{1}: rebase (finish): returning to refs/heads/main
e4f5g6h HEAD@{2}: rebase (pick): Refactor user model
i7j8k9l HEAD@{3}: checkout: moving from feature/auth to main
m0n1o2p HEAD@{4}: reset: moving to HEAD~2
q3r4s5t HEAD@{5}: commit: Fix login bug

Each entry shows the commit hash where HEAD now points after the operation, the relative index (HEAD@{n}), and a human-readable description of what happened. The most recent entry is HEAD@{0}, and you can reference any entry using this syntax.

Viewing the Reflog for a Specific Branch

You can also view the reflog for any branch or ref by passing it as an argument:

git reflog show main
git reflog show feature/checkout
git reflog show stash

For even more detail, use git reflog show with the --date= flag to see precise timestamps:

git reflog show --date=iso main
git reflog show --date=relative HEAD

The Underlying Log Files

The reflog data is stored as plain text files. You can inspect them directly:

cat .git/logs/HEAD
cat .git/logs/refs/heads/main

Each line follows the format: old-hash new-hash author timestamp action description. While you can read these files directly, using git reflog is far more practical for recovery purposes.

Recovery Scenarios: Step-by-Step

Scenario 1: Recovering Lost Commits After a Hard Reset

You've accidentally run git reset --hard HEAD~3 and wiped out three commits. The commits still exist as objects; you just need to find their hashes and point a branch at them.

First, examine the reflog to find the commit that HEAD pointed to before the reset:

git reflog

Look for the line that shows the reset operation. The commit hash in that line is where HEAD was before the destructive reset. For example:

d4e5f6g HEAD@{1}: reset: moving to HEAD~3
a7b8c9d HEAD@{2}: commit: Add export feature   <-- this is the commit you want back

The hash a7b8c9d is the tip of your branch before the reset. To restore it, create a new branch pointing to that commit:

git branch recovered-work a7b8c9d

Alternatively, if you want to move your current branch back to that point directly (and you haven't made new commits since the reset), use:

git reset --hard HEAD@{1}

The HEAD@{1} notation directly references the state of HEAD one step before the current position — exactly the commit you were on before the reset.

Scenario 2: Recovering Commits Made on a Detached HEAD

You were exploring a commit, made several commits on a detached HEAD, then checked out a branch and lost track of those commits.

Run git reflog and look for the checkout entries that bracket your detached HEAD session:

git reflog

You'll see something like:

f1e2d3c HEAD@{0}: checkout: moving from 9a8b7c6 to main
a4b5c6d HEAD@{1}: commit: Experimental feature v3
e7f8g9h HEAD@{2}: commit: Experimental feature v2
i0j1k2l HEAD@{3}: commit: Experimental feature v1
9a8b7c6 HEAD@{4}: checkout: moving from main to 9a8b7c6

The commits at HEAD@{1} through HEAD@{3} are your detached work. To save them, create a branch pointing to the most recent commit:

git branch experimental-findings f1e2d3c

Or use the relative reference directly:

git branch experimental-findings HEAD@{1}

Now you have a proper branch pointing to all that work, and you can merge or rebase it as needed.

Scenario 3: Recovering a Deleted Branch

You deleted a branch with git branch -D feature/payments and need it back. The branch's reflog is deleted along with the branch, but HEAD's reflog may still contain clues if you recently checked out that branch. More reliably, you can use git reflog with the --all flag or search for the branch's last commit.

First, try to find the commit hash from the HEAD reflog where you last interacted with the branch:

git reflog | grep -B 2 -A 2 "feature/payments"

If that doesn't work, Git still retains the branch's reflog entries temporarily. You can try:

git reflog show feature/payments

This often works even after deletion because Git doesn't immediately expire branch reflogs. If successful, you'll see the branch's tip commit. Recreate the branch:

git branch feature/payments <commit-hash>

As a fallback, use git fsck to find dangling commits, then inspect them to identify your lost branch tip:

git fsck --lost-found
git show <suspicious-commit-hash>

Scenario 4: Undoing a Botched Rebase

An interactive rebase went wrong — you accidentally dropped commits, squashed things incorrectly, or the rebase simply produced a mess. You want the exact state of your branch before the rebase began.

The reflog records both the start and finish of a rebase. Look for entries like:

git reflog show feature/refactor

You'll see:

c4d5e6f feature/refactor@{0}: rebase (finish): ...
a1b2c3d feature/refactor@{1}: rebase (pick): ...
e4f5g6h feature/refactor@{2}: rebase (pick): ...
i7j8k9l feature/refactor@{3}: rebase (start): returning to refs/feature/refactor
m0n1o2p feature/refactor@{4}: commit: Pre-rebase state   <-- this is what you want

Find the commit just before the rebase (start) entry — that's your pre-rebase branch tip. Reset your branch to that commit:

git checkout feature/refactor
git reset --hard feature/refactor@{4}

Or use the specific commit hash:

git reset --hard m0n1o2p

Your branch is now exactly as it was before the rebase. You can try the rebase again with more careful planning.

Scenario 5: Recovering an Amended Commit's Original Version

You amended a commit and now need the original version. The reflog captured both states:

git reflog

Look for consecutive commit entries where one is an amend:

d4e5f6g HEAD@{0}: commit (amend): Add payment validation
a1b2c3d HEAD@{1}: commit: Add payment validation   <-- original commit

The hash a1b2c3d is the original commit before amending. You can create a branch pointing to it, compare it, or cherry-pick it:

git branch original-commit a1b2c3d
git diff a1b2c3d..d4e5f6g

If you want to undo the amend and go back to the original, simply reset:

git reset --soft HEAD@{1}

This moves HEAD back to the original commit while keeping your working directory and index intact, effectively un-amending while preserving your staged changes.

Scenario 6: Recovering a Dropped Stash

You dropped a stash entry with git stash drop or cleared all stashes with git stash clear. The stash's reflog retains the commit hashes.

git reflog show stash

You'll see entries like:

a1b2c3d stash@{0}: WIP on main: 9a8b7c6 Fix login bug
e4f5g6h stash@{1}: WIP on feature: i7j8k9l Add export

To recover a dropped stash, create a branch from its commit hash:

git branch recovered-stash a1b2c3d

Then you can inspect the branch, cherry-pick from it, or apply its changes:

git checkout recovered-stash
git switch main
git cherry-pick a1b2c3d

After confirming the recovery, you can delete the temporary branch:

git branch -D recovered-stash

Advanced Reflog Techniques

Using Timed References for Precision

The reflog supports time-based qualifiers that let you reference states at specific moments. This is incredibly powerful when you know when a mistake happened:

git checkout HEAD@{2.hours.ago}
git show main@{yesterday}
git diff HEAD@{2024-01-15T09:30:00}
git reset --soft HEAD@{30.minutes.ago}

These time qualifiers work with any ref and resolve to the nearest reflog entry at or before the specified time. They're perfect for undoing changes you made "about an hour ago" without hunting through reflog output.

Combining Reflog with git log for Context

When recovering commits, you often need more context than just a hash. Pipe reflog output into git log to see full commit messages:

git log --oneline $(git reflog --pretty=%H | head -10)

Or use the reflog's verbose format directly:

git reflog --format="%h %gd %gs %s" --date=short

Expiring and Managing the Reflog

By default, reflog entries expire after 90 days for unreachable commits and never for reachable ones. You can tune this with configuration:

# Set reflog expiration to 180 days
git config gc.reflogExpire 180.days.ago

# Never expire reflog entries
git config gc.reflogExpire never

# Set expiration specifically for unreachable commits
git config gc.reflogExpireUnreachable 30.days.ago

You can also manually expire entries with:

git reflog expire --expire=30.days.ago --all

Be cautious with manual expiration — once expired entries are removed and garbage collected, recovery becomes impossible without external backups.

Best Practices for Reflog Recovery

Conclusion

The Git reflog is your local time machine — an automatic, low-overhead journal that tracks every significant movement of HEAD and branch pointers. Whether you've accidentally hard-reset away a week of work, lost commits on a detached HEAD, deleted a branch prematurely, or botched an interactive rebase, the reflog provides a reliable path back to safety. By learning to read reflog output, use relative and time-based references, and follow a calm, methodical recovery workflow, you transform what could be a catastrophic data loss into a minor, reversible misstep. The key is knowing the tool exists, keeping it well-configured with generous expiration policies, and practicing recovery in low-stakes situations so you're ready when real disaster strikes. With the reflog in your toolkit, you can work more boldly, experiment freely, and recover gracefully from almost any local Git mishap.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles