Git · Lesson 2 · 30 min

Git: undo anything

Everyone makes git mistakes: a typo in a commit message, a file left out, a change that broke the site, a reset that seemed to delete a week of work. The good news is that git almost never forgets anything. This lesson is the map: which undo to use for which mistake, and the one rule that keeps your teammates happy.

You will learn

  • Throw away changes, and unstage files, with restore
  • Fix your last commit with commit --amend
  • Undo a commit that's already pushed, safely, with revert
  • The three kinds of reset: soft, mixed and hard
  • Rescue “lost” commits with reflog

Which undo do I need?

Find your mistake in the left column. Git works the same on Rocky and Ubuntu, so every command here is the same on both.

The mistakeThe fixSafe after a push?
I changed a file and want the last committed version backgit restore FILEYes (nothing was committed)
I staged a file by mistakegit restore --staged FILEYes
Typo in my last commit message, or I forgot a filegit add FILE, then git commit --amendNo
My last commits were a bad idea, and I haven't pushed themgit reset HEAD~1 (soft, mixed or hard: see below)No
A commit that's already pushed broke somethinggit revert HASHYes: that's what it's for
I need an old version of one filegit restore --source=HASH FILEYes
I reset or deleted something and my commits are gone!git reflog, then git reset --hard HEAD@{N}Yes

The golden rule: don't rewrite pushed history

--amend and reset don't edit commits. They make new commits and throw the old ones away. That's fine while the commits only exist on your computer. Once you've pushed, your teammates have the old commits too, and rewriting them makes their next pull a mess (and git will refuse your push anyway).

For anything already pushed, use git revert. It adds a new commit that does the exact opposite of the old one. History stays honest: anyone can see the mistake and the fix.

$ git revert 4e1f0c2
[main 9b3d7aa] Revert "Add a big red banner"
 2 files changed, 6 deletions(-)

On a real computer, git first opens your editor with the message Revert "…" ready to go. Save and close it (in nano: Ctrl+O, Enter, Ctrl+X), or add --no-edit to skip the editor.

Fixing your last commit: --amend

Forgot a file, or spelled the message wrong? If you haven't pushed yet, fold the fix into the last commit instead of adding a “fix typo” commit:

Same on both
git add contact.css                          # the file you forgot (if any)
git commit --amend -m "Add contact page"     # replaces the last commit
git commit --amend --no-edit                 # same, but keep the old message

The three kinds of reset

git reset moves your branch back to an earlier commit. The difference is what happens to the work in the commits you're stepping back over:

CommandBranch moves backStaging areaYour filesUse it when
git reset --soft HEAD~1✓kept, stagedkept“I want to redo that commit”
git reset HEAD~1 (mixed, the default)✓emptiedkept“Uncommit it, I'll pick what to add again”
git reset --hard HEAD~1✓emptiedthrown away“That was a dead end, get rid of it”

HEAD means “the commit I'm on”, HEAD~1 is the one before, HEAD~3 is three back. You can use a hash from git log --oneline instead.

--hard really means it

reset --hard also throws away changes you haven't committed, and those are gone for good. Committed work can still be rescued (next section). Run git status first, and commit or git stash anything you want to keep.

The safety net: git reflog

Git keeps a diary of everywhere your HEAD has been: every commit, reset, switch and merge, for about 90 days. A commit you “lost” with reset is still there, it just has no branch pointing at it.

$ git reflog
2b81c4e (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
9b3d7aa HEAD@{1}: revert: Revert "Add a big red banner"
5d0e6f1 HEAD@{2}: commit (amend): Add contact page
…

Read it from the top: HEAD@{1} is where you were one move ago. Go back with git reset --hard HEAD@{1}, or keep both with git switch -c rescue HEAD@{1}.

“I committed a password!”

If you haven't pushed: remove it from the file, add the file to .gitignore if needed, and git commit --amend (or reset) so the secret never leaves your computer.

If you have pushed, treat it as leaked, because bots scan public repositories for keys within minutes. First change the password or revoke the key. That's the real fix. Then remove it from the code with a normal commit. Scrubbing it out of the history (with a tool like git filter-repo) is a later clean-up, and it rewrites history for everyone, so it's a team decision.

Try it: a messy morning on the club website 🔧

Your site/ repository is shared with the club through /srv/git/site.git. Yesterday you pushed a banner that everyone hates. This morning you committed a contact page (with a typo in the message, and you forgot its CSS file), and then you broke index.html while testing something.

Quick check

1. A commit you pushed an hour ago broke the website. Your teammates have already pulled it. What's the right undo?

2. You want to undo your last (unpushed) commit but keep all its changes staged, ready to commit again. Which one?

3. After a git reset --hard, three commits seem to have vanished. Where do you look?

Finished the missions and the quiz? Mark it done to track your progress.