Skip to content
masterclass Buddy

Git · Command

git reflog

git reflog shows and manages reference logs. These are local records of every position a branch tip, HEAD or another ref has pointed to, so you can find and restore earlier states.

Last updated

How it works

Each time a ref like HEAD or a branch moves (commit, reset, rebase, checkout and so on), Git adds an entry with the old and new object IDs and a reason. Running git reflog with no subcommand defaults to show for HEAD. It lists those entries newest first, labelled ref@{n}. Those selectors, and date forms like ref@{yesterday}, work in any command that takes a revision. Other subcommands list, check, write, delete, drop or expire entries.

Syntax

git reflog [show] [<log-options>] [<ref>]
git reflog list
git reflog exists <ref>
git reflog write <ref> <old-oid> <new-oid> <message>
git reflog delete [--rewrite] [--updateref] [--dry-run | -n] [--verbose] <ref>@{<specifier>}...
git reflog drop [--all [--single-worktree] | <refs>...]
git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]

Parameters

NameTypeDescription
<ref>ref nameThe reference whose reflog to show, check or change. For show, it defaults to HEAD.
<ref>@{<specifier>}reflog selectorAn exact reflog entry for delete to remove, e.g. master@{2}.
--expire=<time>timeUsed by expire: prunes entries older than this time. The default comes from gc.reflogExpire (90 days); all and never are accepted.
--expire-unreachable=<time>timeUsed by expire: prunes entries older than this time that the branch's current tip cannot reach. The default comes from gc.reflogExpireUnreachable (30 days).
--allflagFor expire or drop: work on the reflogs of all refs (for drop, across all worktrees).
--single-worktreeflagWhen used with --all, only handles reflogs from the current worktree.
--updaterefflagIf the top entry was pruned, sets the ref to the new top entry (ref@{0}). Ignored for symbolic refs.
--rewriteflagWhen an entry's predecessor is pruned, changes the entry's old SHA-1 to the new SHA-1 of the entry now before it.
--stale-fixflagPrunes entries that point to broken commits, meaning commits that are unreachable and depend on missing objects.
-n, --dry-runflagShows what would be pruned without pruning anything.
--verboseflagPrints extra information.

Returns

show prints one line per reflog entry (abbreviated hash, selector and reason). exists signals its answer with the exit code: 0 if the reflog exists, non-zero if not.

Key points

  • With no subcommand, git reflog behaves like git log -g --abbrev-commit --pretty=oneline, so it also accepts git log options such as -n or --date.
  • The HEAD reflog records more than commits: it also logs every branch switch, which makes it the best place to retrace what you checked out.
  • git reflog list prints every ref that has a reflog. It is a quick way to find out which refs you can inspect.
  • git reflog exists answers through its exit code, which makes it easy to use in shell scripts and conditionals.
  • git reflog write adds a new most-recent entry by hand. The ref must be fully qualified (e.g. refs/heads/main) and both object IDs must be full hashes of objects that exist.
  • drop removes a ref's entire reflog. delete and expire only remove some of its entries.
  • By default, entries the current tip can still reach are kept for 90 days. Unreachable entries, such as commits left behind by an amend or a rebase, are pruned after just 30 days.

Common mistakes

  • Reflogs are local only. They record what moved in your own repository, so a clone or a teammate's repo has a different reflog, and push never sends it.
  • delete and expire change history in ways you can't undo, and you normally don't need to run them yourself because git gc handles pruning. Run them with --dry-run first.
  • --stale-fix is expensive: it walks every reachable object, which costs as much as a full git prune.
  • --updateref has no effect on symbolic refs such as HEAD.

Example: Recover commits lost to a hard reset

# Oops: threw away the last two commits
git reset --hard HEAD~2

# Inspect where HEAD has been
git reflog -n 5
# e.g.
# 1a2b3c4 HEAD@{0}: reset: moving to HEAD~2
# 9f8e7d6 HEAD@{1}: commit: add feature B

# Check that the pre-reset state is the one you want
git log --oneline -3 HEAD@{1}

# Restore it
git reset --hard HEAD@{1}

# Optional: preview pruning without changing anything
git reflog expire --expire-unreachable=now --dry-run --all

# Script-friendly existence check
git reflog exists refs/heads/main && echo "main has a reflog"

The reflog still holds the commit HEAD pointed to before the reset, so HEAD@{1} lets you inspect that state and return to it.

When to use it

Undo a botched rebase or amend

Once a rebase finishes you dislike the result. Find the branch's pre-rebase entry with git reflog <branch>, then git reset --hard to that selector.

Retrieve a dropped commit

A commit was left behind after reset or checkout, and no branch points to it anymore. Find its hash in the HEAD reflog, then cherry-pick it or create a branch on it.

Retrace recent navigation

Scan the HEAD reflog to see which branches and commits you checked out earlier in the day.

Housekeeping and privacy cleanup

Before running gc, use expire or drop to remove reflog entries that still keep sensitive or bulky commits alive.

Interview questions

QWhat is the reflog and how is it different from git log?

git log walks the commit graph through parent links, and that history is shared between repositories. The reflog instead records the successive values a ref such as HEAD or a branch has held in your own repository. git reflog show is really git log -g, which walks those entries instead of commit ancestry.

Follow-up: Why can't you use ranges like A..B with git log -g? Because -g walks reflog entries, not ancestry, so commit exclusions (^commit, A..B, A...B) aren't allowed.

QHow would you recover work after git reset --hard?

Run git reflog to find the entry from before the reset, usually HEAD@{1}. Check it with git log or git show, then run git reset --hard HEAD@{1} or create a branch there. This works because the reflog still references the old commit.

Follow-up: What if the branch itself was deleted? Deleting a branch also deletes its reflog, but HEAD's reflog may still list the commits. If not, git fsck can find dangling commits as long as they haven't been pruned.

QHow long do reflog entries last, and what removes them?

git reflog expire removes them, normally when git gc runs it for you. Entries the current tip can still reach expire after gc.reflogExpire (90 days by default). Unreachable entries expire after gc.reflogExpireUnreachable (30 days by default).

Follow-up: Why does the unreachable limit default to a shorter time? Those entries usually come from amends or rebases, so they're no longer part of the project and can go sooner.

  • git log
  • git gc
  • git reset
  • git prune
  • git fsck
  • git update-ref
  • gitrevisions
  • git refs

Source: Git - git-reflog Documentation. Checked 2026-11-02.