GitRecovery with git reflog

Recovery with git reflog

The reflog is Git's black-box flight recorder. Every time HEAD or a branch moves — for any reason, including resets, rebases, checkouts, amends, and merges — Git appends an entry to a local log. When something goes wrong, the reflog tells you the exact SHA of every state your branch ever held, so you can rewind to a safe one.

What the reflog is (and is not)
  • It is local — never pushed, never shared. Each clone has its own reflog.

  • It is a record of pointer movements, not commits. A reset --hard adds a reflog entry without creating a commit.

  • It is not part of the commit graph. git log will not show reflog entries; you must use git reflog (or --walk-reflogs).

  • Entries expire by default after 90 days for reachable commits and 30 days for unreachable ones.

Looking at the reflog

Read the log of HEAD movements

Bash
git reflog
# b3f4a2c (HEAD -> main) HEAD@{0}: commit (amend): Fix login
# 9c1a2b3                HEAD@{1}: commit: Fix logn vlaidation
# 7f8d9e0                HEAD@{2}: reset: moving to HEAD~1
# 5a6b7c8                HEAD@{3}: commit: WIP — broken
# 3a4b5c6                HEAD@{4}: checkout: moving from feature to main

# Show per-branch reflog
git reflog show main
git reflog show feature

# Show the reflog with full commit data
git log -g --oneline
git log --walk-reflogs --pretty=fuller
Addressing reflog entries
The `HEAD@{N}` syntax (or `<branch>@{N}`) means *“where HEAD was N moves ago.”* You can also use time-based selectors.

Ways to name a past state

Bash
HEAD@{0}            # current
HEAD@{1}            # one move ago
HEAD@{5}            # five moves ago

HEAD@{yesterday}    # where HEAD was 24 hours ago
HEAD@{2.hours.ago}  # two hours ago
HEAD@{2026-05-19}   # a calendar date

main@{1}            # the previous tip of main
main@{0}            # current tip of main
The recovery recipe

Undo a disastrous reset

Bash
# You ran a bad command
git reset --hard HEAD~5      # oh no — wiped 5 commits of work

# Step 1: find the SHA before the disaster
git reflog
# 1a2b3c4 HEAD@{0}: reset: moving to HEAD~5
# 9f8e7d6 HEAD@{1}: commit: the work I just destroyed   ← we want this

# Step 2: go back
git reset --hard HEAD@{1}
# Or by SHA, which is safer if the reflog has shifted:
git reset --hard 9f8e7d6

# Step 3: confirm
git log --oneline
Retention and pruning

How long reflog entries last depends on two config knobs and whether git gc has run.

Reflog expiry settings

Bash
# Defaults
git config --get gc.reflogExpire             # 90.days
git config --get gc.reflogExpireUnreachable  # 30.days

# Keep entries longer
git config --global gc.reflogExpire 200.days
git config --global gc.reflogExpireUnreachable 90.days

# Manually expire entries older than 30 days
git reflog expire --expire=30.days --all

# Force-prune unreachable objects immediately (DESTRUCTIVE!)
git reflog expire --expire-unreachable=now --all
git gc --prune=now
Warning
Running `git gc --prune=now` after expiring the reflog will permanently delete unreachable objects. Past that point, no recovery is possible — not even with `fsck`. Avoid running these commands until you are certain you do not need anything from your recent history.
When the reflog can save you
  • git reset --hard to the wrong commit.

  • git commit --amend that overwrote a commit you actually liked.

  • git rebase that produced a mess — even mid-rebase you can git reset --hard ORIG_HEAD or check the reflog.

  • Deleted a local branch with git branch -D feature — the reflog still has its last position.

  • git checkout (old-style) that overwrote uncommitted edits — only if they were committed at some point.

When the reflog cannot save you
  • Working-tree edits that were never staged or committed (e.g., after git restore <file>).

  • Untracked files removed by git clean.

  • A commit that was deleted more than 30/90 days ago and git gc has since pruned it.

  • Anything that happened in a different clone — reflogs are not shared.

  • After git gc --prune=now purges unreachable objects.

Branch reflog vs HEAD reflog

Two views of the same data

Bash
# Every place HEAD has been (across branches too)
git reflog
git reflog show HEAD

# Only the positions main has held
git reflog show main

# Useful when HEAD jumped around (checkouts, detached HEAD)
# but you only care about a specific branch&apos;s history.
Mental model
Think of the reflog as a private journal Git keeps in `.git/logs/` — one entry per branch tip movement, one entry per HEAD movement. It does not change the commit graph, it just lets you address past tip positions by name.
Tip
“The reflog has saved my career” is a real sentence said by real engineers. The first time something seems to have vanished, type `git reflog` before anything else. Nine times out of ten, the commits you thought were gone are sitting one `git reset --hard HEAD@1` away.