Introduction
Learn how to undo changes and commits in Git with checkout, restore, reset, revert, clean and amend - with clear examples and when to use each command.

Unlike most editors, Git doesn't have a single "undo" button. Instead it gives you a small set of commands that each target a different place where your work lives: the working directory (the files on disk), the staging index (changes marked for the next commit), and the commit history (snapshots already recorded). Picking the right command depends on which of those three areas you want to roll back, and whether the change has been shared with anyone else.
This page covers the full toolkit: reviewing history, restoring files, and undoing one commit or many — with git checkout, git restore, git reset, git revert, git clean and git commit --amend.
Choosing the right command
Before any specific command, it helps to know which area each one touches and whether it rewrites history:
| Goal | Command | Safe to share? |
|---|---|---|
| Discard edits to a tracked file | git restore <file> (or git checkout -- <file>) | yes |
| Remove a file from staging | git restore --staged <file> (or git reset HEAD <file>) | yes |
| Delete untracked files | git clean | yes |
| Fix the most recent commit | git commit --amend | only if not pushed |
| Move the branch back to an older commit | git reset | local only |
| Cancel a commit by adding an inverse | git revert | yes |
The single most important rule: reset and amend rewrite history, so use them only on commits you have not pushed. For anything already shared, use revert.
Reviewing old commits
You can't undo what you can't find. The best tool for reviewing history is git log. Every commit has an identifying hash that you use to reference it:
git log --oneline
a3b2a21 Crossword solver with Vue.js
c54ce02 New logic for crossword game
3acb8d0 Some changes in crossword logic
de32112 Styling crossword table areaBy default git log shows only the commits reachable from the current branch. To view commits across all branches, add --all. Use git checkout or git switch to visit other branches.
Viewing an old revision
To inspect the project as it looked at an earlier point, first find the hash of the revision you want:
git log --oneline
b7119f2 Changes in Scrabble Solver
234be24 Fixing search input bug
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesThen check that commit out by its hash:
git checkout b235bf4You can now browse files, run tests, and even edit things. Nothing you do here is recorded on a branch, so your current work is safe. This temporary state is called a detached HEAD — HEAD points directly at a commit instead of at a branch tip. Return to your branch when you're done:
git switch - # or: git checkout masterOnce back on your branch, use git revert or git reset to undo any change you want.
Undoing a committed snapshot
There are several ways to undo a commit. The right one depends on whether the commit has been shared. Imagine our history looks like this:
git log --oneline
863fa8e Making some improvements
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesThe sections below undo the 863fa8e Making some improvements commit three different ways.
With git checkout (inspect without changing the branch)
Checking out the previous commit, b235bf4, puts the repository in the state before the improvements commit:
git checkout b235bf4This is a read-only-ish detour, not a real undo: you land in a detached HEAD state. Any new commit you make here becomes orphaned once you switch back to an established branch, and Git's garbage collector may eventually remove it. To keep work made from this state, branch off it:
git checkout -b improvements-removedYou now have a new branch, improvements-removed, whose timeline never included 863fa8e. Checkout is best for looking at an old state; the next two commands actually undo the commit.
With git revert (the safe, shareable undo)
git revert HEAD creates a new commit that applies the inverse of the target commit. Nothing is erased — the history grows forward:
git revert HEADAfter running it, the log looks like this:
git log --oneline
23a4b42 Revert "Making some improvements"
863fa8e Making some improvements
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesThe effect of 863fa8e is now cancelled, yet the commit is still in the history; 23a4b42 simply reverses its changes. Because nothing is rewritten, you stay on the same branch and never break anyone else's clone. This is the correct undo for commits that have been pushed or shared publicly.
With git reset (rewrite local history)
git reset moves the current branch pointer to a chosen commit. Running git reset --hard b235bf4 rewinds the branch to that commit and discards everything after it:
git reset --hard b235bf4
git log --oneline
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesThe 863fa8e commit is gone from this branch's history. This is clean and direct, but because it rewrites history you must only do it on commits you haven't pushed. git reset takes a mode flag that controls how far the undo reaches:
--soft— move the branch pointer only; keep changes staged.--mixed(default) — move the pointer and unstage changes, but keep them in the working directory.--hard— move the pointer and throw away staged and working-directory changes.
git reset --hard permanently discards uncommitted work in the working directory. There is no staging step to recover it from. Use --soft or --mixed when you only want to redo the commit but keep the edits.
Undoing the last commit
Sometimes you don't want to remove the most recent commit — you just committed too early or wrote a poor message. Stage any extra changes with git add, then amend:
git add forgotten-file.txt
git commit --amendGit opens your configured editor so you can edit the last commit's message, and the newly staged changes are folded into that same commit. To change only the message in one line:
git commit --amend -m "A clearer message"Amending replaces the previous commit with a new one (a new hash), so treat it like reset: only amend commits you haven't pushed.
Undoing uncommitted changes
Before a change is committed it lives in the working directory and the staging index, so you can undo it from either area without touching history. The modern command is git restore:
# Discard edits to a tracked file (working directory)
git restore <file>
# Unstage a file but keep its edits
git restore --staged <file>The older equivalents still work and appear in lots of documentation:
# Discard changes in the working directory
git checkout -- <file>
# Unstage a file
git reset HEAD <file>Removing untracked files
git restore and reset only touch files Git already knows about. To delete brand-new, untracked files, use git clean. Always preview first with -n (dry run):
git clean -n # list what would be removed
git clean -f # actually remove untracked filesHow the three areas relate
It's worth keeping the three areas straight, because each undo command targets a specific one:
- Working directory — the files on disk that your editor changes.
git restore <file>andgit cleanoperate here. - Staging index — the snapshot you're building for the next commit.
git addputs changes here;git restore --staged(a--mixedreset) pushes them back to the working directory. - Commit history — recorded snapshots.
git resetrewrites it;git revertadds to it.
Recovering from a mistaken undo
Reset or a deleted branch can feel terrifying, but Git rarely loses anything immediately. The git reflog records where HEAD has pointed, so you can find a "lost" commit's hash and reset back to it:
git reflog
1a2b3c4 HEAD@{0}: reset: moving to b235bf4
863fa8e HEAD@{1}: commit: Making some improvementsRecover with git reset --hard 863fa8e (or branch off it). If you only want to set work aside temporarily rather than undo it, consider git stash instead.
Undoing public changes
Once a commit is pushed, other people may have it. Use git revert for public changes, never git reset. Reset removes commits from history, so rewriting a shared branch forces everyone else to repair their clones. Revert leaves the original commit in place and records a new commit that reverses it — safe for everyone who has already pulled.