Gitgit revert

git revert

git revert is the safe undo for shared history. Instead of rewriting the past, it appends a new commit whose changes are the exact inverse of an older commit. The bad commit stays in history; you just added a fix on top of it. That makes revert the only undo command you can use without panic on a branch other people have pulled.

How revert differs from reset

Reset rewinds; revert appends

Text
Before:            A ─ B ─ C  ◀── main

git reset --hard B:   A ─ B  ◀── main      (C is now unreachable)

git revert C:         A ─ B ─ C ─ C'  ◀── main
                                ▲
                                "Revert C" — applies the inverse of C
                                so the working state matches B again.
The basic revert

Undo a commit

Bash
# Revert a specific commit by SHA
git revert 9c1a2b3

# Revert the most recent commit
git revert HEAD

# Revert two commits ago
git revert HEAD~2

# Git opens the editor with a default message like:
#   Revert "Add broken feature"
#   This reverts commit 9c1a2b3...
Multiple commits

Revert several at once

Bash
# Multiple specific commits — creates one revert commit per source commit
git revert A B C

# A range — reverts everything in the range, newest first
git revert OLD..NEW
# Note: the range is exclusive of OLD and inclusive of NEW
--no-commit: combine reverts into one

Pass --no-commit (or -n) to stage the inverse changes without committing. You can chain several reverts and then make a single commit at the end.

One commit for many reverts

Bash
git revert --no-commit A
git revert --no-commit B
git revert --no-commit C
git commit -m "Roll back broken feature (A, B, C)"
Reverting a merge commit
Merge commits have two parents, so Git does not know which side of the merge to consider “mainline.” You have to tell it with `-m <parent-number>`. The first parent (`-m 1`) is usually the branch you merged *into*; the second (`-m 2`) is the branch you merged *from*.

Roll back a merge

Bash
# Find the merge commit
git log --oneline --merges

# Revert it, keeping main (parent 1) as the mainline
git revert -m 1 <merge-sha>
# This produces a commit that undoes everything the merge brought in.
Warning
Once you revert a merge commit, you cannot simply re-merge the same branch later — Git will think those changes were already merged and skip them. To re-introduce the work you need to revert the revert, or rebase the feature branch on top of the new main.
Conflicts during revert

If the changes in the target commit overlap with later commits, Git will pause with a conflict, just like a merge. You resolve the conflict manually, stage the result, and continue.

The revert conflict loop

Bash
git revert 9c1a2b3
#   CONFLICT (content): Merge conflict in src/util.js
#   error: could not revert 9c1a2b3...

# Fix the conflict markers in the affected files
vim src/util.js
git add src/util.js

git revert --continue        # finish creating the revert commit
# or
git revert --skip            # drop this revert, continue with the next
# or
git revert --abort           # undo the whole revert operation
revert vs reset (when to pick which)

Situation

Pick

Already pushed, others have pulled

git revert

Local commits only, never shared

git reset is fine

Need to keep the bad commit visible in history

git revert

Want history to look like the mistake never happened

git reset (must force-push)

Reverting one bad change inside a long-merged feature

git revert

When revert is not the right tool
  • You want to throw away your own unpushed commits — use git reset and avoid bloating history with revert commits.

  • You need to remove a leaked secret from history — revert leaves the secret in the old commit. Use git filter-repo (and rotate the secret).

  • You are reverting a revert — possible, but think hard. The “revert of the revert” often surprises teammates. Prefer cherry-picking the original work onto a fresh commit.

Mental model
`git revert <C>` is shorthand for: *“compute the diff that turned the tree from C's parent into C, then apply the opposite of that diff, and commit it on top of HEAD.”* History grows forward; nothing is rewritten.
Tip
On a long-lived shared branch, treat `revert` as the default and `reset` as the exception. Future-you (and everyone else who already pulled) will thank you.