Undoing Mistakes
Every version control tutorial eventually arrives here, because this is the moment Git actually earns its keep: something is wrong, and you need to undo it. Maybe you edited the wrong file, staged something you shouldn't have, or committed a typo you already regret. Git has a specific tool for each stage of "how far along" the mistake is — the trick is knowing which one matches your situation.
💡 The Bottom Line
The right undo command depends on where the mistake lives: git restore for
uncommitted changes, git reset for rewriting your own local commit history
before you've shared it, and git revert for undoing a commit that's already
been pushed and shared. Reset rewrites history; revert adds new history that cancels out the
old — that difference is the whole safety story of this lesson.
First, see what's actually going on
git status
This is the single most useful command in all of Git — it tells you which files are staged, which are modified but unstaged, and which branch you're on. Run it constantly; it costs nothing and prevents most "wait, what did I just do?" moments before they happen.
git diff # unstaged changes vs. the last commit
git diff --staged # staged changes vs. the last commit
git diff shows you exactly what changed, line by line — invaluable for
double-checking before you stage or commit something you'll regret.
Undoing unstaged changes
Edited a file and want to throw the changes away completely, back to the last commit?
git restore app.js
This is the modern, purpose-built command for exactly this job. You'll also see the older equivalent in tutorials and existing scripts:
git checkout -- app.js
Both discard uncommitted edits to that file, restoring it to match the last commit. This is permanent — there's no snapshot of the discarded edits anywhere, since they were never committed.
⚠️ Watch Out
git restore and git checkout -- <file> destroy uncommitted
work with no undo. Before running either, double-check with git status and
git diff that you're really willing to lose those specific changes. If there's
any doubt, commit the work first (even with a throwaway message like "WIP") —
you can always clean the message up later, but you can't recover discarded uncommitted
edits.
Un-staging without discarding
Staged a file by mistake but want to keep the edits, just not in the next commit?
git restore --staged app.js
This moves the file back to "modified, not staged" — your edits are untouched, just no longer queued for the next commit.
Reset: soft, mixed, and hard
git reset moves your branch pointer to an earlier commit, and comes in three
strengths depending on what you want to happen to the commits and changes in between:
git reset --soft HEAD~1 # undo the last commit, keep changes staged
git reset --mixed HEAD~1 # undo the last commit, keep changes unstaged (this is the default)
git reset --hard HEAD~1 # undo the last commit AND discard the changes entirely
--soft is for "I committed too early — let me add one more thing before
re-committing." --mixed is for "un-commit this, I want to restage things
differently." --hard is for "I genuinely want this commit and its changes gone" —
and it is the one command in this lesson that can permanently destroy work with no trace.
🙋 There Are No Dumb Questions
Q: Is git reset --hard ever actually safe?
A: Yes, in one specific situation: undoing your own local, not-yet-pushed commits, when
you're certain you don't want those changes at all. It's dangerous specifically when a
commit has already been pushed and others may have pulled it — resetting locally doesn't
remove it from their copy, and force-pushing to "fix" that creates the exact shared-history
problem covered with rebase in Lesson 4. For anything already shared, reach for
git revert instead.
Revert: undo by adding, not rewriting
Once a commit is pushed and possibly pulled by teammates, don't reset it away — revert it:
git revert a1b2c3d
This creates a brand-new commit that applies the exact opposite of the changes in
a1b2c3d, leaving the original commit untouched in history. Nothing is rewritten;
anyone who pulls afterward simply sees "this change was undone" as a normal new commit —
completely safe on shared branches, which is precisely why it's the standard tool for undoing
something already pushed.
🔧 Try It Yourself
Practice the full spectrum of undo, in a scratch repo:
mkdir git-undo-practice && cd git-undo-practice
git init
echo "line one" > notes.txt
git add notes.txt
git commit -m "Add notes file"
echo "a mistake" >> notes.txt
git status
git restore notes.txt # discard the unstaged mistake
cat notes.txt # back to just "line one"
echo "line two" >> notes.txt
git add notes.txt
git commit -m "Add line two"
git revert HEAD --no-edit # undo that commit safely, by adding a new one
git log --oneline
Notice the revert shows up as a new commit on top, rather than erasing the one before it — exactly the behavior that makes it safe once work is shared.
✅ Quick Check
You need to undo a commit that you already pushed and a teammate has already pulled. What should you use, and why?
That's the whole track — staging, committing, branching, merging or rebasing, working with remotes, and undoing mistakes safely. Time for the recap.