Nearly every "I've lost my work in git" panic is unnecessary. git reflog records every position HEAD has ever had — every commit, reset, rebase, checkout, and amend — and keeps the record for about 90 days. It is the undo log under all of git's other undo commands.

Reading the log #

Every place HEAD has been, newest first
$ git reflog
9a8b7c (HEAD -> main) main@{0}: reset: moving to HEAD~1
f3e2d1c main@{1}: commit: fix: cache headers on media
b2c3d4e main@{2}: rebase (finish): refs/heads/feature/toc onto main

Each line is one movement: where HEAD ended up, which operation moved it, and an index. main@{1} means "where main pointed one move ago" — and every one of those positions is still a live commit you can show, diff, or check out:

Inspect and recover a "lost" commit
$ git show f3e2d1c                     # it's all there
$ git switch -c rescue f3e2d1c         # branch from it: recovered

The rescues that pay for learning this #

The hard reset you regret:

Undo a reset --hard
$ git reflog                # find the commit before the reset
$ git reset --hard main@{1}

The branch you deleted:

Recover a deleted branch
$ git reflog
b1c2d3 HEAD@{4}: checkout: moving from feature/toc to main
$ git branch feature/toc b1c2d3^      # the tip commit, rebranched

The rebase that went sideways:

Return to pre-rebase state
$ git reflog                       # find "rebase (start)" line
$ git reset --hard HEAD@{2}

In every case the pattern is identical: find the last good position in the reflog, then branch from it or reset to it.

Why this is safe to rely on #

Git never deletes reachable commits — and reflog keeps unreachable ones alive for gc.reflogExpire (90 days default) precisely so mistakes stay repairable. The record is per-ref (git reflog main, git reflog feature/x) as well as for HEAD, and it lives in .git/logs/ — local only, never pushed, which is also why it cannot save work that was never committed.

Lost thingRescue
Commit after reset --hardreflog + reset or branch
Deleted branchreflog of that branch + git branch
Pre-rebase statereflog + reset to before "rebase (start)"
Dropped stashreflog of stash — fragile, act fast
Uncommitted work, file deletedNot reflog: filesystem/editor backup only

The one real limit #

Reflog records commits, not working-tree changes. git reset --hard discards uncommitted edits permanently; reflog brings back only what was committed somewhere. The commit early habit is what makes reflog omnipotent in practice — more checkpoints, more rescue points.

The reset it rescues is reset vs revert; the archaeology it supports includes bisect. The Git series continues.