Git Course 0%

The safety net and the reflog

Intermediate Git CLI ≈ 10 min

What you will learn

  • What the reflog records and how long it keeps it
  • How to bring back a commit after a hard reset or a deleted branch
  • Which mistakes really are unrecoverable

After this lesson you can

  • I can recover a commit I thought I had destroyed

Why this matters

Most Git panic is unnecessary. The commands that look destructive usually only move a pointer, and Git keeps a private list of every position that pointer has held. Knowing this changes how the rest of the section reads: you are not defusing a bomb, you are looking something up.

The safety net

The reflog: every position HEAD has held Two rows. The upper row is the branch as git log shows it after a hard reset: three commits ending at the guide commit, with the Windows steps commit no longer visible. The lower row is the reflog, which still lists every position HEAD held, most recent first: HEAD at zero is the reset, HEAD at one is the checkout back to main, HEAD at two is the Windows steps commit that log can no longer see. An arrow from that reflog entry back to the commit shows that git reset --hard HEAD at one brings it back. A caption says the reflog is local to your computer, covers roughly ninety days, and is why a commit is almost never lost. What git log shows after git reset --hard HEAD~1 e653def start the project 28593d2 trail list command b81702c guide and FAQ 95a0014 Windows steps — not in log main and HEAD are here What git reflog still shows b81702c HEAD@{0}: reset: moving to HEAD~1 d3b92ec HEAD@{1}: checkout: moving from docs/12-windows-install-steps to main 95a0014 HEAD@{2}: commit: docs: add Windows steps still there git reset --hard HEAD@{1} brings the commit back The reflog lives in your own repository, is never pushed, and keeps entries for about ninety days. It is why a commit you made is almost never really lost.
git log shows the current branch; the reflog still holds every position HEAD has been in, including the commit log can no longer see.

Every time HEAD moves, Git writes a line in the reflog: commits, checkouts, merges, resets, rebases, pulls. The commit objects themselves stay in the repository, unreferenced but intact, for about ninety days.

Three consequences, and they are the whole lesson:

  • A commit you made can be found again, even after git reset --hard or deleting its branch.
  • The reflog is local. It is never pushed, never fetched, and belongs to your clone alone. Your colleague's reflog cannot help you and yours cannot help them.
  • Work you never committed is not in it. Git only records what it has seen.

Reading it

Terminal
$ git reflog
b81702c HEAD@{0}: reset: moving to HEAD~1
d3b92ec HEAD@{1}: checkout: moving from docs/12-windows-install-steps to main
95a0014 HEAD@{2}: commit: docs: add Windows steps to installation guide (#12)
d3b92ec HEAD@{3}: checkout: moving from main to docs/12-windows-install-steps
d3b92ec HEAD@{4}: commit: chore: add CI configuration and contributing guide
b81702c HEAD@{5}: commit: docs: add getting started guide and FAQ
28593d2 HEAD@{6}: commit: feat: add the trail list command
e653def HEAD@{7}: commit (initial): chore: start the trailguide project

Read it as a diary, newest first:

Part Means
b81702c Where HEAD pointed after that action
HEAD@{0} The most recent position; HEAD@{1} the one before it
reset: moving to HEAD~1 What you did

So HEAD@{2} above is the moment the Windows steps commit was made. That commit is not in git log any more, because the branch was reset, but its hash is right there.

git reflog --date=relative adds "11 seconds ago" style timing, which is easier to search when you are looking for "the thing I had before lunch".

Bringing something back

Terminal
$ git reset --hard HEAD@{1}
HEAD is now at d3b92ec chore: add CI configuration and contributing guide

That is the undo for a bad reset: find the line describing the state you want, and reset to it. The same works with the raw hash, which is safer to read than a counted position:

Terminal
$ git reset --hard d3b92ec

Git also keeps ORIG_HEAD, a bookmark for where you were before the last big move (a reset, a merge, a rebase, a pull). When you have just done one thing wrong, it is the fastest answer:

Terminal
$ git reset --hard ORIG_HEAD

A branch, not just a commit

Deleting a branch deletes a name, not the commits. Git even prints the hash as it goes:

Terminal
$ git branch -D docs/12-windows-install-steps
Deleted branch docs/12-windows-install-steps (was 95a0014).

Recreating it is one command:

Terminal
$ git switch -c docs/12-windows-install-steps 95a0014
Switched to a new branch 'docs/12-windows-install-steps'

If you did not note the hash, the reflog has it, and so does git fsck --lost-found, which lists commits nothing points at any more:

Terminal
$ git fsck --lost-found
dangling commit 95a001407cde528f92b4c796b2e359e301e2b863

Per-branch reflogs

git reflog shows HEAD. Each branch has its own, which is often the clearer question to ask:

Terminal
$ git reflog show main
d3b92ec main@{0}: reset: moving to ORIG_HEAD
28593d2 main@{1}: reset: moving to HEAD~2
d3b92ec main@{2}: reset: moving to HEAD@{1}
b81702c main@{3}: reset: moving to HEAD~1
d3b92ec main@{4}: commit: chore: add CI configuration and contributing guide
b81702c main@{5}: commit: docs: add getting started guide and FAQ

"Where was main yesterday morning?" is answered by git reflog show main, and main@{yesterday} is valid syntax for it.

What really is lost

The list is short, and every item is about work Git never recorded:

Gone for good Why
Edits you never staged or committed, after git restore or git checkout -- Git never had a copy
Uncommitted edits after git reset --hard Same
Untracked files after git clean -fd Git never tracked them
A stash you dropped, after the reflog expires Recoverable for a while, then not
Commits older than the reflog's expiry, once garbage collection runs Roughly ninety days by default
Anything in a clone you deleted from disk Not a Git question

Everything else is a lookup. That is why the advice throughout this course is to commit early: a commit is the moment work becomes recoverable.

How to do it

The three commands worth memorising:

Terminal
$ git reflog
$ git reset --hard HEAD@{1}
$ git reset --hard ORIG_HEAD

Read the reflog before acting. The temptation is to type a fix immediately; thirty seconds of reading tells you which line to aim at.

Common mistakes

  • Panicking and running more commands. Each one adds a reflog entry but also moves you further from the state you want. Stop and read.
  • Using git reset --hard with uncommitted changes in the working tree, which throws them away as a side effect.
  • Expecting the reflog to be on the server. It is yours alone.
  • Assuming uncommitted work is recoverable. Only your editor's local history might have it.
  • Waiting. The reflog expires; recover today, not next quarter.

Try it yourself

Goal: destroy a commit on purpose and bring it back.

  1. In your practice repository, make a commit: change a line in docs/faq.md, git add, git commit -m "docs: test commit".
  2. Confirm it is there with git log --oneline -2.
  3. Run git reset --hard HEAD~1 and confirm with git log --oneline -2 that it is gone.
  4. Run git reflog and find the line whose message is commit: docs: test commit.
  5. Run git reset --hard <that hash> and confirm the commit is back.
  6. Now delete a branch with git branch -D and recreate it from the hash Git printed.

Expected result: the commit returns, and you have done the recovery once in calm conditions, which is the point of the exercise.

Show solution

Step 4 is the skill. The reflog line you want is almost always identifiable by its message rather than its position, which is why reading it beats counting HEAD@{n} entries. Doing this once when nothing is at stake means you will remember it when something is.

Check yourself

1. What does the reflog record?
2. You ran git reset --hard HEAD~1 and lost a commit. What now?
3. Which of these is genuinely unrecoverable?

Key terms

Reflog HEAD Commit Hash (SHA)