Undoing Changes
Which command you want depends entirely on where the change you want to undo currently lives: working directory, staging area, or already committed (and possibly already pushed).
Quick decision guide
| You want to… | Use |
|---|---|
| Discard unstaged edits to a file | git restore <file> |
| Unstage a file (keep the edits) | git restore --staged <file> |
| Rewrite the last commit's message/content | git commit --amend |
| Move the branch pointer, keep changes in the working dir | git reset --mixed <commit> (default) |
| Move the branch pointer and discard all changes | git reset --hard <commit> |
| Undo an already-pushed commit safely | git revert <commit> |
| Recover a commit you think you lost | git reflog |
Working directory & staging area: git restore
git restore file.txt # discard unstaged changes to a file
git restore . # discard all unstaged changes
git restore --staged file.txt # unstage, keep the edits in place
git restore --source=HEAD~1 file.txt # revert a file to an older commit's version
Moving the branch pointer: git reset
git reset <commit> moves the current branch to point at <commit>. What
happens to everything after that point depends on the flag:
| Mode | Branch pointer | Staging area | Working directory |
|---|---|---|---|
--soft | moved | unchanged (still has the undone commits' changes staged) | unchanged |
--mixed (default) | moved | reset to match target | unchanged (changes become unstaged) |
--hard | moved | reset to match target | reset to match target — destructive |
git reset --soft HEAD~1 # undo last commit, keep changes staged
git reset HEAD~1 # undo last commit, keep changes unstaged
git reset --hard HEAD~1 # undo last commit and discard the changes entirely
git reset --hard origin/main # throw away local commits, match the remote exactly
--hardpermanently discards uncommitted work in the reset range. Double checkgit statusandgit diffbefore running it. Neverresetcommits that have already been pushed and might be in someone else’s history — userevertinstead.
Undoing a public commit: git revert
git revert creates a new commit that applies the inverse of an existing
one, leaving history intact — safe for commits others may have already
pulled:
git revert <sha> # revert one commit
git revert HEAD~3..HEAD # revert a range
git revert --no-commit <sha> # stage the revert without committing yet
Recovering lost commits: the reflog
Git rarely deletes a commit outright — even after a reset --hard or a
branch deletion, the commit objects usually stick around until garbage
collected. The reflog records every position HEAD has pointed to:
git reflog # list recent HEAD movements with their SHAs
git checkout <sha-from-reflog> # inspect a "lost" commit
git branch recovered <sha> # turn it back into a real branch
The reflog is local-only (not pushed or cloned) and entries expire after
90 days by default (gc.reflogExpire) — treat it as a safety net, not
permanent storage.
Removing untracked files
git clean -n # dry run: show what would be deleted
git clean -f # delete untracked files
git clean -fd # also delete untracked directories