Gitastic
Path › Part 4: Undo & rescue › Lesson 11 of 16
11

The undo toolbox

restore, reset, revert, amend — which one, and when.

1 Learn the idea

Git has several "undo" commands. Each one is simple, but people mix them up. They are easier to tell apart once you ask which area you want to undo, and whether anyone else has seen it yet.

Pick the tool by the situation

  • I messed up a file and want the last saved version back → git restore <file> (discards working-directory changes — gone for good).
  • I staged something by accident → git restore --staged <file> (the change stays in your folder, just not in the next commit).
  • My last commit has a typo / forgot a file → git commit --amend (replaces the last commit with a new one).
  • I want to throw away the last N local commits → git reset with --soft, --mixed (default) or --hard.
  • A bad commit is already pushed → git revert <commit> (makes a new commit that does the opposite).

The three reset modes

All three move the current branch (and HEAD) back to the commit you name. They differ in how much of the three areas they also rewind:

  • --soft: moves the branch only. The undone changes remain staged, ready to recommit.
  • --mixed (the default): moves the branch and resets the staging area. Changes remain in your working directory, unstaged.
  • --hard: moves the branch and resets staging and the working directory. Uncommitted changes are destroyed.

2 Watch it happen

Every undo, one after another

Press Play or use → to step through. Watch the graph as each command runs.

2f5672f Add menu2f5672fAdd menue375f09 Add pricese375f09Add pricesHEAD = where you are. It points at the current branch.HEAD →Branch "main" — a movable pointer (current branch: moves when you commit)main
Working directoryyour folder / editor
menu.md
prices.md
git add →← git restore
Staging areathe next snapshot (index)
menu.md
prices.md
git commit →← restore --staged
RepositoryHEAD → e375f09
menu.md
prices.md
terminal · simulated, no real repository is touched
Read this demo as text
  1. $ edit menu.md "Soup, Slaad (oops, cat on keyboard)"

    We ruin menu.md in the working directory. Nothing is staged or committed yet.

  2. $ git restore menu.md

    git restore copies the file back from the staging area. The mess is gone for good, because git never saw it.

  3. $ edit menu.md "Soup, Salad"
    $ git add menu.md

    A real change this time, and it is staged.

  4. $ git restore --staged menu.md

    restore --staged unstages: the staging area goes back to HEAD's version, but your edit is still in the working directory.

  5. $ git commit -am "Add slaad"

    Committed, with a typo in the message.

  6. $ git commit --amend -m "Add salad"

    --amend builds a brand-new commit with the fixed message and moves main onto it. The old one is left behind, greyed out. (Commits never change.)

  7. $ git reset --soft HEAD~1

    reset --soft moves main back one commit. The salad change is still staged, so you could recommit it right away.

  8. $ git reset

    reset (mixed, the default) resets the staging area to match HEAD. The change now exists only in the working directory.

  9. $ git reset --hard

    reset --hard also overwrites the working directory. The salad edit is destroyed. Double-check before you use it.

  10. $ git revert HEAD

    Now say "Add prices" was already pushed. Resetting would rewrite shared history. revert instead adds a new commit that undoes it, and history only moves forward.

Not pushed yet (only on your machine)
  • git commit --amend to fix the last commit
  • git reset --soft HEAD~1 to redo it
  • git reset --hard HEAD~1 to throw it away
  • Rewriting is fine: nobody has the old commits
Already pushed (others may have it)
  • git revert <commit>: new commit, opposite change
  • History only moves forward, so teammates can pull normally
  • Never reset/amend + force-push a shared branch
  • Check the graph: is origin/main on or after that commit?
git restore <file>Discard working-directory changes
git restore --staged <file>Unstage, keep the change
git commit --amendReplace the last commit
git reset --soft|--mixed|--hard <commit>Move the branch back (rewinds 1, 2 or 3 areas)
git revert <commit>Undo a shared commit with a new commit

3 Check your understanding

Question 1

You ran git reset --soft HEAD~1. Where are the changes from the undone commit?

Question 2

Look at the graph. Which commit can you safely remove with git reset --hard HEAD~1?

2f5672f Add menu2f5672fAdd menue375f09 Add pricese375f09Add prices5689cee Local experiment5689ceeLocal experime…Remote-tracking branch: where origin's main was last time you fetchedorigin/mainHEAD = where you are. It points at the current branch.HEAD →Branch "main" — a movable pointer (current branch: moves when you commit)main
Question 3

A buggy commit was pushed to main yesterday and teammates have pulled it. What is the safe fix?

Question 4

You git add-ed secrets.env by mistake (not committed). How do you take it out of the next commit but keep the file?

4 Practice for real

Your mission

Clean up a messy Monday

Three problems: (1) menu.md has garbage edits. Discard them. (2) Your unpushed "WIP junk" commit should disappear. (3) The already pushed "Break prices" commit must be undone without rewriting shared history. Finish with a clean working tree.

2f5672f Add menu click to insert the hash into the terminal2f5672fAdd menue375f09 Add prices click to insert the hash into the terminale375f09Add prices1a7262f Break prices click to insert the hash into the terminal1a7262fBreak prices5f90266 WIP junk click to insert the hash into the terminal5f90266WIP junkRemote-tracking branch: where origin's main was last time you fetchedorigin/mainHEAD = where you are. It points at the current branch.HEAD →Branch "main" — a movable pointer (current branch: moves when you commit)main
Working directoryyour folder / editor
junk.txt
menu.mdchanged
prices.md
git add →← git restore
Staging areathe next snapshot (index)
junk.txt
menu.md
prices.md
git commit →← restore --staged
RepositoryHEAD → 5f90266
junk.txt
menu.md
prices.md
terminal · simulated, no real repository is touched
Type commands below. `help` lists everything. Tab completes, ↑ recalls. Click a commit to paste its hash.
~/project (main) $