When to Use git revert vs git reset: Engineering Decision Matrix Jump to heading
Choosing incorrectly between these two rollback commands is one of the fastest ways to corrupt shared history or lose work irreversibly. The core of the problem is that both commands can undo a commit, but they do so with fundamentally different mechanisms. This recipe, part of the Squash & Fixup Strategies workflow, gives you a precise framework for choosing correctly every time.
git revert creates a new commit that applies the inverse of a target commitβs diff. git reset moves the branch pointer backward and optionally discards index and working-tree state. The critical difference: revert preserves history and is safe on shared branches; reset rewrites history and must be restricted to local, unpushed work.
When to Use This Approach Jump to heading
Use git revert when any of the following conditions apply:
- The target commit has already been pushed to a remote branch, regardless of whether it is protected.
- The branch is shared with other engineers who have already pulled the problematic commit.
- You need a verifiable audit trail for compliance or incident post-mortems.
- A CI/CD pipeline trigger must fire on the corrective change so tests and deployments run automatically.
- You are reverting a merge commit (use
git revert -m 1).
Use git reset when all of the following conditions apply:
- The commits exist only on your local branch and have never been pushed.
- You want to restructure, rephrase, or consolidate commits before they become part of shared history β a task that pairs directly with interactive rebase workflows.
- You intend to clean up a branch before opening a pull request, using
--softto restage changes for a cleaner commit message.
Step-by-Step Recipe Jump to heading
Step 1: Identify whether the commit is shared Jump to heading
# Check if the commit SHA exists on the remote
git branch -r --contains <sha> If any remote branch is listed, the commit is shared β proceed to Step 2. If the output is empty, the commit is local only β proceed to Step 3.
Verify: An empty result from git branch -r --contains <sha> confirms the commit has not been pushed.
Step 2: Revert a shared commit Jump to heading
# Revert a standard commit β creates an inverse commit, no history rewrite
git revert <sha>
# Revert a merge commit; -m 1 selects the mainline parent (the branch before the merge)
git revert -m 1 <merge-sha>
# Suppress the interactive editor β required for CI/CD automation pipelines
git revert --no-edit <sha> Git will create a new commit whose diff is the exact inverse of <sha>. Push it normally β no force flag needed.
Verify: Run git log --oneline -5 and confirm a Revert "..." commit appears at the top of the log.
Step 3: Reset a local commit Jump to heading
# --soft: moves HEAD back but keeps changes staged β use when rewriting a commit message
git reset --soft HEAD~1
# --mixed (default): unstages changes but leaves them in the working tree
git reset --mixed HEAD~1
# --hard: discards all changes β working tree reverts to the target state entirely
git reset --hard HEAD~1 Choose the mode that matches your intent. --soft is safest for restructuring; --hard is destructive and permanent outside the reflog window.
Verify: Run git status and git log --oneline -5 to confirm HEAD has moved and the working tree reflects the expected state.
SAFETY WARNING β
git reset --hard: This command permanently discards all uncommitted modifications in the index and working tree. There is no standard undo path exceptgit reflog. Never rungit reset --hardon a branch that has already been pushed; it will create diverged history and require a force-push that breaks collaboratorsβ clones. Recovery command:git reflog | head -20β find the pre-reset SHA, thengit reset --hard <that-sha>.
Step 4: Handle a revert conflict Jump to heading
If git revert stops mid-way because the inverse diff conflicts with local changes:
# Inspect which files are in conflict
git status
# After resolving conflicts in each file, stage them
git add <resolved-file>
# Complete the revert
git revert --continue
# Or cancel entirely and return to pre-command state
git revert --abort Verify: After --continue, git status must report a clean working tree and git log --oneline -3 must show the new revert commit.
Step 5: Confirm CI/CD pipeline impact Jump to heading
For git revert, the new commit SHA triggers standard CI/CD pipeline automation without disrupting downstream consumers. For git reset followed by a force-push, verify pipeline cache invalidation and ensure --force-with-lease is used instead of --force:
# Safer alternative to --force: fails if the remote has changed since your last fetch
git push --force-with-lease origin <branch> Verify: Check your CI provider for a new pipeline run on the corrective commit. Confirm deployment gates show the expected SHA.
Exact State Transitions Jump to heading
Understanding what each command touches prevents surprises in the working tree and staged index:
| Command | HEAD | Index | Working Tree |
|---|---|---|---|
git revert <sha> | Advances (new commit) | Updated to inverse diff | Updated to inverse diff |
git reset --soft HEAD~1 | Moves back | Unchanged | Unchanged |
git reset --mixed HEAD~1 | Moves back | Cleared | Unchanged |
git reset --hard HEAD~1 | Moves back | Cleared | Overwritten |
Validation Checklist Jump to heading
Frequently Asked Questions Jump to heading
Can I use git reset on a branch that has already been pushed? Jump to heading
Technically yes, but it requires a force-push that rewrites the remote history. Any collaborator who has already pulled the old SHAs will have a diverged history that requires manual repair. For commits that have been pushed, git revert is the correct tool in almost every case. Reserve force-push resets for private feature branches where you are the only author.
Does git revert work on merge commits? Jump to heading
Yes. Merge commits have two parents, so Git needs to know which parent represents the mainline. Pass -m 1 to select the first parent β the branch main was on before the merge. Omitting -m causes Git to abort with an error on merge commits.
How do I recover if git reset --hard removed changes I needed? Jump to heading
Gitβs reflog tracks every HEAD movement independently of branch pointers and retains entries for at least 90 days by default. Run git reflog | head -20, find the SHA that represents the state before the reset, then run git reset --hard <that-sha> to restore it. If the changes were never committed at all, they are unrecoverable via Git β only external tools such as editor undo history may help.
Related Jump to heading
- Squash & Fixup Strategies β the parent workflow covering pre-merge history cleanup where
git reset --softis a core tool. - Interactive Rebase Workflows β reorder, squash, and edit commits on local branches before they become shared history.
- Safe git rebase -i for Shared Branches β detailed safety rules for running interactive rebase without breaking collaborators.
- 3-Way Merge Fundamentals β understand the underlying merge mechanics that
git revertrelies on when generating its inverse diff.