CodeOath
← All posts
Git65 min total · 17 parts

Git Internals and Workflows: Branching, Merging, and Rebasing

Contents — Part 11 of 17: git reset vs. git revert vs. git checkout/git restore
Part 11 of 17 · ~2 min

git reset vs. git revert vs. git checkout/git restore

These all "undo" something, but in very different ways and with very different safety profiles on shared history:

CommandWhat it doesRewrites history?Safe on pushed/shared commits?
git reset --soft <commit>Moves the branch pointer, keeps all changes stagedYesNo
git reset --mixed <commit> (default)Moves the branch pointer, keeps changes in the working tree, unstages themYesNo
git reset --hard <commit>Moves the branch pointer, discards later commits and uncommitted changes entirelyYesNo
git revert <commit>Creates a new commit that undoes an earlier one's changesNo — adds to history insteadYes
git restore <file>Discards uncommitted changes to a file in the working tree, back to the last commit (or a given commit with --source)N/A — doesn't touch commit history at allN/A
git checkout <commit> -- <file>Restores a specific file's contents from a specific commit, without moving any branch pointerNoN/A

If a bad commit is already public (pushed, and possibly already pulled by someone else), git revert is almost always the right tool — it fixes the problem by adding a new, honest commit that undoes the damage, without rewriting anything anyone else has already based work on. git reset --hard is fine, even useful, on commits that exist only locally and haven't been shared with anyone.

git restore (a newer, more explicit split-out of what git checkout used to do for both files and branches in one overloaded command) is worth preferring for file-level undo specifically, since git checkout alone is ambiguous about whether you mean "switch branches" or "discard changes to this file" — git restore can only mean the latter.