Git Basics #5: Undoing Changes — restore, reset, and revert

7 min read

Mistakes are part of every workday. You edit the wrong file, stage something you did not mean to, typo a commit message, or push a commit that should never have gone out. Git can undo all of these situations. The problem is that the undo commands come in several flavors — restore, reset, revert, amend — and it is easy to lose track of which one fits which situation. The goal of this post is to clear up that confusion. There is exactly one criterion: decide what you want to undo and how far, and the command follows automatically.

It runs in six parts.

  • #1 What Git is — the snapshot model and the three areas
  • #2 add, commit, status — what the staging area means
  • #3 Branching and merging — fast-forward and 3-way
  • #4 Remotes — clone, fetch, pull, push, and what origin really is
  • #5 Undoing changes — restore, reset, and revert ← this post
  • #6 GitHub and your first Pull Request

This post walks through the five situations you will meet most often in practice, then maps situations to commands in a single table at the end.

Why undoing feels confusing #

Recall the three-areas model from part 1. Git has three areas — the working directory, the staging area, and the repository — and changes move through them in that order.

The undo commands feel confusing because each one touches different areas. restore deals with the working directory and the staging area, reset moves the commit position in the repository, and revert stacks a new commit instead of deleting anything. So the first question to answer is what you want to undo. The answer changes depending on whether it is an edit you have not committed, a file you only staged, a commit you just made, or a commit you already pushed.

Situation 1 — Discard uncommitted edits: git restore #

You were editing a file and decided the direction was wrong. To bring the file back to the state of the last commit, use git restore.

Discard edits in the working directory
# Restore one file to the state of the last commit
git restore app.py

# Restore the entire working directory
git restore .

There is one thing you must remember here. Git never recorded your uncommitted edits anywhere. In other words, edits thrown away with git restore are impossible to recover. Make it a habit to check one more time that the edits are safe to lose before you run it.

Note
Running git status makes Git suggest the undo command that fits your situation — hints like use "git restore <file>..." to discard changes. When you have no idea which command to reach for, reading git status first is the safest starting point.

Situation 2 — Unstage only: git restore –staged #

You ran git add, then decided this file should not go into the next commit. All you need is to take it off the staging area while leaving the file contents alone.

Unstage
git restore --staged app.py

Unlike situation 1, this command does not touch the file contents at all. It only releases the snapshot that was staged; the edits in your working directory stay put. Nothing gets lost — this one is safe.

Situation 3 — Fix the last commit: git commit –amend #

You made a commit and then noticed a typo in the message, or a file that belonged in it was left out. Instead of stacking one more commit, you can redo the last commit itself.

Redo the last commit
# Fix only the message
git commit --amend -m "fix: correct login validation error"

# Include a file you forgot, keeping the message
git add missed_file.py
git commit --amend --no-edit

--no-edit keeps the message as is and updates only the contents. The point to watch: amend does not modify the existing commit — it replaces it with a new commit. The commit hash changes. That is why amend should only be used on commits you have not pushed yet. Amending an already pushed commit puts your history out of sync with the remote.

Situation 4 — Undo a commit: git reset #

To make the last commit itself un-happen, the command is git reset. reset moves the commit position your branch points to backwards, and the option decides how far down the three areas the undo reaches.

Three levels of undoing a commit
# Undo the commit only, keep the changes in the staging area
git reset --soft HEAD~1

# Undo the commit and the staging, keep the changes in the working directory (default)
git reset HEAD~1

# Delete the commit, the staging, and the edits in the working directory
git reset --hard HEAD~1

HEAD~1 is the notation for the commit right before the current one. Mapping the three options onto the three-areas model gives the following.

OptionRepository (commit)Staging areaWorking directory
--softundonekeptkept
--mixed (default)undoneunstagedkept
--hardundoneunstagededits deleted

--soft and --mixed only unwind the commit while the changes themselves survive, so both are safe. They are useful when you want to split a commit into pieces or rewrite the message from scratch.

Note
git reset --hard is the most dangerous command covered in this series. It deletes even your uncommitted edits without warning, and those edits cannot be recovered. Before running it, make sure with git status that there is nothing you would lose. For the record, the commit undone by reset can itself be recovered for a while through a log called reflog, which we will cover in a separate post.

Situation 5 — Undo an already pushed commit: git revert #

If you have already pushed the problematic commit to the remote and your teammates have pulled that history, you must not use reset. Deleting the commit in your repository leaves it in the remote and in your teammates’ repositories, so you cannot push without forcing it, and a forced push tangles your teammates’ history along with yours.

The command for this case is git revert. revert does not delete the commit. It applies the target commit’s changes exactly in reverse, as a new commit stacked on top.

Undo a pushed commit
# Create a new commit that applies a specific commit's changes in reverse
git revert a1b2c3d

# Revert the commit you just made
git revert HEAD

History only grows forward and the already shared commits stay in place, so your teammates just pull as usual. As a bonus, the fact that the code was rolled back remains in the history as a record.

Summary by situation #

The five situations in one table.

SituationCommandRisk
Discard uncommitted editsgit restore <file>edits unrecoverable
Unstage onlygit restore --staged <file>safe
Fix the last commit’s message or contentsgit commit --amendbefore push only
Undo a local commitgit reset --soft/--mixed/--hard--hard deletes edits
Undo a pushed commitgit revert <commit>safe

The order of judgment runs top to bottom. Before a commit it is restore, committed but not pushed it is amend or reset, already pushed it is revert. Keep just one principle — never delete shared history, cover it with a new commit — and you avoid most of the Git accidents that happen on teams.

Wrap-up #

The conclusion of this post fits in one sentence. Undo commands are not something you memorize — decide what to undo and how far, and the command follows. Before a commit it is restore, before a push it is amend and reset, after a push it is revert.

The next post, “Git Basics #6: GitHub and Your First Pull Request”, is the final part of the series. We will gather the branches, commits, and pushes we have learned and build the standard procedure of collaboration, a Pull Request, from start to finish.

X