Undoing Mistakes

Table of Contents

  1. The One Rule
  2. Throw Away Changes You Haven't Committed
  3. Unstage a File
  4. Fix Your Last Commit
  5. Undo a Commit Safely: git revert
  6. Go Back in Time: git reset
  7. Get an Old Version of One File
  8. The Safety Net: git reflog
  9. Which Undo Do I Need?
  10. Try It Yourself

1. The One Rule

Git has several ways to undo things, and they fall into two groups:

  • Safe undos add something new to history, like a commit that reverses an older one. Nothing in the past changes.
  • Rewriting undos change or remove existing commits. They give you a tidier history, but anyone who already has a copy of the old commits will run into trouble.

So the rule is:

Rewrite only what you haven't shared. Before you push commits to GitHub (chapter 8), you can fix them however you like. After you push, use the safe undo — git revert.

Everything in this chapter happens on your own computer, so you can try all of it freely.

2. Throw Away Changes You Haven't Committed

You edited index.html, it's gone badly wrong, and you just want it back the way it was at the last commit. Try it — make a mess first:

    <h1>asdfasdf BROKEN</h1>

Save. git status even suggests the fix:

  (use "git restore <file>..." to discard changes in working directory)
	modified:   index.html

So:

git restore index.html

The file is back to its last committed version. To throw away every uncommitted change in the project, use git restore ..

Careful: this is one of the few things Git can't undo. The changes you throw away were never committed, so Git never saved them. Use git diff first if you're unsure what you're about to lose.

git restore also brings back a file you deleted by accident, as long as it existed in the last commit.

3. Unstage a File

You ran git add . and staged a file you didn't mean to include. Take it out of the staging area — without losing your edits — with:

git restore --staged index.html

The changes are still in the file; they're just not in the "packing box" any more. Again, git status tells you this command whenever you have staged changes.

4. Fix Your Last Commit

Two of the most common mistakes happen right after committing:

A typo in the message:

git commit -m "Add favrite food"     # oops
git commit --amend -m "Add favorite food"

You forgot a file:

git add style.css
git commit --amend --no-edit

--amend replaces the last commit with a new one that includes whatever is staged, plus the new message (or, with --no-edit, the old message). Check git log --oneline before and after: the hash changes, because it's a brand new commit.

That makes --amend a rewriting undo. Use it freely on commits you haven't pushed yet — and not after.

5. Undo a Commit Safely: git revert

Suppose you decide the "Add favorite food" commit from the last chapter shouldn't be there. Find its hash:

git log --oneline
d9a4f21 (HEAD -> main) Make heading friendlier
6e3b8c0 Add favorite food
8b1e4d0 Add .gitignore
c47a2e9 Add where I live
3f2a1c9 Add home page

Then revert it:

git revert 6e3b8c0

Git works out the opposite of that commit — here, removing the food line — and makes it into a new commit. Your editor opens with a ready-made message, Revert "Add favorite food". Save and close it (or add --no-edit to the command to skip the editor).

git log --oneline
1c7e5a2 (HEAD -> main) Revert "Add favorite food"
d9a4f21 Make heading friendlier
6e3b8c0 Add favorite food
8b1e4d0 Add .gitignore
...

The original commit is still in history — you've simply added a commit that cancels it. Nothing was rewritten, so it's safe even for commits the whole team already has. Notice also that you could revert an older commit without touching the newer ones after it.

6. Go Back in Time: git reset

git reset moves your branch back to an earlier commit, as if the later commits never happened. It's a rewriting undo, so use it only on commits you haven't pushed.

It has three strengths. In each example, HEAD~1 means "go back one commit":

git reset --soft HEAD~1   # undo the commit, keep the changes staged
git reset HEAD~1          # undo the commit, keep the changes unstaged
git reset --hard HEAD~1   # undo the commit and DELETE its changes
CommandThe commitYour changes
--softRemoved from historyStill in the staging area, ready to commit again
(default)Removed from historyStill in your files, not staged
--hardRemoved from historyGone from your files too

The first two are gentle. They're useful when you committed too early and want to redo the commit differently — maybe split it into two.

--hard is the dangerous one. It also throws away any uncommitted changes in your files, just like git restore .. Before you run it, run git status and make sure there's nothing you want to keep.

7. Get an Old Version of One File

Sometimes you don't want to undo a whole commit — you just want one file as it was at some point in the past:

git restore --source=3f2a1c9 index.html

Now index.html in your working directory matches the very first commit. Every other file stays as it is. If you like the result, commit it; if not, git restore index.html takes you back to the latest version.

8. The Safety Net: git reflog

What if you run git reset --hard and then realise you needed those commits? They're not gone yet. Git keeps a private diary of everywhere HEAD has been, called the reflog:

git reflog
8b1e4d0 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
1c7e5a2 HEAD@{1}: revert: Revert "Add favorite food"
d9a4f21 HEAD@{2}: commit: Make heading friendlier
6e3b8c0 HEAD@{3}: commit: Add favorite food
...

Find the commit you were on before the mistake — here 1c7e5a2 — and reset back to it:

git reset --hard 1c7e5a2

Everything is back. The reflog only exists on your computer, and Git cleans it up after a few months, but for everyday accidents it saves you.

The lesson: once something is committed, it's very hard to truly lose it. The only work Git can't help with is work you never committed. One more reason to commit often.

9. Which Undo Do I Need?

SituationCommand
I messed up a file and want the last committed versiongit restore <file>
I staged something by mistakegit restore --staged <file>
Typo in my last commit message, or I forgot a file (not pushed)git commit --amend
A commit was a mistake, and it's already pushedgit revert <hash>
My last commit(s) were a mistake, not pushed yetgit reset HEAD~1
I want one file as it was in an old commitgit restore --source=<hash> <file>
I reset or amended and lost commitsgit reflog, then git reset --hard <hash>

10. Try It Yourself

  1. Discard a change. Break your <h1>, check git diff, then git restore index.html.
  2. Unstage. Edit a file, git add ., then git restore --staged it. Run git status after each step.
  3. Amend. Make a commit with a typo in the message, then fix it with --amend. Compare the hash before and after.
  4. Revert. Revert your "Add favorite food" commit and check that the line disappears from index.html and a new commit appears in git log --oneline.
  5. Reset and rescue. Run git reset --hard HEAD~2. Look at git log --oneline — two commits are missing. Now use git reflog to find where you were and git reset --hard back to it. Everything returns.

You can now experiment without fear. Next, let's make experimenting even safer with branches.

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!