Learning Objectives
By the end of this lesson you will be able to:
- Predict exactly what each of the three resets keeps and discards
- Choose the right depth for a given undo
- Explain what
git reset --hardgenuinely destroys - Un-stage a file and un-commit a commit with confidence
One Command, Three Areas
Every reset moves the current branch pointer to a commit you name. What differs is how much else it drags along.
Remember the three areas from the foundations trail:
Working directory → Staging area (index) → Repository (commits)
your files what git add put there what git commit wrote
| Reset | Branch moves | Index | Working directory |
|---|---|---|---|
--soft |
Yes | Kept as it was | Kept |
--mixed (default) |
Yes | Reset to match | Kept |
--hard |
Yes | Reset to match | Overwritten |
Read it top to bottom: each one throws away everything the one above it does, plus one more layer.
--soft: Un-Commit, Keep Everything Staged
git reset --soft HEAD~1
The last commit is gone from the branch. All of its changes are still staged, ready to commit again.
Use it when the changes are right and the commit is not — wrong message, wrong grouping, forgot a file:
git reset --soft HEAD~1
git add tests/payments/refund.spec.ts # the file you forgot
git commit -m "test: add refund boundary cases"
Three commits into one, same idea:
git reset --soft HEAD~3
git commit -m "test: cover the refund limit end to end"
Nothing can be lost by a --soft reset. Every change is still there, staged.
--mixed: Un-Commit and Un-Stage
git reset HEAD~1 # --mixed is the default
The commit is gone and the changes are back in your working directory, unstaged. The files are untouched; only the index was cleared.
This is the one you want when you need to re-decide what goes into which commit:
git reset HEAD~1
git add src/payments/refund.ts
git commit -m "fix: clamp refund to the configured limit"
git add tests/payments/refund.spec.ts
git commit -m "test: cover the clamped-refund path"
That is exactly the commit-splitting recipe from Module 2, now with a name.
--mixed also un-stages without touching commits:
git reset # un-stage everything
git reset src/payments/refund.ts # un-stage one file
Nothing is lost here either. Your edits are all still in the files.
--hard: Move and Overwrite
git reset --hard HEAD~1
The commit is gone, the index is cleared, and your working directory is overwritten to match the target commit. Uncommitted edits in tracked files are gone.
Legitimate uses:
git reset --hard # discard all uncommitted changes, keep the commit
git reset --hard origin/main # make my branch identical to the remote
The second is the recovery from Module 1 when someone rebased a branch you had pulled.
What It Really Destroys
Be precise about this, because the fear around --hard is half-misplaced.
Recoverable: the commits you moved off. They are still in the object database, and git reflog knows their IDs. Module 5 is entirely about getting them back.
Not recoverable: uncommitted changes in your working directory. They were never in a commit, so Git never stored them anywhere. Nothing brings them back.
So the danger of --hard is not the commits. It is the two hours of unsaved editing you had in the working tree.
Pro Tip: Run
git statusbefore every--hard. If it lists modified files you care about,git stashthem first. Stash costs one second and is fully reversible;--hardover unsaved work is not.
Choosing
| You want to | Command |
|---|---|
| Redo the last commit, keeping the changes staged | git reset --soft HEAD~1 |
| Combine the last three commits into one | git reset --soft HEAD~3 then commit |
| Split a commit into two | git reset HEAD~1, stage in pieces |
| Un-stage a file | git reset <file> |
| Throw away all uncommitted changes | git reset --hard |
| Match a branch to the remote exactly | git reset --hard origin/<branch> |
And the constraint that governs all of them: reset rewrites your branch, so it is for commits that have not left your machine. For anything published, use git revert.
Key Takeaways
- All three resets move the branch pointer; they differ in how much else they clear
--softkeeps changes staged;--mixedkeeps them in the working directory;--hardoverwrites the files--mixedis the default, and is also how you un-stage a file--hardcannot lose commits — the reflog has them — but it permanently loses uncommitted editsgit statusbefore every--hard, and stash anything you would miss- Reset rewrites history, so it is only for commits nobody else has