← back to the section

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.

one unwanted commit C3 — two ways to undo it git reset --hard HEAD~1 C3 C1 C2 out of history main git reflog → d4e5f6 HEAD@{1}: commit: C3git reset --hard d4e5f6 — C3 is back git revert c3d4 C1 C2 C3 C4 undoes C3 main

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: restore is 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.