The main truth about Git, worth learning before your first panic: committed work is almost impossible to lose. Every mistake has a standard undo, and for the worst case there's reflog — a black box that remembers everything. Let's go through the tools from the light cases to the heavy ones.
The same unwanted commit C3 in two outcomes. On top reset --hard moves the main bookmark back to C2: the commit drops out of history but does not vanish — git reflog remembers its id, and everything comes back through it. At the bottom revert moves no bookmark: a new commit C4 with the inverse changes lands after C3, and history shows both the mistake and its undo.
Undo uncommitted changes: restore
git restore file.txt # bring the file back to what is in the index (edits WILL BE LOST)
git restore --staged file.txt # remove from the index (edits stay in the file)
git restore --source=HEAD . # bring the working files back to the last commit
The only place in Git where work is truly lost is here: uncommitted edits cannot be brought back after restore. One detail: without --source, restore takes content from the index, not the commit — a file already staged with add rolls back to what was staged. Hence the rule: when in doubt — commit first or stash it; you can always roll back later.
Stash it for a minute: stash
You need to switch urgently, but the work isn't finished:
git stash # tuck uncommitted work into the pocket
git stash list # what's in the pocket
git stash pop # bring back the last stashed item
Stash is a temporary pocket, not an archive: back on the task — pop right away. Collecting stashes for half a year is a way to forget what's in them.
Rolling back commits: reset versus revert
Two tools for different situations — the line is drawn by the question "has this already been shared with others?"
reset — rewrite local history (commits not pushed yet):
git reset --soft HEAD~1 # undo the last commit, changes stay in the index
git reset HEAD~1 # undo the commit, changes go to the working files (default)
git reset --hard HEAD~1 # undo the commit AND THROW AWAY the changes
--soft is "rebuild the commit differently", --hard is "this never happened". Treat --hard like UPDATE without WHERE: first make sure you're throwing away exactly what you think.
revert — undo publicly and honestly (commits already on the server):
git revert a1b2c3 # creates a NEW commit with the inverse changes
History isn't rewritten — it's extended with an antidote commit. This is exactly how merged-to-main changes get rolled back: a PR that broke production is undone with a revert commit in a minute — safely, with the chronicle preserved. The one-line rule: local — reset, pushed — revert.
A separate case is reverting a merge commit. It has two parents, so Git needs to know which is the mainline: git revert -m 1 <sha> undoes everything the merged branch brought in, relative to the first parent (usually main). Side effect: Git now treats those commits as merged, so merging that branch again brings nothing — to land the fixed feature, revert the revert first.
reflog: the time machine of last resort
"Did reset --hard to the wrong place", "rebase broke everything", "deleted a branch with my work" — stay calm. Reflog is a journal of every HEAD movement, including "deleted" states:
git reflog
# a1b2c3 HEAD@{0}: reset: moving to HEAD~3
# d4e5f6 HEAD@{1}: commit: that very work ← there it is!
git reset --hard d4e5f6 # return to the state before the accident
# or more carefully: git branch rescue d4e5f6 — point a branch at the found commit
Everything ever committed can be found in reflog. There are almost no real ways to lose committed work — which brings back the rule from the start of the series: commit often; a commit is insurance that can't be taken away.
In short
- Uncommitted work is the only thing that's truly losable:
restoreis irreversible. When in doubt — commit or stash. - stash is a pocket for a minute: tuck away → switch → come back → pop.
- Local history is edited with reset (soft — rebuild, hard — throw away); pushed history is undone with a revert commit. "Local — reset, public — revert".
- reflog keeps lost states for about 30 days (ordinary entries — 90): after any accident —
git reflog→ return to the right commit, and do not put it off. - Frequent commits = insurance nobody can take away.
What to read next
- Rebase and cherry-pick — operations after which reflog is especially heartwarming.
- Commits and history —
--amendand other light touch-ups. - Branching models — the final article of the series.