Sometimes you commit and immediately regret it: wrong message, wrong scope, wrong grouping of files. You don't want to lose the work, you just want the commit itself to go away so you can recompose it. That's what soft reset is for.
The three flavours of reset
git reset has three modes, and picking the right one is the
whole game. The mode determines what happens to your files:
| Mode | HEAD | Staging area | Working tree |
|---|---|---|---|
--soft |
moves back | untouched | untouched |
--mixed (default) |
moves back | reset to match HEAD | untouched |
--hard |
moves back | reset to match HEAD | reset to match HEAD |
In every mode, the branch pointer moves backwards to the commit you specified. The differences are in what happens to your files.
--softkeeps everything exactly as it is; only the commit history changes. All the file changes from the un-done commits are still staged, ready for a fresh commit.--mixed(the default when you don't specify) moves the changes from staged to unstaged. Files are still modified on disk, but not currently staged.--hardthrows everything out. Files revert to the state at the target commit. Destructive.
The soft reset recipe
To "undo" your most recent commit while keeping the changes ready to re-commit:
git reset --soft HEAD~1
HEAD~1 means "one commit before HEAD". So this moves the branch
pointer back by one commit, keeping the staging area and working
tree untouched. Run git status and you'll see all the files
from your undone commit still staged, exactly as they were when
you committed them.
From here you can:
- Commit again with a better message:
git commit -m "New message". - Unstage a file to leave it for a separate commit:
git restore --staged <file>. - Add more files and commit them all together.
- Change your mind and re-run the same commit:
git commit -m "...".
The undone commit's changes are preserved in staging. You have full control over what happens next.
Going back further
HEAD~1 is one commit back. HEAD~3 is three commits back. So:
git reset --soft HEAD~3
Moves the branch pointer back three commits. All three commits' file changes accumulate in the staging area. This is the "squash" recipe from the rewriting module, in a slightly different shape.
What about reset vs amend
Both git commit --amend and git reset --soft HEAD~1 let you
modify the most recent commit. When do you use which?
--amendwhen you want to keep most of the commit and just tweak one thing (the message, add a forgotten file). Simpler.--softreset when you want to fully rebuild the commit from scratch, or when you're going back more than one commit. More flexible.
Both operations produce a new commit with a new SHA. Both are subject to the "don't rewrite pushed commits" rule.
The reset warning applies here too
Even though soft reset preserves your files, it still rewrites commit history. The commits before the reset had one set of SHAs; after the reset, those commits are unreachable from your branch. If you've pushed them, anyone else has the old versions and you're now diverging in a hard-to-reconcile way.
Local reset before push: fine, common. Reset commits others have: never.