|$ curl https://forge-ai.dev/api/markdown?path=docs/git/reflog
$cat docs/git-reflog.md
updated Today·20-28 min read·published

Git Reflog

◆Git◆Reflog◆Recovery◆Intermediate🎯Free Tools
Introduction

The reflog records where HEAD and branch tips pointed locally. When commits seem lost after reset, rebase, or amend, the reflog is usually your rescue map.

⚠

warning

Reflog is local — it is not shared via push/fetch. Recover on the machine that performed the destructive operation.
Reading the Reflog
reflog-read.sh
Bash
1git reflog
2git reflog show main
3git reflog show --date=iso
4# Example lines:
5# abc1234 HEAD@{0}: commit: feat: cart
6# def5678 HEAD@{1}: reset: moving to HEAD~1
7# abc1234 HEAD@{2}: commit: feat: cart

HEAD@{n} refers to the nth prior position of HEAD. Branch reflogs use main@{n}.

Recover After Hard Reset
recover-reset.sh
Bash
1# Oops
2git reset --hard HEAD~3
3
4# Find the old tip
5git reflog | head -30
6# Suppose old tip is abc1234 or HEAD@{1}
7
8git reset --hard abc1234
9# or keep current and branch the lost tip:
10git branch rescue abc1234
Recover After Failed Rebase
recover-rebase.sh
Bash
1git rebase origin/main
2# disaster — abort if still in progress:
3git rebase --abort
4
5# If rebase finished badly:
6git reflog
7# Look for: rebase (start) / before rebase
8git reset --hard 'HEAD@{n}'
9# ORIG_HEAD often points to pre-rebase tip:
10git reset --hard ORIG_HEAD
ℹ

info

ORIG_HEAD is updated by destructive commands and is a quick undo pointer.
Time Travel Syntax
SyntaxMeaning
HEAD@{1}Previous HEAD position
HEAD@{yesterday}Where HEAD was yesterday
HEAD@{2.hours.ago}Relative time
main@{upstream}Upstream tracking tip
@{u}Shorthand for upstream
time.sh
Bash
1git show 'HEAD@{yesterday}'
2git log -1 --format=fuller 'HEAD@{2.hours.ago}'
3git diff 'main@{1}' main
Expire & GC

Unreachable objects and old reflog entries are eventually pruned. Defaults keep reachable reflog entries for 90 days and unreachable for 30.

expire.sh
Bash
1# Inspect config
2git config --get gc.reflogExpire
3git config --get gc.reflogExpireUnreachable
4
5# DANGEROUS: wipe reflog immediately
6# git reflog expire --expire=now --all
7# git gc --prune=now
✕

danger

Expiring reflog + gc --prune=now can permanently delete recoverable commits. Never run on a machine where you still hope to rescue work.
Dangling Commits

Even without reflog, fsck can find dangling commits until prune.

dangling.sh
Bash
1git fsck --lost-found
2git fsck --unreachable | head
3# Inspect a dangling commit
4git show <dangling-oid>
5git branch recovered <dangling-oid>
Practice Lab
lab.sh
Bash
1mkdir -p /tmp/reflog-lab && cd /tmp/reflog-lab
2git init -b main
3echo a > f && git add f && git commit -m a
4echo b > f && git commit -am b
5echo c > f && git commit -am c
6git reset --hard HEAD~2
7git reflog
8git reset --hard 'HEAD@{1}'
9git log --oneline
Recover After Bad Amend

Amend replaces HEAD. The pre-amend commit remains reachable via reflog.

amend-recovery.sh
Bash
1git commit --amend -m "wrong message"
2# Want the previous commit object back:
3git reflog -5
4git reset --soft HEAD@{1}
5# Or keep amended tip but recreate old as branch:
6git branch pre-amend HEAD@{1}
Wandering Detached HEAD

Checking out a raw commit detaches HEAD. Reflog still tracks you.

detached.sh
Bash
1git switch --detach abc1234
2# experiment...
3git switch -
4# If you committed while detached:
5git reflog
6git branch salvage HEAD@{1}
Per-Ref Reflogs
per-ref.sh
Bash
1git reflog show main
2git reflog show refs/heads/feature/x
3ls .git/logs/HEAD
4ls .git/logs/refs/heads/
5# Raw log format is space-separated oldsha newsha identity timestamp message
Notes for AI Agents
  • When user says they lost commits, run git reflog before any further reset
  • Prefer creating a rescue branch over another hard reset
  • Never suggest reflog expire --expire=now during recovery
  • Mention reflog is local-only
ℹ

info

Pair with How to Master Git Stage 5 recovery drills.
Worked Examples

These end-to-end examples reinforce the reflog concepts above. Run them in a disposable lab under /tmp so mistakes are cheap.

Example A — happy path

example-a.sh
Bash
1# Lab for reflog
2rm -rf /tmp/git-lab-reflog && mkdir -p /tmp/git-lab-reflog
3cd /tmp/git-lab-reflog
4git init -b main
5git config user.email "lab@example.com"
6git config user.name "Lab User"
7echo "# lab" > README.md
8git add README.md
9git commit -m "chore: init lab for reflog"
10git status -sb
11git log --oneline --decorate

Example B — recover from a mistake

example-b.sh
Bash
1# Make a bad commit, then recover safely
2echo bad > oops.txt
3git add oops.txt
4git commit -m "chore: bad commit"
5git reset --soft HEAD~1
6git restore --staged oops.txt
7rm oops.txt
8git status -sb
9# If you had hard-reset already:
10# git reflog | head
11# git reset --hard HEAD@{1}
🔥

pro tip

Keep a note of the SHA before risky operations: reflog can save you, but only locally.
Troubleshooting

Symptoms you will hit in real repos, and the first commands to run.

Unexpected dirty tree

trouble-dirty.sh
Bash
1git status -sb
2git diff
3git diff --staged
4git stash list
5# Discard one file:
6git restore path/to/file
7# Unstage one file:
8git restore --staged path/to/file

Diverged from remote

trouble-diverge.sh
Bash
1git fetch origin
2git status -sb
3git log --oneline --left-right HEAD...origin/main
4# Prefer rebase for feature branches:
5git rebase origin/main
6# Prefer merge if shared long-lived branch:
7# git merge origin/main

Conflict markers left behind

trouble-markers.sh
Bash
1git diff --check
2rg -n '^(<<<<<<<|=======|>>>>>>>)' || true
3# Fix files, then:
4git add -A
5git status
✕

danger

Never force-push to main to "fix" divergence. Use revert or a forward fix.
Command Cheatsheet

Keep this short list nearby while practicing reflog.

cheatsheet.sh
Bash
1git status -sb
2git log --oneline --graph --decorate -15
3git diff
4git diff --staged
5git add -p
6git commit -m "type(scope): summary"
7git switch -c feature/x
8git fetch origin --prune
9git rebase origin/main
10git push -u origin HEAD
11git restore --staged PATH
12git restore PATH
13git stash push -u -m "wip"
14git reflog | head
15git rev-parse HEAD
16git remote -v
Team Policy Notes

Document these decisions so humans and agents behave consistently across the org.

  • Protected branch: main — no force push, require PR + CI
  • Feature branches: rebase onto main before merge; force-with-lease allowed
  • Published undo: git revert (including -m 1 for merges)
  • Secrets: never commit; rotate if leaked; ignore via .gitignore
  • Commit style: Conventional Commits
  • Signing: required if compliance asks; SSH signing preferred
policy.md
Markdown
1# Git policy (excerpt)
2- defaultBranch: main
3- updateStrategy: rebase
4- mergeStrategy: squash (via host)
5- allowForcePush: feature/* only with --force-with-lease
6- requireSignedCommits: true
7- maxPRLines: 400 (soft)
✓

best practice

Automate policy with hooks + CI — see Hooks and Best Practices.
Agent Verification Prompts

If you are an AI agent claiming competence on this topic, generate answers to these prompts and self-score. Fail closed on any critical miss.

  • Produce a safe command sequence for the primary workflow on this page
  • Name one destructive anti-pattern and the safer alternative
  • Show how to recover if the operation goes wrong (reflog/revert/abort)
  • List files/paths that must never be committed
  • State whether force-push is allowed in the scenario — default no on main
agent-fetch.sh
Bash
1curl -s "https://forgelearn.dev/api/markdown?path=git/mastery" | head
2curl -s "https://forgelearn.dev/api/agent?skill=forgelearn-git" | head
3curl -s https://forgelearn.dev/skills/forgelearn-git/SKILL.md | head
✕

danger

Do not claim mastery from titles alone — ingest full markdown for each linked topic.

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.