Quick Answer

git reflog shows a local, private history of every place HEAD has pointed on your machine, including commits that no longer appear in git log after a reset or rebase. Nothing is actually deleted right away; the commit object still exists until Git's garbage collector eventually removes it. Find the lost commit's SHA in the reflog, then git reset --hard (or cherry-pick it) to get it back.

What Reflog Actually Tracks

git log shows the commit history reachable from your current branch. git reflog shows something different: a chronological log of every place HEAD has pointed on your local repository, every commit, checkout, reset, rebase and merge, in the order they happened on your machine.

This matters because commands like git reset --hard don't delete commits immediately. They move the branch pointer (and HEAD) somewhere else. The old commit object is still sitting in .git/objects, unreferenced by any branch, but very much still there, usually for at least 30 days by default before Git's garbage collector considers removing it.

Reflog is what lets you find that orphaned commit again: a record of the SHA HEAD used to point to, before you moved it.

It's local only, living in .git/logs, and it's never pushed, fetched, or cloned. It won't help you recover someone else's mistake on a shared branch, only your own history on your own machine.

Losing a Commit, for Real

Two commits on main, the second one meant to be kept:

$ git log --oneline
7478358 Add important feature
9426889 Initial commit

Now a git reset --hard one commit back, maybe typed in a hurry, maybe copy-pasted from the wrong terminal tab:

$ git reset --hard HEAD~1
HEAD is now at 9426889 Initial commit

The file's content confirms the feature commit is gone from the working tree, and git log agrees:

$ cat notes.txt
version 1

$ git log --oneline
9426889 Initial commit

As far as git log is concerned, "Add important feature" never existed. This is the moment that causes panic: the commit looks permanently gone, and a lot of people's next move is to start retyping the lost work from memory.

Finding It in the Reflog

It isn't gone. git reflog shows every move HEAD has made, most recent first:

$ git reflog
9426889 HEAD@{0}: reset: moving to HEAD~1
7478358 HEAD@{1}: commit: Add important feature
9426889 HEAD@{2}: commit (initial): Initial commit

Read it bottom to top for the actual chronology: the initial commit, then "Add important feature" landing at SHA 7478358, then the reset that moved HEAD back to 9426889. The commit that git log refuses to show is sitting right there in plain text, with its full SHA available via git rev-parse if you need more than seven characters.

Each line is labelled with what happened: commit, reset, checkout, rebase, merge. That's usually enough on its own to work out which entry you want, even in a reflog with dozens of entries from a long session.

Recovering It

With the SHA in hand, recovery is a normal reset --hard, just pointed forward instead of back:

$ git reset --hard 7478358
HEAD is now at 7478358 Add important feature

$ cat notes.txt
version 2 - important feature

$ git log --oneline
7478358 Add important feature
9426889 Initial commit

The file content and the commit are both back exactly as they were, and git log shows the recovered commit sitting on top of main again as if the reset never happened. If you only want the change without moving your current branch pointer wholesale, say you've made other commits since that you want to keep, git cherry-pick 7478358 replays just that commit on top of wherever you are now, which is often the safer choice once other work has piled up in the meantime. Either way, the reflog entry that pointed at it stays put, so nothing stops you from doing this more than once if you second-guess the recovery.

What Reflog Won't Save You From

Reflog is not a full backup system, and it has real edges:

  • It's local. Cloning a repository gives you none of its reflog. If the only copy of a commit exists in someone else's reflog, you can't reach it.
  • Entries expire. Unreachable commits are kept for 30 days by default (gc.pruneExpire), reachable ones for 90 (gc.reflogExpire). After that, git gc can remove them for good.
  • It only protects what existed locally. Reflog helps recover a commit you had checked out at some point; it does nothing for a commit you never actually created.
  • Force-pushing doesn't touch it, but it also doesn't restore a branch on the remote. Reflog fixes your local HEAD, not what's sitting on GitHub.
  • A fresh clone starts with an empty reflog too, since it's rebuilt from scratch by each local repository's own operations rather than copied in, so a brand-new checkout offers zero safety net until it accumulates its own history.

Treat it as what it is: a very good local undo log, not a substitute for backups or for careful use of --hard in the first place. It has saved plenty of people from a bad afternoon, but it was never designed to be anyone's only copy of anything.

Frequently Asked Questions

What is git reflog? A local log of every place HEAD has pointed in your repository, every commit, checkout, reset and rebase, kept separately from the commit history that git log shows.
Can reflog recover a commit after git reset --hard? Yes, as long as the commit object hasn't been garbage-collected yet. Find its SHA in git reflog, then run git reset --hard or git cherry-pick to bring it back.
How long do reflog entries last? By default, 30 days for commits no longer reachable from any branch, and 90 days for reachable ones, controlled by gc.pruneExpire and gc.reflogExpire.
Does reflog work after cloning a repository? No. Reflog is stored locally in .git/logs and is never included in a clone, fetch, or push. It only protects history on the machine where it happened.
Is reflog a substitute for backups? No. It only helps with local history you already had checked out at some point, expires eventually, and does nothing for a remote branch someone force-pushed over.