Tutorials Git & GitHub Pro Series Chapter 4

Undo Anything in Git — reset, revert, restore, stash, reflog & cherry-pick

Git + GitHubChapter 4 of the Git & GitHub Pro Series26 minMay 28, 2026Beginner

This is the chapter people wish came first, because it removes the fear. Here's the reassuring truth, and it falls straight out of Chapter 1's model:

Git almost never deletes anything. Commits are immutable snapshots, and even when you "undo" one, the snapshot usually still exists — you've just moved a label off it. There's a recovery for nearly every mistake.

Every "undo" in git is one of two moves: move a label (fast, but rewrites local history) or add a new commit that reverses an old one (safe for history others have). Keep that split in mind and the whole toolkit below makes sense.

The Playbook: Find Your Mistake, Get the Fix

Most of the time you just need the right command for the situation you're in. Here's the lookup table; the sections after explain the important ones.

"Oh no, I…"Fix
…staged a file I didn't mean togit restore --staged file
…want to throw away my edits to a filegit restore file  (discards uncommitted work)
…made a typo in my last commit message (not pushed)git commit --amend
…forgot to include a file in the last commitgit add file && git commit --amend --no-edit
…want to undo the last commit but keep the changesgit reset --soft HEAD~1
…want to undo the last commit and unstage, keep file editsgit reset HEAD~1
…want the last commit and its changes gonegit reset --hard HEAD~1  (dangerous)
…need to undo a commit that's already pushedgit revert <sha>
…committed to the wrong branchcherry-pick to the right one, then reset the wrong one
…need just one commit from another branchgit cherry-pick <sha>
…must drop everything and switch tasks right nowgit stash … later git stash pop
…think I lost a commit (bad reset/rebase)git reflog → find the SHA → recover

restore: Working Directory and Staging

git restore is the modern, purpose-built undo for uncommitted changes (it split off from the overloaded git checkout):

reset: Moving the Branch Label (the Three Modes)

git reset HEAD~1 means "move the current branch label back one commit." HEAD~1 is shorthand for "one commit before HEAD." Because branches are just labels (Ch 1), this un-commits by sliding the label backward. The three modes differ only in which of the three trees they also touch:

ModeMoves branch label?Resets staging?Resets working dir?Net effect
--softChanges stay staged, ready to re-commit
--mixed (default)Changes stay in working dir, unstaged
--hardChanges gone from everywhere
Loading diagram…

Figure 1 — The three resets, mapped to the three trees from Chapter 1. --soft touches only the label; --mixed also clears staging; --hard also overwrites your files. The further right, the more it undoes — and --hard is the only one that can lose work.

revert: The Safe Undo for Pushed Commits

git revert <sha> does not move any label or rewrite history. Instead it creates a brand-new commit that is the exact inverse of the target commit — if the old commit added a line, the revert commit removes it. The bad change is undone, but the history stays intact and forward-only.

Loading diagram…

Figure 2 — revert adds an inverse commit R on top rather than deleting C. History only ever moves forward, so it's safe even on main that everyone has pulled. This is the right tool when a bad change already shipped.

The rule of thumb: reset to undo local commits nobody has seen; revert to undo pushed commits other people already have.

cherry-pick: Grab One Commit

git cherry-pick <sha> copies a single commit onto your current branch (as a new commit with a new SHA). Two classic uses:

stash: Park Work Without Committing

Sometimes you're mid-edit and have to switch tasks now — but your changes aren't ready to commit. git stash sweeps all your uncommitted changes into a safe holding area and gives you a clean working directory:

git stash            # set aside all uncommitted changes
git switch main      # do the urgent thing on a clean tree
# ...later...
git switch my-branch
git stash pop        # bring your changes back and remove them from the stash

Think of it as a clipboard for "work in progress I'm not ready to snapshot." git stash list shows what you've parked; you can stash multiple times.

reflog: The Time Machine That Saves You

Here's the safety net almost nobody knows about, and it's why --hard and a botched rebase are rarely fatal. Git logs every move of HEAD — every commit, checkout, reset, rebase, merge — in the reflog. Even when a commit is no longer reachable from any branch (you "lost" it), the reflog still remembers where you were, and the snapshot still exists for weeks before git garbage-collects it.

git reflog
# 8367b9b HEAD@{0}: reset: moving to HEAD~1
# a1b2c3d HEAD@{1}: commit: the work I thought I destroyed
# ...
git reset --hard a1b2c3d     # or: git checkout a1b2c3d — you're back

So the recovery for "I ran reset --hard and lost a commit" or "my rebase went wrong" is: git reflog, find the SHA from before the mistake, and reset/checkout to it. The label moves back onto the snapshot that was never actually deleted.

Bonus: Stop Tracking a File You Already Committed

Committed node_modules/ or a .env by accident? Add it to .gitignore, then untrack it without deleting it from disk:

echo ".env" >> .gitignore
git rm --cached .env       # stop tracking; keep the local file
git commit -m "Stop tracking .env"

(If the file held a secret that was ever pushed, also rotate the secret — it's in history forever. .gitignore only prevents future tracking.)

Mental Model — Three Sentences

  1. Almost every "undo" is either moving a branch label (reset — fast, rewrites local history) or adding an inverse commit (revert — safe for pushed history); pick based on whether others have the commit.
  2. reset's three modes map to the three trees: --soft moves the label only, --mixed also unstages, --hard also wipes your files — only --hard can lose work.
  3. reflog records every move of HEAD, so committed work survives even a hard reset or a broken rebase — the only truly unrecoverable loss is uncommitted changes you discarded.

Try It Yourself (15 Minutes)

In a throwaway repo (so you can be reckless):

  1. Make a commit. Run git reset --soft HEAD~1 and git status — the changes are staged again. Re-commit.
  2. Make a commit. git reset --hard HEAD~1 — gone. Now git reflog, find the commit, git reset --hard <sha> — back. Feel the safety net work.
  3. Make and push a commit to a test branch, then git revert HEAD — see a new inverse commit instead of a rewrite.
  4. Start editing, then git stash, confirm a clean tree, then git stash pop to get your edits back.
  5. git cherry-pick a commit from one branch onto another and watch it arrive with a new SHA.

Where This Lands in the Series — and What's Next

That covers the core mechanics of the Git & GitHub Pro series:

  1. How git actually works — the snapshot/DAG/label model.
  2. Merge, rebase, or squash — reshaping history.
  3. GitHub like a pro — issues, branch protection, releases, the gh CLI.
  4. Undo anything — the recovery toolkit (this chapter).

You now understand not just the commands you've been typing, but the model underneath them — which means you can reason about any git situation you hit, including the ones this series didn't name. Commit early, commit often, and nothing can really hurt you.

One practical chapter remains. You've used GitHub on the free plan this whole time — so Chapter 5 answers the money question: what do Pro, Team, Enterprise, and Copilot actually unlock, what are those "usage-based" charges (Actions, Codespaces, and friends), and what's genuinely worth paying for versus what you already get for free?

Ch 3: GitHub Like a ProCh 5: GitHub Free vs Paid — Is It Worth It?
DeliveryModern Delivery PipelineCI/CD, review, runner, and deploy workflows for teams shipping apps and websites safely.Production WebProduction Web Apps SeriesProduction patterns for web apps: caching, rate limiting, webhooks, queues, cron jobs, and idempotency.WebUltimate Web Development SeriesWeb development tutorials for HTML, CSS, JavaScript, Next.js, Workers, databases, and production shipping.

Ship your apps faster

When you're ready to publish your Swift app to the App Store, Simple App Shipper handles metadata, screenshots, TestFlight, and submissions — all in one place.

Try Simple App Shipper
5 free articles remainingSubscribe for unlimited access