git restore vs git reset
git reset was doing two unrelated jobs, which is why it always felt confusing.
git restore split them apart.
git reset moves where the branch pointer is, and optionally updates the index and
working tree to match:
git reset --soft HEAD~1 # move the branch back, keep everything staged
git reset HEAD~1 # move the branch back, unstage the changes
git reset --hard HEAD~1 # move the branch back, discard the changes
git restore never touches the branch pointer. It copies file contents from some
source into the index, the working tree, or both:
git restore --staged file.txt # unstage it, leave my edits alone
git restore file.txt # throw away my edits, take the staged version
git restore --source=HEAD~2 file.txt # take an old version of just this file
So the rule I’m keeping: if I want to undo a commit, that’s reset. If I want to undo
a change to a file, that’s restore.
The --staged / --worktree flags are the part worth remembering — restore defaults
to --worktree only, which is why git restore file.txt on a staged file appears to do
nothing visible in git status.
Where I learned it
git restore --help, after the fourth time reaching for git reset HEAD <file> out of
muscle memory.