Revert — the safe undo
git revert undoes a commit by adding a NEW commit that inverts it, leaving history intact — so it is safe on shared, pushed branches where reset is not. Reverting a merge needs -m 1 to pick the mainline parent, and --no-commit lets you batch several reverts.
A bad commit just landed on main, and ten teammates have already pulled it. You want it undone — but if you rewind history with git reset, everyone else’s repository now disagrees with yours, and the next person to push re-introduces the bad commit. There is an undo built exactly for this situation: git revert. Instead of erasing the past, it writes a new commit that cancels the old one out.
By the end of this lesson you will be able to choose between revert and reset based on whether the history is shared, and revert ordinary commits and even merge commits correctly.
After this lesson you can explain how git revert creates an inverse commit that preserves history, justify why that makes it safe on shared branches, contrast it with git reset, and revert a merge commit using -m 1.
git revert undoes a commit by adding a new commit, not by deleting the old one. Given a commit, git computes the exact opposite change — every added line becomes a removed line and vice versa — and records that as a fresh commit on top of your branch:
git revert <commit>The original commit stays in history, fully visible in git log. Below it now sits an “inverse” commit whose net effect cancels it. The end state of your files matches “as if that commit never happened,” but the record of both the change and its undo is preserved.
Because it only adds commits, revert never rewrites shared history — which is what makes it safe. git reset moves the branch pointer backward and drops commits; if you have already pushed, everyone who pulled now has commits your branch no longer claims, and reconciling that requires force-pushes and coordination. revert only ever moves the branch forward with a new commit, so every other clone fast-forwards normally on the next pull. That is the whole rule:
revert -> safe on public / pushed branches (history preserved)
reset -> only for local, unpushed history (history rewritten)Reverting a merge commit needs -m to pick which side is “mainline.” A normal commit has one parent, so its inverse is unambiguous. A merge commit has two parents, and git cannot guess which line of history you want to keep. You tell it with -m (the parent number, 1-based):
git revert -m 1 <merge-commit>-m 1 means “treat parent 1 — the branch you merged into, usually main — as the mainline, and undo everything the merged branch brought in.” Omit -m on a merge and git refuses with an error rather than guess. Choosing the wrong parent reverts the opposite set of changes, so confirm with git show <merge-commit> first.
▸Edge cases
Reverting a merge has a subtle trap: it undoes the code the merge introduced, but git still records that the branch was merged. If you later re-merge the same feature branch to “bring the work back,” git sees nothing new to merge and the code does not return. The standard fix is to revert the revert (git revert <the-revert-commit>) when you are ready to reinstate the feature, rather than re-merging. This is a known sharp edge worth remembering.
--no-commit lets you stage several reverts into one commit. By default each revert makes its own commit. To undo a run of commits and record the combined undo as a single commit, stage them without committing, then commit once:
git revert --no-commit <commit-a> <commit-b> <commit-c>
git commit -m "Revert the failed rollout (3 commits)"Each --no-commit revert applies its inverse to the index and working tree but pauses before committing, so they accumulate. This keeps history readable when several commits formed one logical mistake.
Undo a deploy-breaking commit on a shared main.
Commit 9f1c2ab (“Switch to new auth client”) shipped to main, was pulled by the team, and is now crashing production. You must undo it without rewriting history everyone else holds.
git log --oneline -3
# 9f1c2ab Switch to new auth client <- the culprit
git revert 9f1c2ab
# git opens an editor with a prefilled message:
# Revert "Switch to new auth client"
# This reverts commit 9f1c2ab...
git pushThe push fast-forwards cleanly for everyone — their next git pull simply adds your revert commit on top. Production is fixed, and git log still shows both the original change and its revert, so the history of what went wrong and how it was undone is intact. Had you run git reset --hard 9f1c2ab^ and force-pushed instead, every teammate’s branch would have diverged and the next careless push could have resurrected the bug.
▸Why this works
Why is rewriting shared history so harmful, mechanically? Each commit’s identity (its SHA) includes its parent. Drop or rewrite a commit and every commit after it gets a new SHA. Anyone who based work on the old SHAs now has a branch that shares no common tip with yours; git treats it as divergence and produces confusing merge conflicts or duplicated commits. Revert sidesteps all of this by never changing an existing SHA — it only ever adds new ones.
A bad commit is already pushed to a shared branch that teammates have pulled. Which undo is safe, and why?
git revert undoes a commit by appending a new inverse commit, leaving the original — and all surrounding history — untouched. Because it only moves the branch forward, it is safe on shared, pushed branches where git reset (which rewrites history) would force divergence on everyone. Rule of thumb: revert for public history, reset for local-only history. Reverting a merge commit requires -m 1 to name the mainline parent, and --no-commit batches several reverts into one. Next you will learn git reflog — the safety net that recovers commits even after a reset you regret.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.