Build the mental model
Every 'undo' situation in Git comes down to one question: what state is the mistake in right now? Answering that tells you exactly which tool applies.
- Uncommitted change you want gone -> git restore <file>
- Staged by mistake, not committed -> git restore --staged <file>
- Local-only commit, just made -> git commit --amend
- Local-only commits, further back -> git reset
- Already pushed/shared commit -> git revert
`git commit --amend` rewrites the most recent commit entirely at the same spot in history; `git reset` unwinds several local commits at once, deciding by mode whether their changes end up staged or in your working directory.
Once a commit is pushed and someone might have pulled it, reach for `git revert` instead — it undoes the change with a new commit rather than rewriting anything, so nobody's clone gets pulled out from under them.
WHAT STATE IS YOUR MISTAKE IN?
------------------------------
WHAT STATE IS YOUR MISTAKE IN?
---------------------------------
uncommitted, unstaged change -> git restore <file>
uncommitted, staged by mistake -> git restore --staged <file>
committed, local only (latest) -> git commit --amend
committed, local only (older) -> git reset
committed, already pushed/shared -> git revertConnect it to a real scenario
Staged by mistake
Accidentally `git add` a `.env.local`? `git restore --staged .env.local` drops it back to untracked, nothing lost.
Fixing a commit
Realize the commit is missing something? Fix it, stage it, and `git commit --amend` — Git rewrites the previous commit in place.
This ties together earlier lessons
None of this introduces new mechanics — restore, stash, revert, and reset all work exactly as covered before. This lesson is just the map for which one to reach for.
Try the working example
git init -q -b main
git config user.name "Thuta Learner"
git config user.email "learner@example.com"
export GIT_AUTHOR_NAME="Thuta Learner"
export GIT_AUTHOR_EMAIL="learner@example.com"
export GIT_COMMITTER_NAME="Thuta Learner"
export GIT_COMMITTER_EMAIL="learner@example.com"
echo "console.log('core');" > app.js
git add app.js
GIT_AUTHOR_DATE="2026-01-08T09:00:00" GIT_COMMITTER_DATE="2026-01-08T09:00:00" \
git commit -m "Initial commit"
git branch -M main
echo "SECRET_KEY=abc123" > .env.local
git add .env.local
git status
git restore --staged .env.local
git status
rm .env.local
echo "console.log('feature v1');" >> app.js
git add app.js
GIT_AUTHOR_DATE="2026-01-08T10:00:00" GIT_COMMITTER_DATE="2026-01-08T10:00:00" \
git commit -m "add feature"
git log --oneline
echo "console.log('feature v1 finished');" >> app.js
git add app.js
GIT_AUTHOR_DATE="2026-01-08T10:05:00" GIT_COMMITTER_DATE="2026-01-08T10:05:00" \
git commit --amend -m "Add feature v1"
git log --oneline
git show --stat HEAD$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .env.local
$ git restore --staged .env.local
$ git status
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
.env.local
nothing added to commit but untracked files present (use "git add" to track)
$ git log --oneline
deb9ba8 add feature
85d10e3 Initial commit
$ git commit --amend -m "Add feature v1"
$ git log --oneline
06c60a7 Add feature v1
85d10e3 Initial commit
$ git show --stat HEAD
commit 06c60a7d3c9aac1b63ec91514d72939ce4d1825a
Author: Thuta Learner <learner@example.com>
Date: Thu Jan 8 10:00:00 2026 +0900
Add feature v1
app.js | 2 ++
1 file changed, 2 insertions(+)5-minute try-it
Stage a file by mistake and use `git restore --staged` to undo it without losing the content. Then make a real commit, realize you forgot something, fix it, and use `git commit --amend` to fold the fix into the same commit instead of creating a new one. Compare `git log --oneline` before and after each step to confirm exactly what changed.
One important caution
`git commit --amend` rewrites the last commit's hash — if you already pushed that commit, amending and pushing again requires a force-push, which carries the same shared-history risk as `git reset` on pushed commits.
`git restore <file>` (without `--staged`) discards the working directory change permanently — make sure you actually want it gone, not just unstaged, before running it.
Git Documentation: git-restore — Git & GitHub