How the three commands affect repository state
Git tracks three separate areas: the working directory that holds your files, the index that stages changes for the next commit, and the commit history that records snapshots. Each undo command touches these areas differently. Revert operates only at the commit level by creating an inverse patch and recording it as a new commit. It never alters the working directory or index unless you have uncommitted changes that conflict. Reset can update all three areas depending on the mode you choose. The default mixed mode updates the index to match the target commit while leaving the working directory untouched. Hard mode overwrites both the index and working directory. Restore focuses on paths rather than commits and pulls content from a source commit or the index into the working tree or staging area.
- git revert creates a new commit that reverses the selected change and leaves history intact
- git reset --soft moves HEAD only and keeps staged and working changes
- git reset --mixed updates the index but leaves the working tree alone
- git reset --hard replaces the index and working tree with the target commit
- git restore updates specific files from HEAD or another source without moving HEAD
Safe undo on branches others have cloned
Once a branch is pushed and teammates have fetched it, any command that rewrites commits creates divergent histories. Revert is the standard tool for these situations because it adds a new commit that everyone can merge or pull without force-pushing. The command inspects the patch introduced by the target commit and records the opposite changes. You can revert a single commit or a range, and Git will create one new commit per reverted commit by default. After the revert commit is pushed, the original faulty commit remains visible in the log, which helps future debugging and code review.
Always run git status first to confirm the working tree is clean. If uncommitted changes exist, stash or commit them before reverting. Revert will prompt you to resolve conflicts if the inverse patch overlaps with later work. The resulting commit message starts with Revert and includes the original commit subject, making the intent clear in the history.
- git revert creates the reversing commit on the current branch
- git revert --no-commit lets you review the changes before committing
- git revert HEAD~2 reverts the second most recent commit
- Push the revert commit so teammates receive the correction
Using reset on private branches only
Reset is appropriate when you have not yet shared the commits you want to discard. It moves the current branch pointer to a different commit and optionally updates the index and working tree. Because it removes commits from the visible history, teammates who have already fetched those commits will encounter errors on their next pull. The safest practice is to run reset only on local feature branches that you created and have not pushed, or on branches you are willing to force-push after coordinating with the team.
Choose the mode carefully. Soft mode is useful when you want to combine several small commits into one larger commit. Mixed mode, the default, unstages changes so you can edit them before committing again. Hard mode is the most destructive because it permanently discards uncommitted work in the working directory. Before using hard mode, consider whether git stash or git checkout would preserve the changes instead.
- git reset --soft HEAD~3 keeps all changes staged for a new commit
- git reset --mixed HEAD~1 unstages the last commit while keeping files
- git reset --hard origin/main discards everything since the last push
- Never reset a branch that has been pushed unless you coordinate a force push
Restoring files without moving history
Restore is the right choice when you need to recover the contents of specific files rather than entire commits. It can pull a file from HEAD, from the index, or from any other commit. Because it does not change which commit HEAD points to, it is safe on both private and shared branches. The command is especially helpful after you have accidentally edited a file or when you want to unstage a single file without affecting other staged changes.
By default, restore pulls from the index into the working tree. Adding the --staged flag restores the index from HEAD. You can combine both flags to restore a file completely to the state recorded in HEAD. When you need content from an older commit, supply the --source option followed by a commit reference.
- git restore replaces the working copy with the staged version
- git restore --staged unstages the file without touching the working tree
- git restore --source=HEAD~2 restores the file as it existed two commits ago
- git restore --staged --worktree resets both index and working tree
Practical decision checklist
Before running any undo command, answer three questions: Has the commit been pushed? Do you need to change history or only files? Are other people depending on the current state? If the answer to the first question is yes, reach for revert. If the branch is private and you want to discard commits, reset is acceptable. If you only need to fix file contents, restore is usually sufficient. After any operation that changes history, run git status and git log to verify the result before pushing.
- Check git status before every undo operation
- Prefer revert when the branch is shared
- Use reset only on branches you control
- Restore individual files when history should stay untouched
- Document the reason for the change in the commit message