datarekha

Undoing changes: restore, reset, revert

A precise map of every Git undo command — which tree each one touches, when to use each, and which ones can destroy work permanently.

10 min read Intermediate Git Lesson 14 of 15

What you'll learn

  • Which undo command targets which of the three trees (working dir, staging, repo)
  • When to reach for restore, reset, amend, or revert — and why the answer depends on whether you have pushed
  • "reset --hard and restore are destructive: what that means and how to avoid surprises"

Before you start

You already know the three trees: the working directory (files on disk), the staging area (the proposed next commit), and the repository (the chain of committed snapshots, with HEAD pointing at the tip). Every Git undo command is just a targeted operation on one or more of those trees. Once you know which tree holds your mistake, the right command becomes obvious.

The undo toolbox at a glance

IntentCommandTouches
Discard unsaved edits to a filegit restore <file>Working dir only
Unstage a file (keep edits)git restore --staged <file>Staging only
Fix the last commit message or add a forgotten filegit commit --amendRepo (rewrites last commit)
Move HEAD back, keep everything stagedgit reset --soft <ref>HEAD / repo only
Move HEAD back, unstage changes, keep editsgit reset --mixed <ref>HEAD + staging
Move HEAD back and discard all changesgit reset --hard <ref>HEAD + staging + working dir
Undo a commit on shared historygit revert <sha>Repo (adds new commit)

git restore — undo edits you have not committed yet

git restore talks to the working directory and the staging area. It does not touch commits.

Discard working-directory changes (reset a file back to what HEAD has):

git restore README.md
# No output. README.md now matches the last commit.

Unstage a file without losing your edits (move staging back to match HEAD, leave the file on disk untouched):

git restore --staged README.md
# No output. README.md is now unstaged; your edits are still in the working dir.

You can also combine both flags to unstage and discard in one step:

git restore --staged --worktree README.md

git commit —amend — rewrite the last commit

Sometimes you commit and immediately realize the message has a typo, or you forgot to include one file. --amend replaces the tip commit with a new one that has your corrections.

# Stage the forgotten file first
git add forgot-this.py

# Rewrite the last commit
git commit --amend -m "Add login endpoint and missing helper"
[main 9c3f21a] Add login endpoint and missing helper
 2 files changed, 47 insertions(+)

Notice that the SHA changed from whatever it was before — --amend creates a new commit object. The old commit is discarded. This is fine as long as that commit was never pushed. If teammates have already pulled it, amending produces a diverging history and forces a conflict.


git reset — move HEAD (and optionally the trees)

git reset moves the HEAD pointer (and the branch it points to) back to an earlier commit. Three modes control how much collateral change happens.

—soft: move HEAD only

git reset --soft HEAD~1

HEAD moves back one commit. The changes from that commit reappear in the staging area, ready to be recommitted differently. Your working directory is untouched.

Use this when: you want to squash several local commits into one, or when your commit was correct but needs a better message before you push.

—mixed (the default): move HEAD and unstage

git reset HEAD~1
# same as:
git reset --mixed HEAD~1

HEAD moves back, and the staging area is cleared to match that earlier commit. Your edits stay in the working directory as unstaged changes. Nothing in your files is lost.

Use this when: you want to undo a commit and re-stage only some of the changes.

—hard: move HEAD and discard everything

git reset --hard HEAD~1

HEAD moves back, staging is cleared, and the working directory is overwritten to match that earlier commit. Any uncommitted work is gone.

Use this when: you are absolutely certain you want to throw away that entire commit’s worth of changes and start fresh from the previous state.

HEAD~1 means “one commit before HEAD”. You can also pass a specific SHA: git reset --hard a1b2c3f.


git revert — the safe undo for shared history

git revert does not move HEAD backward. It reads a past commit, figures out what it changed, and creates a new commit that does the opposite. The history stays intact; a correction is simply appended.

git revert a1b2c3f
[main 7d4f11e] Revert "Add experimental cache layer"
 1 file changed, 12 deletions(-)

Because revert only adds commits and never rewrites them, it is safe on any branch that others are using — pushed main, shared feature branches, release branches. When in doubt, prefer revert.


The decision: local cleanup vs. shared history

The central question is always: has this commit been pushed?

  • Not pushed yet — you have full freedom. restore, amend, and reset are all on the table. Pick based on which tree holds the mistake.
  • Already pushed — use git revert. Every other option rewrites history and will cause problems for anyone who has pulled.

Which tree does each command touch?

Working DirStaging AreaRepo / HEADrestore <file>restore —stagedcommit —amendreset —softreset —mixedreset —hardrevert
Which tree each undo command touches. A check mark means that tree is modified.

Quick check

0/3
Q1You staged three files and want to remove one from the staging area without losing your edits to it. Which command is correct?
Q2A teammate pushed a buggy commit to the shared main branch six hours ago and others have already pulled it. What is the safest way to undo that commit?
Q3You have three small local commits that all belong to the same feature. You want to collapse them into one before pushing. Which reset mode should you use?

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Explore further

Glossary terms
Cheat sheets

Related lessons

Skip to content