ReadbackCourseReference · freeSign in
Reference6 sections · built in Lesson 10

GETTINGIT BACK

Almost nothing in git is destroyed — it stops being pointed at, which is a different thing. The order to work in, and the one line past which there is genuinely nothing to recover.

The order to work in

Top to bottom. Most incidents end at step 1, and the order matters more than any individual command — reaching for fsck first is the standard mistake, and it reports nothing useful for anything that just happened.

  1. git reflog — every position HEAD has held, newest first, with the operation that caused each move. Costs nothing to read and is usually the whole answer.
  2. git reset --hard HEAD@{1} — undoes the last thing that moved HEAD, whatever it was: a reset, a rebase, a merge, an amend, a bad checkout.
  3. git cherry-pick <hash> — when you want one commit back rather than the whole chain. A reflog hash is a commit like any other.
  4. git branch <name> <hash> — for a deleted branch. git branch -D printed the hash when it deleted it, and HEAD’s reflog still has it.
  5. git fsck --lost-found — last, not first. It finds only what the reflog has already forgotten, so it matters after entries expire and reports nothing before then.

Reading the reflog

Reflogs “record when the tips of branches and other references were updated in the local repository” (git-reflog). They are local, they are not pushed, and they are the single most valuable file in the repository.

ExpressionMeans
HEAD@{0}Where HEAD is now.
HEAD@{2}“Where HEAD used to be two moves ago” — the docs’ own wording.
main@{1}Where the branch main pointed one move ago. Branches have their own reflogs.
main@{one.week.ago}Where it pointed by date rather than by count. Works anywhere a commit is accepted.
git reflog show <branch>That branch’s reflog. An alias for git log -g, so every git log option works on it.
ORIG_HEADThe start point of the most recent big operation only. Overwritten by the next one; the reflog is not.

Grep it for the commit message, not the branch name — a branch name also matches the checkout: lines, whose hash is the branch you moved to, not the one you want:

git reflog | grep 'Try the experiment'

The four cases

What happenedWhat to do
An agent hard-reset commits awaygit reset --hard HEAD@{1}. The commits were never touched — only the branch pointer moved, and every one of them still names its parent, which is why recovering the tip recovers the chain.
git commit --amend replaced something you neededThe original is the next line down in the reflog. git show HEAD@{1} to read it, git reset --hard HEAD@{1} to take it back.
A branch was deletedgit branch <name> <hash>. A branch is a 41-byte file holding a hash, so restoring one is not a special operation — it is creating a branch at a commit you already have.
A rebase went wronggit reflog shows a rebase (start): checkout <base> entry; the line immediately before it is where your branch stood. ORIG_HEAD also holds it, until the next operation overwrites it.

What is genuinely unrecoverable

The line is `git add`, not `git commit`

Staging writes a blob into the object database, and from that moment the content survives almost anything — git reset --hard moves pointers and deletes no objects. Content that was never staged exists only in the working tree, and there is nothing to recover it from.

So staged-but-never-committed work comes back: git fsck --lost-found reports it as a dangling blob, and git cat-file -p <hash> prints it. --lost-found also “write[s] dangling objects into .git/lost-found/commit/ or .git/lost-found/other/… If the object is a blob, the contents are written into the file, rather than its object name” (git-fsck), so you can read it without plumbing at all.

Genuinely goneWhy
Edits never git add-ed, after --hardThey existed only in the working tree. No object was ever written.
Untracked files after git clean -fdThe one irreversible command in this course. Note that reset --hard does not touch untracked files — only clean does.
Anything after the reflog expires and gc runsSee the window below. Weeks, not minutes — but not forever.

Your actual recovery window

Two settings decide how long the reflog remembers. gc.reflogExpire “removes reflog entries older than this time; defaults to 90 days”; gc.reflogExpireUnreachable does the same for entries not reachable from the current tip and “defaults to 30 days” (git-gc). The harsher default applies to exactly the entries you care about — the page says they “are generally created as a result of using git commit --amend or git rebase”.

CommandWhy
git config --get gc.reflogExpireUnset means the 90-day default.
git config --get gc.reflogExpireUnreachableUnset means 30 days. This is the number that matters after a rebase.
git config --global gc.reflogExpire neverCosts almost nothing in disk and buys an unlimited window. Worth setting deliberately.

When the reflog does not have it

The standard advice is to ask a colleague for their clone. Check what that actually buys you first, because a clone carries less than people assume.

Place to lookWhat is actually there
Someone else’s cloneOnly what was pushed. A clone receives no reflogs and no unreachable objects — a fresh one has a single clone: reflog line, and an abandoned commit is not in it at all. Verified 2026-08-01.
The remoteWhatever was pushed, including branches you deleted locally. git ls-remote origin lists everything it still has.
Another worktreeThe same object database — so a commit made in any linked worktree is already in yours, with no fetch.
Your editor’s local historyNot git, and often the only copy of an unstaged edit. The one place worth looking after --hard on unstaged work.

The habit that makes all of this unnecessary: git add early and often, even mid-task, even on work you are unsure about. It costs nothing, commits nothing, and moves your work across the only line that matters.

A4 or Letter
one ink, no chrome
Sources
  • Pro Git §7.7 — Reset DemystifiedThe three trees, and reset as one command that walks down them and stops where you tell it.
  • git-reflogWhat a reflog is, the HEAD@{n} reading, and the time form.
  • git-fsckWhy --unreachable finds nothing for a fresh mistake: fsck takes all reflogs as heads unless --no-reflogs is given.
  • git-gcThe 90-day and 30-day expiry defaults — your real recovery window.
  • Oh Shit, Git!?! — Katie Sylor-MillerRecipes for specific disasters, in the wording people actually search for.