Decision tree: what do you want to undo?
The right Git undo command depends on where your changes are in the Git workflow. Ask yourself three questions: Is it committed? Is it pushed? Is it staged? The answer points you to the right command.
Modified files in working directory
You edited files but haven't run git add yet.
Use git restore or git clean
Files added to the staging area
You ran git add but haven't committed yet.
Use git restore --staged
Local commits only
You committed but the changes are still on your machine.
Use git reset
Changes shared with the team
The commit is on the remote. Rewriting history would break things.
Use git revert
Temporarily shelve changes
You want to set aside your current work and come back to it later.
Use git stash
Recover from a destructive operation
You ran reset --hard or deleted a branch and need to recover.
Use git reflog
Undo uncommitted changes
These commands operate on your working directory — files you have edited but not yet committed. This is the simplest undo scenario: you changed something and want to go back to the last committed version.
git restore — Discard file modifications
git restore <file> resets a file to its last committed state. All uncommitted edits in that file are discarded. Use git restore . to discard all changes across all files at once. This is the modern replacement for git checkout -- <file>.
Warning: this is irreversible. Once you restore a file, the discarded edits are gone — they were never committed, so Git has no record of them.
git clean — Remove untracked files
git restore only works on tracked files. For untracked files (new files that Git doesn't know about), you need git clean. The -n flag does a dry run so you can preview what would be deleted. The -f flag actually removes the files, and -d includes directories.
Always run git clean -n first. Like git restore, this command is irreversible for untracked files — they were never committed.
Undo staged changes
You ran git add and now a file is in the staging area, ready to be committed. But you changed your mind — you want to unstage it without losing your edits.
git restore --staged — Unstage without losing edits
git restore --staged <file> moves a file out of the staging area and back to the "modified but not staged" state. Your actual edits in the file are preserved — only the staging is undone. This is the modern replacement for git reset HEAD <file>.
You can also combine both flags: git restore --staged --worktree <file> unstages the file and discards the modifications in one go.
Undo local commits
You committed but haven't pushed yet. Since the commit only exists on your machine, you can safely rewrite history without affecting anyone else. The tool for this is git reset.
git reset — Move HEAD backwards
git reset moves the branch pointer (HEAD) to a previous commit. What happens to the files depends on the mode you choose:
--soft— Undoes the commit. Changes stay staged (in the index). Use this when you want to amend or split a commit.--mixed(default) — Undoes the commit and the staging. Changes stay in your working directory. Use this to re-select which files to include.--hard— Undoes everything: commit, staging, and working directory changes. Use with extreme caution — this is destructive.
git reset --soft: keep changes staged
git reset --mixed: keep changes unstaged
git reset --hard: discard everything
Undo pushed commits
Once a commit has been pushed, it's shared with the team. Rewriting history with git reset would cause problems for everyone who already pulled. The safe approach is git revert.
git revert — Create a reverse commit
git revert <hash> creates a new commit that does the exact opposite of the specified commit. If the original commit added a line, the revert removes it. If it deleted a file, the revert restores it. The original commit stays in the history — nothing is rewritten.
This is the only safe way to undo a commit on a shared branch. After reverting, you simply push the new commit and your teammates pull it without conflicts.
Save work temporarily
Sometimes you don't want to undo changes — you want to set them aside for a moment. Maybe you need to switch branches for an urgent fix, or you want a clean working directory without losing your progress.
git stash — Shelve changes for later
git stash takes all your uncommitted changes (both staged and unstaged) and saves them on a stack. Your working directory becomes clean. Later, git stash pop restores the most recent stash and removes it from the stack. Use git stash apply instead if you want to keep the stash for reuse.
Useful tip: git stash -u includes untracked files in the stash. And git stash list shows all saved stashes so you can manage multiple work-in-progress contexts.
Recover lost commits
Ran git reset --hard by accident? Deleted a branch? Don't panic. If the commits ever existed, Git still has them — at least for 90 days. The tool to find them is git reflog.
git reflog — Git's safety net
git reflog shows a chronological log of every position HEAD has been at: commits, resets, checkouts, rebases — everything. Even if a commit no longer appears in git log, it will be in the reflog. Find the hash you need and reset or cherry-pick it to recover.
Think of reflog as Git's undo history. It doesn't track file-level changes — it tracks where HEAD pointed at each step. This makes it the ultimate safety net for recovering from nearly any mistake.
That covers every undo scenario in Git. The key takeaway: almost nothing is truly lost in Git. Between restore, reset, revert, stash, and reflog, there is always a way to get back to a good state. Scroll down for a side-by-side comparison of all methods.