Almost every "undo" in git comes down to one question: what state is the change in right now? Once you know that, the command is obvious. The hard part is that git has historically overloaded checkout and reset to mean half a dozen unrelated things, so the same word undoes a file edit, switches a branch, and throws away a commit. Since git 2.23 (2019) there is a cleaner split: git restore for files, git switch for branches. The rest still works, but the modern commands say what they do.
Here is the whole map in one table. Pick your row, run the command, read the section below for the nuance.
| Situation | Command |
|---|---|
| Discard unstaged edits to a file | git restore path/to/file |
| Unstage a staged file (keep the edits) | git restore --staged path/to/file |
| Reword the last commit message | git commit --amend |
| Undo the last commit, keep the changes | git reset --soft HEAD~1 |
| Undo the last commit, throw the changes away | git reset --hard HEAD~1 |
| Undo a commit you have already pushed | git revert <sha> |
| Delete untracked files and folders | git clean -fd (preview with -nd first) |
| Recover something you think you lost | git reflog |
Discard unstaged changes to a file
You edited a file, did not stage it, and want the file back to its last committed state:
git restore path/to/fileThis is the modern replacement for git checkout -- file. The old form still works, but checkout is so overloaded that it was easy to fat-finger a branch name and lose your edits. git restore only ever touches files, so there is no ambiguity. To discard every unstaged change in the working tree at once:
git restore .There is no undo for this. The edits were never committed, so once they are gone, they are gone. If you are not sure you want to lose the work, stash it instead with git stash, which tucks the changes away where you can get them back later.
Unstage a file
You ran git add on something you did not mean to stage. You want it out of the index but you want to keep the edits:
git restore --staged path/to/fileThat replaces the old git reset HEAD file. The file goes back to "modified but not staged"; your actual changes are untouched. To unstage everything:
git restore --staged .A common point of confusion: if a file was only ever modified (never staged), you do not need this step at all. Just git restore file and move on. The --staged form is only for pulling something back out of the index.
Reword the last commit message
You committed with a typo in the message, or "wip" when you meant something real:
git commit --amendThat opens your editor on the last commit's message. To set it in one line without the editor:
git commit --amend -m "A clearer commit message"--amend replaces the previous commit entirely, which means it gets a new SHA. That is fine while the commit is local. Do not amend a commit you have already pushed to a shared branch: the new SHA diverges from what everyone else has, and you are back to force-push territory. See git force push safely for what that costs and how to do it without clobbering a teammate.
Undo the last commit
Two versions, and the difference matters a lot. Both move the branch pointer back one commit; what they do with the changes is the whole question.
Keep the changes (they land back in your working tree, staged):
git reset --soft HEAD~1This is the everyday "I committed too early" or "I committed to the wrong branch" undo. The commit is gone, but every line you wrote is right there, staged and ready to re-commit (or move to another branch). Nothing is at risk.
Throw the changes away entirely:
git reset --hard HEAD~1
--hardis destructive. It deletes the commit and the changes in it from your working tree. There is no confirmation prompt. Run it only when you are certain you want those edits gone. If you are not certain, use--soft(or--mixed, the default, which keeps the changes unstaged) and you keep your safety net.
HEAD~1 means "one commit before HEAD." HEAD~2 undoes the last two, and so on. The --soft / --hard distinction applies the same way at any depth.
Undo a commit you have already pushed
This is the one that catches people. git reset rewrites history, which is fine on a commit that only exists on your machine and a disaster on a commit other people have already pulled. The safe tool for a pushed commit is revert:
git revert <sha>git revert does not erase anything. It creates a new commit that is the exact inverse of the one you name, undoing its changes while leaving the original in the history. Because it only adds a commit, you push it normally, no force-push, no rewritten history, nothing breaks for anyone who already pulled. That audit trail ("this change was reversed, here is when and why") is a feature, not noise, on a shared branch.
To find the SHA, run git log --oneline and copy the short hash. To revert the most recent commit specifically:
git revert HEADReverting a merge commit needs the -m parent flag, which is its own small topic; the git revert docs cover the mainline-parent rule. The rule of thumb is simple: local commit, use reset; pushed commit, use revert.
Remove untracked files
git restore and git reset only touch files git already knows about. Brand-new files that have never been staged are untracked, and the tool for those is git clean. It deletes files off your disk, so always preview first:
git clean -ndThe -n is a dry run: it lists exactly what would be deleted and changes nothing. Read that list. When it is what you expect, run it for real:
git clean -fd-f is "force" (git refuses to delete without it, by design), and -d includes untracked directories, not just files. This is the command you want for "get rid of all the junk I created but never committed."
A flag worth getting right, because it is a common and damaging mix-up: -fX (uppercase X) does not mean "only untracked files." Uppercase -X removes only the files git is ignoring (the build artifacts and caches in your .gitignore), and leaves your real untracked files in place. Lowercase -x is more aggressive still: it removes untracked and ignored files together. For the ordinary "clean up untracked stuff" case, you want plain git clean -fd, not -fX. When in doubt, git clean -nd shows you the truth before anything is deleted.
| You want to delete | Command |
|---|---|
| Untracked files and folders | git clean -fd |
| Only ignored files (build output, caches) | git clean -fdX |
| Untracked and ignored, everything | git clean -fdx |
When you think you lost something: reflog
Here is the reassuring part. Almost nothing in git is truly gone, even after a reset --hard or a bad rebase. Git keeps a private log of everywhere HEAD has pointed, and that log is reflog:
git reflogYou get a list like abc1234 HEAD@{0}: reset: moving to HEAD~1, one line per move, most recent first. Find the SHA from before the move you regret, and you can get back to it:
git reset --hard abc1234Or, if you only want the lost commit back as a branch without disturbing where you are now:
git switch -c recovered-work abc1234reflog is local and only goes back so far (entries expire, ninety days by default for reachable commits), but in the moment right after a mistake it is almost always there. Before you panic about a --hard or an amend that ate the wrong thing, check git reflog. The thing you lost is usually one SHA away.
A note on the old commands
If you learned git years ago, the muscle memory is git checkout -- file and git reset HEAD file. Those still work and are not deprecated; nothing about your existing scripts breaks. But git restore (files) and git switch (branches) were added precisely because checkout did too many jobs, and the new pair is what I reach for now and what I would teach someone starting today. If you are still getting comfortable with the basics, git for beginners walks through the staging model that makes all of this make sense.
FAQ
See also
- Git force push safely: --force vs --force-with-lease: once you have rewritten local history, this is how you publish it without clobbering a teammate.
- Squash git commits with interactive rebase: clean up a messy commit history before you open a pull request.
- Essential git branch commands: current branch, default branch, switching back, and deleting local and remote branches.
- Git for beginners: the staging-area model that makes restore, reset, and revert click.
Sources
Authoritative references this article was fact-checked against.
- git-restore (official documentation)git-scm.com
- git-reset (official documentation)git-scm.com
- git-revert (official documentation)git-scm.com
- git-clean (official documentation)git-scm.com
- git-reflog (official documentation)git-scm.com





