Understanding Git Revert
Git revert creates a brand new commit that undoes the changes introduced by a previous commit. Instead of altering history, it adds a layer on top, preserving the original commit and recording the reversal as a separate event. This makes revert the safest way to "undo" commits that have already been shared with others.
Why Revert Matters
- Non-destructive: The commit history remains intact, which is critical when collaborating.
- Shared branch safety: Other team members wonβt have to force-push or rebase to incorporate your reversal.
- Audit trail: The revert commit itself serves as documentation of what was undone and why.
How to Use Git Revert
To revert the most recent commit:
git revert HEAD
This opens an editor for a commit message (usually "Revert <original commit message>"). After saving, a new commit is created that undoes the changes from HEAD.
To revert a specific commit by its hash:
git revert abc123
If the commit you're reverting is a merge commit, you must specify which parent lineage to keep. Use the -m flag followed by the parent number (1 for the mainline, 2 for the merged branch):
git revert -m 1 abc123
For reverting multiple commits without creating a separate revert for each, you can use the --no-commit (or -n) flag to stage the changes and then commit them all at once. This is useful when reverting a range of commits that are consecutive:
git revert --no-commit abc123..def456
git commit -m "Revert range of commits"
Note: the range syntax abc123..def456 is inclusive of def456 but excludes abc123. Adjust accordingly or revert commits individually if needed.
If conflicts occur during a revert, Git will pause and ask you to resolve them. After fixing the conflicts, use:
git add .
git revert --continue
Understanding Git Reset
π Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Git reset moves the current branch pointer to a specified commit, and optionally updates the staging index and working directory. It rewrites history, making it powerful but dangerous when used on branches that have been pushed to a shared repository.
Why Reset Matters
- Local cleanup: Quickly discard uncommitted changes or remove commits that are still local.
- History rewriting: Before pushing, you can reshape commits to keep a clean narrative.
- Three modes:
--soft,--mixed, and--hardgive fine-grained control over what gets undone.
How to Use Git Reset
Reset modes explained:
--soft: Moves the branch pointer only. The staging area and working directory remain unchanged. All changes from the undone commits appear as staged changes ready to be committed.--mixed(default): Moves the branch pointer and resets the staging area, but leaves the working directory untouched. Changes from undone commits become unstaged modifications.--hard: Moves the branch pointer and resets both staging area and working directory. All changes from undone commits are permanently discarded.
Undo the last commit but keep changes staged (so you can re-commit with a different message or add more changes):
git reset --soft HEAD~1
Undo the last commit and unstage the changes (they remain as working tree modifications):
git reset HEAD~1
Or explicitly:
git reset --mixed HEAD~1
Completely discard the last commit and all its changes:
git reset --hard HEAD~1
Reset a branch to match a remote state (discard all local commits and changes not yet pushed):
git fetch origin
git reset --hard origin/main
Unstage a specific file (this is a mixed-mode reset on a path, not moving HEAD):
git reset HEAD file.txt
This keeps your working directory changes but removes the file from the staging area.
Important warning: After a reset to a previous commit, if the undone commits were already pushed, you'll need to force-push (git push --force) to update the remote. This can disrupt teammates. Only use reset for commits that haven't been shared.
Understanding Git Restore
Introduced in Git 2.23, git restore was designed to replace the ambiguous git checkout and git reset commands when dealing with working tree files and the staging area. It focuses solely on restoring file states from different sources.
Why Restore Matters
- Clear separation of concerns: Unlike checkout (which switches branches and restores files) or reset (which moves HEAD and operates on paths), restore has a single purpose.
- Safer defaults: It won't accidentally move your branch pointer.
- Modern workflow: Encourages explicit flags for staging vs. working directory operations.
How to Use Git Restore
Discard local changes in the working directory for a specific file (reverts to the version in the index):
git restore file.txt
Discard all local working directory changes (use with caution):
git restore .
Unstage a file (remove from staging area, keeping the working tree unchanged):
git restore --staged file.txt
Restore a file from a specific commit (without changing branches):
git restore --source abc123 file.txt
This places the version of file.txt as it existed in commit abc123 into your working directory. You can also combine with --staged to update the staging area directly:
git restore --source abc123 --staged file.txt
Restore both working directory and staging area to match a commit:
git restore --source HEAD~1 --staged --worktree file.txt
When you need to undo a bad merge or cherry-pick, restore can also help by reverting the working tree state. For example, to discard all uncommitted changes across the entire project:
git restore --source HEAD --staged --worktree .
Comparing Revert, Reset, and Restore
Although all three commands can "undo" something, they operate at different levels and have distinct consequences:
- Git Revert works at the commit history level. It adds a new commit that reverses previous changes. Perfect for public branches where rewriting history is forbidden.
- Git Reset works at the branch pointer and commit history level. It moves HEAD and optionally adjusts staging and working directory. Best for local cleanup before pushing.
- Git Restore works at the file and staging level. It never moves branch pointers, only restores file content from a source (index, HEAD, or a specific commit). Ideal for discarding uncommitted changes or unstaging.
Here's a quick decision guide:
- Need to undo a commit that has been pushed? β
git revert - Need to completely remove the last few commits that only exist locally? β
git reset --hard - Need to unstage a file but keep the changes? β
git restore --staged(orgit reset HEAD) - Need to discard local modifications to a file? β
git restore - Need to keep changes from undone commits staged for a new commit? β
git reset --soft
Best Practices
- Use revert for shared history. If a commit exists on a remote branch that others might have pulled, always use
git revert. It prevents force-push wars and keeps history linear and traceable. - Reserve reset for local-only commits.
git reset --hardcan be a lifesaver when you want to abandon an experiment, but double-check that you haven't pushed those commits. - Prefer restore over checkout for file operations.
git restorehas a clearer syntax and reduces the chance of accidentally switching branches. - Use
git reset --softto squash or reword commits locally. It leaves all changes staged, allowing you to create a single, polished commit. - Always verify with
git logorgit statusbefore destructive operations. A quick check can save you from losing important work. - Consider backing up current state with a branch. Before a hard reset or a complex revert, create a temporary branch like
backup/before-undo. That way you can always go back. - Communicate if force-pushing. If you absolutely must reset a shared branch (e.g., removing sensitive data), inform your team and coordinate the force-push to minimize disruption.
Conclusion
Git provides multiple ways to undo changes, each tailored to a specific situation. git revert keeps history safe and is your go-to for public commits. git reset rewrites local history with precision and power. git restore handles file-level recovery without touching the branch pointer. Understanding their differences and applying the right command in the right context will keep your repository clean, your team productive, and your stress levels low. Master these three tools, and you'll navigate any Git mishap with confidence.