You ran git reset --hard. Here's how to get your work back
The specific feeling of running git reset --hard and watching an afternoon vanish is
one almost every engineer has had exactly once, and then never forgets. The good news is that git
usually still has your work. The bad news is that finding it requires knowing which of several
unrelated mechanisms applies to your particular disaster.
This is the practical guide, ordered by what actually happened to you.
You committed, then reset --hard past it
This is the recoverable case. Your commits still exist as objects; only the branch pointer moved. The reflog remembers where it was:
git reflog
# find the entry just before the reset, e.g. HEAD@{3}
git reset --hard HEAD@{3}If you'd rather inspect before committing to it, git show HEAD@{3} or
git checkout -b rescue HEAD@{3} gets you a branch to look at safely.
You never committed — the changes were only in the working tree
Here the news is worse. reset --hard overwrites tracked files with the committed
state, and uncommitted changes that were never staged are simply gone from git's perspective. Two
things sometimes save you:
- Your editor's local history. JetBrains IDEs keep a Local History; VS Code has Timeline. Both frequently hold the last few hours and are the most common successful recovery route.
- Staged-but-not-committed content. If you had run
git add, the content is in the object database as a dangling blob:git fsck --lost-foundthen read the files in.git/lost-found/.
You dropped or cleared a stash
Stashes are commits, so they survive being dropped until garbage collection:
git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk # or, more simply git fsck --unreachable | grep commit git show <sha> # inspect candidates git stash apply <sha>
You deleted a branch that was never pushed
The reflog is per-branch, and deleting the branch deletes its reflog — so git reflog
alone won't show it. You're back to git fsck --unreachable and reading through dangling
commits by hand.
A rebase or merge resolution went wrong
git rebase --abort works while the rebase is in progress. Once it has finished and
you dislike the result, you're in reflog territory again: ORIG_HEAD points at where you
were before the rebase started, so git reset --hard ORIG_HEAD is usually the fastest
undo. If you'd already resolved conflicts by hand and lost that work, it is gone — conflict
resolutions are never stored anywhere until they're committed.
The pattern underneath all of this
Notice what these have in common. Five different accidents, five different recovery mechanisms — reflog, ORIG_HEAD, fsck for dangling blobs, fsck for unreachable commits, editor local history — and you must correctly diagnose which category you're in before you can begin. Some cases are trivial. Some are unrecoverable. Nothing tells you which one you're in.
That isn't a flaw in git's implementation; it's a consequence of the reflog being a log of where branches pointed rather than a log of what you did. Operations that don't move a ref leave no trace, which is precisely why uncommitted work and hand-resolved conflicts fall through the floor.
What it looks like when the log records operations instead
This is the problem kal was designed around. Every mutating operation — snapshot, merge, reset, branch delete, tag, even a previous undo — appends an entry recording the complete state of every branch and tag before and after. Recovery isn't a diagnosis followed by the right incantation; it's one command:
kal ops # every operation, with before/after state kal undo # reverse the last one kal undo 7 # or a specific one
Two properties follow from that design and matter more than the syntax. Snapshots are never deleted, so undo is itself an operation you can undo — pressing it twice returns you exactly where you started. And garbage collection is aware of the log, so it will not prune an object that any past operation could still need. The safety net has no holes in it, which is a different claim from "there is usually a way."
What to do right now, in git
If you're reading this mid-incident: don't run anything else destructive, especially not
git gc, and work through the sections above in order. Objects survive roughly two weeks
by default, so you have time.
If you're reading it calmly: commit more often than feels necessary. Almost every unrecoverable case in this article is unrecoverable for the same reason — the work was never committed. A cheap checkpoint is the difference between an inconvenience and an afternoon.
Try kal on a repository you already have
kal runs alongside git — kal init inside an existing repo changes nothing until you
ask it to. Snapshots record intent and human-vs-AI authorship, every operation is undoable, and
kal git sync mirrors everything back into git.