Knowing exactly what each undo command touches is what makes Git feel safe instead of scary.
Undo commands differ in which of the three areas (working tree, index, HEAD) they change.
`git restore` works on files:
git restore file: discard unstaged edits, copying the index version back to the working tree. The edits are gone for good.git restore --staged file: unstage, copyingHEAD's version into the index; your edits stay on disk.git restore --source=HEAD~3 file: bring back an older version of a file.
`git reset <commit>` moves the current branch pointer to another commit, and then optionally updates the other areas:
--soft: move the branch only. The undone commits' changes stay staged. Useful to squash the last few commits into one.--mixed(default): also reset the index. Changes stay in the working tree, unstaged.--hard: also reset the working tree. Uncommitted changes are destroyed.
So git reset --soft HEAD~1 "uncommits" the last commit but keeps its work staged, and git reset --hard origin/main throws away everything local to match the remote.
Reset rewrites the branch's history: the commits after the new position are no longer on the branch (they're still recoverable through the reflog for a while). That's fine for commits only you have, but if they've been pushed, others still have them and your next push would need force. For shared history, use git revert, covered next.
Before any --hard, run git status and git stash anything you might want.
Example
git restore config.yml # throw away unstaged edits
git restore --staged secrets.txt # unstage, keep the file as is
git restore --source=v1.4 -- api.py # old version of one file
git reset --soft HEAD~3 # undo 3 commits, keep changes staged
git commit -m "Add search (squashed)"
git reset HEAD~1 # --mixed: uncommit + unstage, keep edits
git reset --hard origin/main # discard ALL local commits and editsRemember
- restore = files; reset = move the branch pointer
- --soft keeps changes staged, --mixed unstaged, --hard deletes them
- Reset only commits you haven't shared; use revert for pushed ones
Watch out for
- Running git reset --hard with uncommitted work you wanted — uncommitted changes are not in the reflog and can't be recovered.