"I Lost Everything" (Spoiler: You Probably Did Not)
Take a breath. If the work you are missing was ever committed -- even once, even on the wrong branch, even in a commit you later "deleted" -- it is almost certainly still sitting in your repository right now. Git is far better at keeping things than at losing them, and the next ten minutes of reading will very likely end with your code back on screen.
Here is the reassuring truth about how Git works internally: Git almost never deletes anything immediately. Every commit you make is stored as an object in the .git directory, identified by its SHA. When you run git reset --hard, delete a branch, or rewrite history with a rebase, Git does not erase those commit objects. It only moves pointers -- branch names and HEAD -- so they no longer point at them. The commits become "unreachable", which means no branch or tag leads to them anymore, but the objects themselves stay on disk for weeks before garbage collection even considers touching them.
So the problem is not that your commits are gone. The problem is that you no longer have a name for them. You need the SHA. And Git keeps a journal that has been quietly recording every SHA your repository has visited: the reflog.
What the Reflog Actually Is
The reflog (reference log) is a local, per-machine journal of where HEAD and each branch tip have pointed over time. Every time HEAD moves -- because you commit, checkout, merge, rebase, reset, or amend -- Git appends a line to this journal. It is your repository's flight recorder.
To see it, run:
git reflog
The output looks like this:
e4f5a6b HEAD@{0}: reset: moving to HEAD~3
a1b2c3d HEAD@{1}: commit: add payment validation
9f8e7d6 HEAD@{2}: commit: refactor checkout flow
3c4d5e6 HEAD@{3}: checkout: moving from main to feature/payments
Read it from top to bottom as "most recent first". Each line shows the SHA that HEAD pointed to, a positional reference like HEAD@{1}, the operation that moved HEAD, and a short description. The HEAD@{n} syntax means "where HEAD was n moves ago": HEAD@{0} is where you are now, HEAD@{1} is one operation back, and so on. You can use these references in any command that accepts a commit, which is exactly what makes rescue operations so direct.
In the example above, the story is clear: you made two commits (HEAD@{2} and HEAD@{1}), then a reset moved HEAD three commits back. Those two commits are not lost -- they are right there at a1b2c3d.
HEAD is not the only reference with a log. Each branch keeps its own:
git reflog show feature/payments
This shows every position the tip of feature/payments has occupied -- useful when you want the history of one branch without the noise of every checkout you ever made.
Rescue Scenarios, Step by Step
1. You went too far with git reset --hard
You meant to undo one commit and wiped out three, or you reset to entirely the wrong place. The reflog recorded where you were immediately before the reset, and that position is HEAD@{1}:
git reset --hard HEAD@{1}
That is the whole fix. The reset itself was just another HEAD movement, so moving HEAD back to its previous position restores everything. If you have done other things since the bad reset, run git reflog first, find the line just before the reset entry, and reset to that SHA instead. For a broader tour of undo strategies, see how to undo a Git commit.
2. You deleted a branch
Deleting a branch deletes the pointer, not the commits. If the branch was recently checked out or committed to on this machine, its tip is in the HEAD reflog. Find it:
git reflog | grep "feature/payments"
Look for the last commit made on that branch, or the last checkout: moving from feature/payments entry -- the SHA on that line is the branch tip. Then recreate the branch pointing at it:
git branch feature/payments a1b2c3d
The branch is back, identical to the moment it was deleted. As a bonus, when you delete a branch Git prints its tip SHA (Deleted branch feature/payments (was a1b2c3d)) -- if that message is still in your terminal scrollback, you can skip the reflog search entirely.
3. A rebase went wrong
A rebase rewrites commits, and halfway through a conflict-riddled one it can feel like your branch is mangled beyond repair. It is not: the pre-rebase state is in the reflog. If the rebase is still in progress, the cleanest exit is git rebase --abort. If it already finished and you hate the result, find the entry from before it started:
git reflog
# look for: "rebase (start)" -- the entry just BELOW it
# is where your branch was before the rebase
git reset --hard HEAD@{5}
(Replace {5} with whatever position the pre-rebase entry occupies in your reflog.) Your branch is exactly as it was. If rebases regularly put you in this position, our guide to interactive rebase without fear covers how to make them routine instead of risky.
4. A commit vanished after --amend
git commit --amend does not edit a commit -- it creates a new one and moves the branch to it. The original is unreachable but intact, and it is sitting at HEAD@{1} right after the amend. To inspect it:
git show HEAD@{1}
If you amended the wrong commit or need the original back, reset to it or cherry-pick it onto wherever it belongs.
5. You committed on a detached HEAD, then switched away
You checked out a specific commit or tag, made commits there in a detached HEAD state, then checked out a branch -- and your commits seemingly evaporated. Git even warns you about this when you switch away, printing the orphaned SHA. The reflog has it regardless:
git reflog
# find the last commit you made before the
# "checkout: moving from <sha> to <branch>" entry
git branch rescued-work e4f5a6b
Your detached commits now live on a real branch, and you can merge or rebase them wherever they need to go.
Inspect Before You Restore
Before pointing anything at a recovered SHA, look at it. Confirm it is the commit you think it is:
git show a1b2c3d
This prints the commit message, author, date, and full diff. If you want to browse the reflog with full commit details instead of one-line entries, use the log's reflog mode:
git log -g
And here is the safest habit of all: instead of resetting your current branch to a recovered commit, create a temporary branch on it first:
git branch rescue a1b2c3d
Now the recovered work has a permanent, reachable name. Nothing about your current branch changed, garbage collection can never touch the rescued commits, and you can inspect, diff, and merge at your leisure. A branch is free; use one whenever you are not 100% sure.
The Limits, Honestly
The reflog is a remarkable safety net, but it has real boundaries, and you should know them before you need them:
- It is strictly local. The reflog lives on your machine and is never pushed, pulled, or cloned. A fresh clone starts with an empty reflog. If you lost commits on your laptop, the reflog on your laptop is the one that matters -- a colleague's clone cannot help, and neither can the server's copy.
- Entries expire. By default, reflog entries for commits still reachable from a branch expire after 90 days, and entries for unreachable commits expire after 30 days. After expiry, garbage collection can delete the unreachable objects for real. In practice this is plenty of time -- but it means the reflog rescues last month's mistake, not last year's.
- It cannot recover what was never committed. Uncommitted changes destroyed by
git reset --hardorgit checkout -- <file>were never Git objects, so no journal recorded them. The one partial exception: files that were at least staged withgit addexist as blob objects, andgit fsck --lost-foundcan dig those dangling blobs out into.git/lost-found/-- content without filenames or history, but better than nothing. It is a last resort, not a workflow. - Dropped stashes have their own escape hatch. A stash you dropped or cleared is not in the HEAD reflog, but the stash commits often survive as dangling objects.
git fsck --unreachable | grep commitplusgit showon the candidates can find them. If you use stash a lot, our guide to git stash covers the safer workflows.
Prevention: Make Future Panics Boring
Every scenario above had one prerequisite: the work was committed. That leads to the only prevention advice that matters: commit early and commit often. Small, scrappy, work-in-progress commits cost nothing -- you can squash, reword, or reorder them later. The moment something is committed, it is in the object database and the reflog has its back. The moment it is not, you are relying on luck.
For quick context switches where a commit feels too heavy, use stash rather than juggling uncommitted changes across checkouts -- a stash is also a real object Git can find again.
Where GitSquid Fits (and Where the CLI Wins)
Honest answer: the reflog is one of those places where the command line is the right tool. GitSquid does not have a reflog browser, and when you need git reflog, you should open a terminal and use it -- the commands in this article are the recovery path.
What GitSquid does instead is make the surrounding workflow safer, so you reach for the reflog less often. The file timeline (right-click any file and choose Show history) gives you a visual, per-file history, which often answers "where did that code go?" without any recovery at all. Destructive operations like reset and branch deletion go through explicit context menus on the commit graph, so you see exactly what you are about to do before you do it. And when you are reconstructing "what just happened", the command log (Cmd/Ctrl+Shift+L) shows every Git command GitSquid ran on your repository, with full arguments and exit codes -- which is precisely the evidence you want next to a reflog when piecing an incident back together.
Quick Reference
| Situation | Command |
|---|---|
| See where HEAD has been | git reflog |
| See where a branch tip has been | git reflog show <branch> |
Undo a bad reset --hard |
git reset --hard HEAD@{1} |
| Restore a deleted branch | git branch <name> <sha> |
| Undo a finished rebase | git reset --hard HEAD@{n} (pre-rebase entry) |
| Inspect a recovered commit | git show <sha> |
| Browse reflog with full details | git log -g |
| Park a rescue safely | git branch rescue <sha> |
| Last resort for staged-but-never-committed files | git fsck --lost-found |
The reflog turns "I lost everything" into "I misplaced a pointer for ten minutes". Commit often, check the reflog before you panic, and park recoveries on a temporary branch before touching anything else. And if you want a Git client that makes the destructive operations explicit and keeps a full log of every command it runs, Download GitSquid Free -- it will not replace your reflog, but it will help you need it less.