How to Recover Lost Git Commits Using Reflog

How to Recover Lost Git Commits Using Reflog

Soren FischerBy Soren Fischer
Quick TipHow-To & Fixesgitversion-controlrecoverydeveloper-toolscli

Quick Tip

Git reflog tracks every HEAD movement for 30 days, letting you recover commits even after destructive operations like reset or rebase.

Accidentally deleted commits, botched a rebase, or lost work after a force push? Git's reflog acts as a chronological record of every HEAD movement in a repository — every commit, checkout, merge, and reset gets tracked for at least 90 days by default. Without knowing this command exists, valuable code can vanish permanently. Here's how to use it.

What is Git reflog and how does it work?

Git reflog is a local log that tracks every time HEAD moves to a new commit. Unlike git log — which shows the commit history of the current branch — reflog captures every action: branch switches, rebases, cherry-picks, even detached HEAD state. The catch? It's entirely local. If the repository gets deleted, the reflog disappears with it. Run git reflog in any Git repository to see the chronological list of HEAD movements with timestamps and short commit hashes.

Each entry looks something like this: abc1234 HEAD@{0}: commit: add user authentication. The HEAD@{n} syntax provides a relative reference to previous states. HEAD@{0} is the current position. HEAD@{1} is where HEAD was one move ago. Worth noting: this data stays in .git/logs/HEAD — an internal file users rarely touch directly.

How do I recover a deleted branch or commit?

Run git reflog, identify the commit hash from before the deletion, then create a new branch pointing to that commit. Here's the sequence that works every time:

  1. git reflog — scan the output for the commit hash right before the "branch deleted" entry
  2. git checkout <commit-hash> — verify it's the right commit
  3. git checkout -b recovered-branch — create a branch from that point

That said, timing matters. Git's garbage collector (running git gc) eventually prunes unreferenced commits older than 90 days. For urgent recoveries, stop all Git operations immediately — the less you move HEAD around, the cleaner the reflog stays.

What's the difference between git log and git reflog?

These commands serve entirely different purposes. git log shows the commit ancestry of the current branch — a curated, logical history. git reflog shows the physical movements of HEAD — every context switch, mistake, and recovery attempt. Here's the breakdown:

Feature git log git reflog
What's tracked Commit ancestry (parent relationships) HEAD movements (all local actions)
Persistence Shared with remotes (GitHub, GitLab) Local only — not pushed
Retention Permanent (unless manually rewritten) 90 days default, then pruned
Best for Code review, release notes, documentation Recovery from mistakes

The reflog even survives operations that rewrite history. After a botched git rebase or aggressive git reset --hard, the old commits remain accessible via HEAD@{1}, HEAD@{2}, etc. — right up until garbage collection runs.

How do I undo a git rebase or reset using reflog?

Run git reset --hard HEAD@{1} (or the appropriate reflog entry) to rewind to the state before the destructive operation. The syntax works because reflog entries are immutable references to specific repository states — even when those states no longer appear in normal branch history.

Here's the thing: this technique rescues developers daily. After an accidental git reset --hard HEAD~3 (deleting three commits permanently from the branch), the reflog still contains those commits. Find the entry labeled HEAD@{1}: reset: moving to HEAD~3 — the commit hash before that line is the target. A single git reset --hard <hash> restores everything.

For interactive rebases gone wrong, the process is identical. The reflog preserves every step of the rebase with entries like rebase -i (start), rebase -i (squash), and rebase -i (finish). Pick the hash from before the rebase started, and the original branch state returns intact. Developers using Git's official documentation or the GitHub Blog's feature rundowns will find additional edge cases covered — including how to expire old reflog entries manually with git reflog expire.

One last detail: configure reflog expiration if working with sensitive code. The default 90-day retention keeps everything — including accidental commits of API keys or passwords. Run git config gc.reflogExpire 30.days to shorten this window, or git config gc.reflogExpireUnreachable never to preserve unreachable commits indefinitely. For teams using GitLab's enterprise features, server-side hooks can mirror some reflog data — though true reflog behavior remains fundamentally client-side in Git's architecture.