Your stomach drops. You typed git reset --hard, or deleted the wrong branch, or a rebase ended with half your commits missing. The log looks empty. Three days of work, gone.
Take a breath. If you ever committed that work, it is almost certainly still there. Git is much harder to destroy than it looks, and the tool that proves it is called the reflog.
What the Reflog Is
Your normal history (git log) shows the commits reachable from your current branch. The reflog is a different record: a private diary of everywhere HEAD has pointed on your machine, in order, including places you have since left.
git reflog
a1b2c3d HEAD@{0}: reset: moving to HEAD~3
9f8e7d6 HEAD@{1}: commit: add pricing page
4c5d6e7 HEAD@{2}: commit: fix navbar on mobile
b7a8c9d HEAD@{3}: commit: add contact form
A hard reset moved your branch pointer backwards. It did not delete the three commits. They are still stored, and the reflog tells you their IDs.
Disaster 1: git reset --hard Threw Away Commits
Find the entry from just before the mistake and go back to it:
git reflog
git reset --hard 9f8e7d6
You can also use the position instead of the ID:
git reset --hard HEAD@{1}
Your branch is back exactly where it was. The "lost" commits are the same commits, same IDs, nothing rewritten.
Disaster 2: You Deleted a Branch You Still Needed
git branch -D feature/checkout
# Deleted branch feature/checkout (was 3e4f5a6).
Look at that output. Git told you the ID of the commit the branch pointed at. If you missed it, the reflog still has it:
git reflog
# find the last commit you made on that branch
git checkout -b feature/checkout 3e4f5a6
A branch is only a label that points at a commit. Delete the label and the commits stay, until Git cleans them up much later.
Disaster 3: A Rebase Went Wrong
Mid-rebase mistakes feel the scariest, because rebase rewrites history. It still keeps the old version in the reflog.
Right after a rebase, ORIG_HEAD points at where you were before it started:
git reset --hard ORIG_HEAD
If you have done other things since, find the entry that says something like rebase (start) in the reflog, take the entry just before it, and reset to that.
Disaster 4: You Dropped a Stash
git stash drop removes the stash from the list, but the commit it was stored in still exists for a while. If you have the ID that was printed, apply it directly:
git stash apply 7a8b9c0
If you do not have the ID, search for unreachable commits:
git fsck --no-reflog | grep "dangling commit"
Then inspect each ID with git show <id> until you find your changes. This is slower and less certain than the reflog, so treat it as the last resort.
The One Thing You Cannot Recover
This is the part many tutorials skip.
Changes you never committed and never staged are not in the reflog, and git reset --hard deletes them permanently.
The reflog records commits. If you edited files, never ran git add or git commit, and then ran git reset --hard or git checkout . or git restore ., those edits are not stored anywhere in Git. There is nothing to recover.
A small mercy: if you did run git add, the staged file contents were saved as objects, and git fsck --lost-found may be able to find them. It is messy, but possible. Unstaged edits have no such safety net.
Why Reflog Entries Do Not Last Forever
The reflog is local to your machine, and it expires. By default, entries for commits that are still reachable are kept for about 90 days, and entries for unreachable ones for about 30 days. After that, Git's cleanup can remove the commits for good.
It also lives only in your clone. A fresh git clone has an empty reflog, and it cannot help you recover something that was lost in someone else's copy.
How to Make This Less Scary Next Time
- Commit early, even when it is messy. A commit called "wip" is a safety net. You can clean the history up later with an interactive rebase.
-
Stash before you experiment.
git stashtakes a snapshot you can come back to. -
Use
--force-with-leaseinstead of--force. It refuses to overwrite a branch if someone else pushed commits you have not seen yet. - Read what Git prints. Deleting a branch tells you the ID. Copy it somewhere before doing anything else.
-
Create a rescue branch first. Before any risky command, run
git branch backup-before-rebase. It costs nothing and gives you a label to return to.
The Rule
If you committed it, Git almost certainly still has it. Open git reflog before you do anything else. And if you are about to run a command that can discard work you have not committed, commit or stash first.
Quick drill: make a throwaway repo, create three commits, run git reset --hard HEAD~3, and get them back with the reflog. Do it once when nothing is at stake, and the real thing is not scary any more.
If you build with Next.js, my templates on pixelanas.com give you a clean starting point, which is a good first commit to have in your history.
Get the templates: https://pixelanas.gumroad.com
What is the worst thing you have ever lost to a Git command, and did you get it back? Tell me in the comments.
Anas, full-stack Next.js developer building SaaS products and premium templates. X: @ASheikh69751
Top comments (0)