Undoing Changes: restore, amend, revert and reset
Discard edits with git restore, unstage files, fix your last commit with --amend, safely reverse commits with git revert, and understand the three modes of git reset.
On this page 11 sections
Introduction
Everyone makes mistakes: deleting the wrong line, staging the wrong file, committing with a typo, or committing something that breaks the site. One of Git’s biggest benefits is that almost every mistake can be undone — as long as you pick the right command. This lesson covers the five you need, from the gentlest to the most powerful.
- How to throw away edits you have not committed (git restore).
- How to take a file out of the staging area (git restore --staged).
- How to fix the message or contents of your last commit (git commit --amend).
- How to undo a commit safely on shared history (git revert), and how git reset moves a branch back in time.
Discard Changes to a File
Priya accidentally deletes the heading from index.html and saves the file. Git notices immediately:
git statusClick Run to see what this code prints.
Git even suggests the fix. git restore replaces the file in your working directory with the version from the last commit:
git restore index.htmlgit statusClick Run to see what this code prints.
git restore throws your uncommitted edits away for good — they were never committed, so Git has no copy of them. Check with git diff before you restore.
Unstage a File
Next, Priya tries a red text colour in style.css and stages it with git add style.css — then decides it should not be in the next commit. git restore --staged removes it from the staging area but keeps the edit in the file:
git restore --staged style.cssgit status --shortClick Run to see what this code prints.
M in the second column means the file is modified but no longer staged. To throw the edit away completely, she follows up with git restore style.css.
Fix the Last Commit
Priya changes the text colour and commits quickly — with a typo in the message:
git commit -am "Updte colours"Click Run to see what this code prints.
(The -a option stages every modified tracked file before committing, a handy shortcut when you have reviewed the changes.) To fix the most recent commit, use --amend. It replaces that commit with a new one:
git commit --amend -m "Update text colour"Click Run to see what this code prints.
git log --onelineClick Run to see what this code prints.
Look closely: the commit ID changed from bc5f55f to b04b6de. Amending does not edit a commit; it creates a replacement. If you forgot to include a file, stage it and run git commit --amend --no-edit to add it while keeping the same message.
Because amend creates a new commit ID, never amend a commit you have already pushed to GitHub. Anyone who downloaded the old commit would end up with a conflicting history.
Safely Reverse a Commit
Suppose the colour change turns out to be a bad idea after it has been shared. git revert creates a new commit that does the exact opposite of an earlier one. History is not rewritten — it records both the change and its undo, which is why revert is the safe choice for commits other people already have:
git revert --no-edit HEADClick Run to see what this code prints.
git log --onelineClick Run to see what this code prints.
HEAD means the latest commit; you can revert any commit by its ID, such as git revert b04b6de. Without --no-edit, Git opens your editor so you can explain why you are reverting.
A commit you pushed to GitHub yesterday broke the contact page, and your teammates have already pulled it. What is the safe way to undo it?
Move Back in History: git reset
git reset moves your current branch back to an earlier commit. It is powerful and useful for tidying up local work that you have not shared yet. Its three modes differ in what happens to the changes from the commits you move past:
| Mode | Branch moves back? | Staging area | Your files | Use it to |
|---|---|---|---|---|
| --soft | Yes | Changes stay staged | Unchanged | Redo the last commit(s) with a different message or split |
| --mixed (default) | Yes | Changes unstaged | Unchanged | Uncommit and restage selectively |
| --hard | Yes | Cleared | Changes deleted | Throw away commits and edits completely |
Priya commits a draft blog page, then decides it is not ready. A soft reset undoes the commit but keeps the file staged:
git reset --soft HEAD~1git status --shortClick Run to see what this code prints.
A in the first column means “added, staged”. Running git reset with no options (a mixed reset of nothing) now unstages it, leaving an untracked file:
git resetgit status --shortClick Run to see what this code prints.
Finally, a hard reset. She commits the draft again and then decides to delete it entirely:
git reset --hard HEAD~1lsClick Run to see what this code prints.
The commit is gone from the branch and blog.html is gone from the folder. Lesson 16 shows how git reflog can still rescue commits after a hard reset — but uncommitted edits wiped by --hard are lost for good.
Priya has an hour of unsaved-to-Git edits in index.html and wants to undo her last commit. She runs git reset --hard HEAD~1. What is the problem?
Which Command Should I Use?
| I want to… | Command |
|---|---|
| Throw away uncommitted edits to a file | git restore <file> |
| Unstage a file but keep my edits | git restore --staged <file> |
| Fix the message of my last (unpushed) commit | git commit --amend -m "new message" |
| Add a forgotten file to my last (unpushed) commit | git add <file> then git commit --amend --no-edit |
| Undo a commit that is already pushed or shared | git revert <commit> |
| Uncommit my last local commit but keep the changes | git reset --soft HEAD~1 (staged) or git reset HEAD~1 (unstaged) |
| Delete my last local commit and its changes | git reset --hard HEAD~1 |
git restore was added in Git 2.23 (2019). Older tutorials use git checkout -- <file> instead of git restore <file>, and git reset HEAD <file> instead of git restore --staged <file>. They still work, but the newer commands are clearer.
Common Mistakes
- Using git reset --hard without checking git status for uncommitted work first.
- Amending or resetting commits that are already on GitHub, which forces everyone else to repair their history.
- Running git restore . to discard “one” change and losing edits to every file in the folder.
- Deleting and re-cloning a repository to “undo” something — there is almost always a precise command instead.
Frequently Asked Questions
Yes. Reverting the revert commit re-applies the original change. This is a common way to bring back a feature once a bug in it is fixed.
revert adds a new commit that cancels an old one, so history only grows — safe for shared branches. reset moves the branch pointer back, removing commits from the branch — fine for local work, risky for anything shared.
Yes. The original commit still exists for a while; git reflog (lesson 16) shows its ID, and git reset --soft <that-id> brings it back.
Key Takeaways
- git restore <file> discards uncommitted edits; git restore --staged <file> only unstages.
- git commit --amend replaces your last commit — use it only before pushing.
- git revert creates a new commit that undoes an old one; it is the safe choice for shared history.
- git reset moves a branch back: --soft keeps changes staged, --mixed unstaged, --hard deletes them.
- Uncommitted edits removed by restore or reset --hard cannot be recovered, so check git status first.
Summary
You now have a toolkit for every kind of mistake, from a stray edit to a bad commit on a shared branch. The best mistake, of course, is the one you never make — and one of the most common is committing files that should never be in a repository at all. That is what .gitignore is for.