Undoing Changes in Git: Reset, Restore, Revert Explained
Understand git restore, reset, and revert so you can unstage files, discard local edits, undo commits, and avoid rewriting shared history.
Undoing Changes in Git: Reset, Restore, Revert Explained
Undoing changes in Git is safe when you know which layer you are changing: the working tree, the staging area, or commit history. The common commands are git restore, git reset, and git revert, and they solve different problems.
The big rule is simple: use restore for files, reset for local history changes, and revert for undoing commits that other people may already have.
Understanding the Core Concepts
Keep these areas straight:
- Working tree: the files you edit.
- Staging area, also called the index: the changes selected for the next commit.
- Commit history: the commits already recorded.
HEAD: the current commit your branch points to.
Run git status before undoing anything. It tells you whether changes are unstaged, staged, or already committed.
git restore: For Discarding Changes in the Working Directory and Staging Area
Use git restore when you want to change files in the working tree or staging area without moving branch history.
Unstaging Files
If you staged a file by mistake, unstage it with --staged:
git restore --staged <file>
To unstage everything:
git restore --staged .
Your file edits remain in the working tree. They are simply removed from the next commit.
Discarding Changes in the Working Directory
To throw away unstaged edits in one file:
git restore <file>
This makes the working-tree copy match the index. If the file is not staged, that usually means it goes back to HEAD. Review git diff first because this discards local edits.
To discard both staged and unstaged changes for a file and restore it from HEAD:
git restore --source=HEAD --staged --worktree <file>
Restoring a File from a Specific Commit
You can also bring back one file from an older commit:
git restore --source=<commit> -- path/to/file
That changes the file in your working tree. Commit it if the old version is what you want going forward.
git reset: Rewriting History
Use git reset when you want to move the current branch pointer. It can also change the staging area and working tree depending on the mode.
Understanding the Modes (--soft, --mixed, --hard)
The three common modes are:
git reset --soft HEAD^
Moves HEAD back one commit and keeps the undone commit's changes staged.
git reset --mixed HEAD^
Moves HEAD back one commit and keeps the undone commit's changes unstaged. --mixed is the default, so git reset HEAD^ does the same thing.
git reset --hard HEAD^
Moves HEAD back one commit and discards matching changes from the staging area and working tree. This is destructive. Do not run it casually.
Useful reset patterns:
git reset --soft HEAD^ # redo the last local commit, keep changes staged
git reset HEAD^ # undo the last local commit, keep changes unstaged
git reset # unstage all staged changes
git reset --hard origin/main # discard local changes and match origin/main
Resetting an Old Commit
To move your branch back two commits while keeping the changes in your working tree:
git reset --mixed HEAD~2
Only reset commits that are local or that your team has agreed to rewrite. If the commits are already on a shared branch, prefer git revert.
git revert: Creating a New Commit to Undo Changes
git revert creates a new commit that applies the inverse of an earlier commit. It does not delete or move the original commit, so it is safe for shared branches.
Revert one commit:
git revert <commit-hash>
Revert a range:
git revert HEAD~3..HEAD
If conflicts appear, resolve them, stage the files, and continue:
git add <fixed-files>
git revert --continue
Reverting a merge commit needs extra care because Git must know which parent should be treated as the mainline:
git revert -m 1 <merge-commit>
Do that only when you understand what the merge introduced. For production branches, ask for a review before reverting merges.
Choosing the Right Command
Use this quick guide:
- Unstage a file:
git restore --staged <file>. - Discard unstaged edits in a file:
git restore <file>. - Undo the last local commit and keep changes staged:
git reset --soft HEAD^. - Undo the last local commit and keep changes unstaged:
git reset HEAD^. - Discard local changes and commits destructively:
git reset --hard <commit>. - Undo a pushed commit safely:
git revert <commit-hash>.
Practical Takeaway
Before undoing anything, run git status and decide what you are changing. Use restore for file-level cleanup, reset for local unpushed commits, and revert for shared history. When a command includes --hard, pause and check git diff first.