A commit went wrong and you need it gone — but the branch is shared, so rewriting history
is off the table. That is exactly what git revert commit is for: instead of
deleting the commit, it adds a new commit that undoes it, leaving the history intact and safe
for everyone who already pulled. This guide covers every case you will actually hit — how to
revert to previous commit state, how to git revert last commit,
the special (and confusing) case of how to revert a revert commit to bring a
change back, reverting pushed commits, reverting merge commits, and where
git revert ends and git reset begins. Every command is copy-paste
ready, and the last section covers the safety step most tutorials skip:
verifying exactly what the revert will produce before you commit it.
Quick Answer: Which Command Reverts What
"Revert" means two different things depending on who is asking. Some people want to undo a single bad commit; others want to roll the whole branch back to an earlier point. The table below maps each intent to the right command. Details follow in the linked sections.
| What you want to do | Command | History rewritten? | Safe on a shared branch? |
|---|---|---|---|
| Undo one specific commit, keep it in history | git revert <sha> | No — adds a new commit | Yes |
| Undo the most recent commit safely | git revert HEAD | No — adds a new commit | Yes |
| Undo everything after commit X, keep history | git revert --no-commit X..HEAD | No — adds inverse commits | Yes |
| Roll the branch back to commit X, drop later commits | git reset --hard X | Yes — moves HEAD back | No — local only |
| Re-apply a change that was reverted | git revert <revert-sha> | No — adds a new commit | Yes |
| Undo a merge commit | git revert -m 1 <merge-sha> | No — adds a new commit | Yes |
The single most important distinction: git revert grows history forward with a
new commit and is safe on any branch, while git reset moves the branch pointer
backward and is only safe on local commits nobody has pulled. If the commit has been pushed,
reach for revert. For the broader menu of undo tools — reset modes, amend, and
reflog recovery — see the companion guide on
how to undo your last commit.
What git revert Actually Does (and Doesn't)
git revert takes a commit you point it at, computes the inverse of that commit's
diff, and records the inverse as a brand-new commit on top of your current branch. The target
commit is never touched or removed. If commit C added three lines, the revert
commit deletes those same three lines. The net effect on your files matches a world where
C never happened — but both C and its revert stay in the log.
What git revert does not do:
- It does not remove the original commit from
git log. - It does not move the branch pointer backward the way
git resetdoes. - It does not "revert the repository to a previous state" by discarding later commits — that is a reset, covered in the rolling-back section below.
- It does not require a force-push, because history is preserved.
Because the revert is a normal commit, it carries its own diff. You can inspect that diff
before or after committing — it is a standard unified diff with additions and deletions
flipped relative to the original commit. If the notation is unfamiliar, the
unified diff format guide walks through reading the
+ and - hunks line by line.
How to Revert a Commit: git revert <hash>
The core workflow has two steps: find the commit's SHA, then revert it.
# Step 1: find the commit hash you want to undo
git log --oneline
# Example output:
# 9f2c1ab (HEAD -> main) docs: update readme
# 3d81e0f feat: add pricing table
# a1b2c3d fix: correct tax rounding
# 77e4f90 chore: bump deps
# Step 2: revert that specific commit by its hash
git revert 3d81e0f
Git computes the inverse of commit 3d81e0f and opens your editor with a
pre-filled message: Revert "feat: add pricing table". Save and close the editor
and the revert commit is created. The pricing-table changes are undone, but both the original
feature commit and its revert remain in the log — a clean, auditable record of what was added
and then rolled back.
If the reverted commit conflicts with later changes, git pauses and asks you to resolve the conflict, exactly like a merge:
# If a revert hits a conflict:
git status # see which files conflict
# ...edit files to resolve the conflict markers...
git add <resolved-files>
git revert --continue
# Or bail out entirely and return to the pre-revert state:
git revert --abort You can also revert several individual commits in one command; git creates one revert commit per target, newest first:
# Revert three specific commits (each gets its own revert commit)
git revert 3d81e0f a1b2c3d 77e4f90 Revert the Last Commit: git revert HEAD
When the commit you want to undo is the most recent one, you don't need to look up a SHA at
all — HEAD already points to it.
# Revert the most recent commit safely (creates a new revert commit)
git revert HEAD
# Revert the commit before the last one
git revert HEAD~1
# Revert the last commit without opening the editor (accept default message)
git revert HEAD --no-edit
This is the safe counterpart to git reset HEAD~1. Both "undo the last commit,"
but git revert HEAD adds a new commit and leaves history untouched, while
git reset HEAD~1 rewrites history by moving the branch pointer back. On a shared
branch, only the revert is safe. If the last commit is still local and unpushed and you would
rather it vanish completely, the reset modes are covered in the
undo last commit guide.
A common follow-up: you reverted the last commit, then realized you actually wanted to keep it. Because the revert is just another commit, you undo it the same way — see reverting a revert below.
Revert to a Previous Commit: Rolling Back Multiple Commits
"Revert to a previous commit" usually means something bigger than undoing one commit: you
want the branch to look like it did at commit X, discarding or neutralizing
everything committed since. There are two fundamentally different ways to do this, and picking
the wrong one on a shared branch causes real damage.
Option 1 — Revert a range (safe, keeps history)
To neutralize every commit after X while keeping the full history, revert the
range X..HEAD. Use --no-commit so git stages all the inverse
changes into one commit instead of creating a separate revert per commit:
# Stage the inverse of every commit after X, then commit once
git revert --no-commit a1b2c3d..HEAD
git commit -m "Revert to a1b2c3d: roll back broken release"
# Push normally — no force needed
git push origin main
The branch now produces the same files as commit a1b2c3d, but B,
C, and D still appear in the log, followed by a single revert
commit. This is the correct way to revert to a previous commit on
main or any branch others share.
Option 2 — Hard reset (rewrites history, local only)
If the commits after X are local and unpushed, and you want them gone entirely,
move the branch pointer back with a hard reset:
# Inspect what you are about to drop first
git log --oneline a1b2c3d..HEAD
# Move HEAD back to X and discard B, C, D and their changes
git reset --hard a1b2c3d Never run git reset --hard on commits that have been pushed to a shared
branch. It rewrites history and forces every teammate to reconcile a diverged
branch. When commits are already public, use Option 1. Before any hard reset, diff what you
will lose — git diff a1b2c3d HEAD shows the combined change, and the
git diff between two files guide covers
how to scope that comparison down to individual files.
How to Revert a Revert (Undo a git revert)
Here is the case that trips people up. You reverted a commit — say a feature — and later decided the feature should come back after all. Because a revert is just an ordinary commit, you revert the revert. The double negative restores the original change.
# Step 1: find the SHA of the REVERT commit (not the original)
git log --oneline
# 5c7a9d2 (HEAD -> main) Revert "feat: add pricing table"
# 3d81e0f feat: add pricing table
# a1b2c3d fix: correct tax rounding
# Step 2: revert the revert to bring the feature back
git revert 5c7a9d2
Git creates a new commit named Revert "Revert "feat: add pricing table"" — the
doubled title is expected and correct. The pricing table is now live again, and the log tells
the whole story: added, rolled back, restored. This is the standard way to re-apply work on a
shared branch without rewriting history.
Why not just cherry-pick or re-commit? You could, but reverting the revert is cleaner: it guarantees you re-apply exactly what was removed, byte for byte, with no chance of a hand-typed reconstruction drifting from the original. When the reverted change spanned many files, that guarantee matters — and you can confirm it with a diff, as the verification section shows.
One caveat specific to reverting a reverted merge: if the original revert was of a merge commit, re-merging the branch later will not re-introduce the changes, because git still considers them merged. In that situation you revert the revert (as above) rather than attempting a fresh merge. Git's own git-revert documentation describes this reverted-merge edge case in detail.
Reverting a Pushed Commit on a Shared Branch
This is the scenario git revert was built for. Once a commit reaches a shared
remote — main, develop, a release branch — other developers may have
already pulled it. Rewriting history at this point (with reset plus a force-push)
forces everyone to reconcile a diverged branch and, on protected branches, the remote rejects
the push outright.
# Safely undo a pushed commit — no history rewrite, no force-push
git revert <sha>
git push origin main
Because the revert is a forward commit, teammates simply git pull and receive the
revert like any other commit. Compare that to the reset-and-force-push path, which the
undo last commit guide covers in full — that
path is only appropriate on a private branch you alone own, and even then only with
--force-with-lease.
When revert is the right choice:
- The commit is on
main,master, or any branch teammates share. - Others may already have pulled the commit.
- The branch has protections that reject non-fast-forward pushes.
- You want an audit trail showing exactly what was reversed and when.
Windows developers running these commands in a terminal can compare file states before and after a revert with the tools in the PowerShell diff guide; on Linux and macOS, the diff command in Unix guide does the same from a shell.
Reverting a Merge Commit with -m
Merge commits have two parents, so a plain git revert fails with
"commit is a merge but no -m option was given." Git cannot guess which side of the
merge you consider the "mainline." You tell it with the -m flag, passing the
parent number.
# List the merge commit's parents to confirm which is which
git show --no-patch --format="%H %P" <merge-sha>
# Revert the merge, keeping parent 1 (the branch you merged INTO — usually main)
git revert -m 1 <merge-sha>
In almost every case -m 1 is what you want: it keeps the mainline and undoes the
changes the feature branch introduced. Use -m 2 only in the rare case where the
second parent is the line you want to preserve. Verify with a diff afterward — reverting a
merge with the wrong parent silently reverses the opposite set of changes, and a quick
side-by-side comparison catches it before it ships.
git revert vs git reset: Which One to Use
This is the decision that determines whether your undo is safe. Both commands "undo" work, but they do it in opposite ways and carry opposite risks.
| Dimension | git revert | git reset |
|---|---|---|
| What it does | Adds a new commit that inverts a target commit | Moves HEAD (and optionally index/tree) to an earlier commit |
| Effect on history | Preserved — history grows forward | Rewritten — later commits drop off the branch |
| Safe after push? | Yes — no force-push needed | No — requires a force-push and breaks other clones |
| Which commit it can target | Any commit at any point in history | Only works backward from the current HEAD |
| Keeps your file changes? | N/A — produces a new state via inverse commit | --soft/--mixed keep them; --hard discards |
| Best for | Undoing a pushed or shared commit | Cleaning up local commits before they leave your machine |
The mnemonic: revert = public undo (safe, additive), reset =
private undo (destructive, local). If in doubt about whether a commit has left your
machine, run git log --oneline origin/main..HEAD. Zero results means the commits
are local and reset is safe; any results mean they are pushed and you should
revert.
Useful git revert Options: --no-commit, --no-edit, --abort
A handful of flags cover almost every real-world revert:
| Flag | What it does |
|---|---|
--no-commit (-n) | Stage the inverse changes without committing, so you can revert several commits into one commit or review first |
--no-edit | Use the default revert message without opening an editor |
-m <parent> | Required when reverting a merge commit; picks which parent is the mainline |
--continue | Resume a revert after you resolve conflicts |
--abort | Cancel an in-progress revert and restore the pre-revert state |
--skip | Skip the current commit during a multi-commit revert that hit a conflict |
# Revert several commits but bundle them into a single commit
git revert --no-commit HEAD~2..HEAD
git commit -m "Revert last two commits in one shot"
# Revert with no editor prompt, accepting the default message
git revert --no-edit HEAD
# Abandon a revert that ran into a messy conflict
git revert --abort --no-commit is the workhorse: it lets you batch a range of reverts into one clean
commit, or pause to inspect the staged inverse diff before finalizing it. That inspection step
is worth building into your habit, and the next section shows the fastest way to do it.
Verify the Revert: Diff Before and After
The step most tutorials skip: confirm the revert produced exactly the change you intended
before you push it. Reverting the wrong SHA, or reverting a merge with the wrong
-m parent, produces a commit that looks plausible but reverses the wrong
code. A ten-second diff catches it.
# See exactly what the revert commit changed
git show HEAD
# Compare the branch tip against the commit you meant to restore
git diff <target-sha> HEAD
# Scope the check to a single file that mattered
git diff <target-sha> HEAD -- src/pricing.ts
When the revert touches many files, a wall of terminal output is hard to scan. This is where a
visual side-by-side view pays off: copy the "before" and "after" versions of a file into
Diff Checker and every added, removed, and unchanged line is color-coded at a
glance — no squinting at +/- hunks. It runs entirely in your browser,
so nothing you paste leaves your machine.
The workflow pairs naturally with the reading skills from the
unified diff format guide: git show emits a
unified diff, and a visual tool turns that into a two-column comparison you can verify in
seconds. For a reverted change spanning a whole feature, that confirmation is the difference
between shipping a correct rollback and shipping a new bug.
Frequently Asked Questions
How do I revert to a previous commit in Git?
There are two safe ways. To undo the changes from every commit after a target while
keeping history intact, run git revert --no-commit <target>..HEAD then
git commit — this adds new commits that reverse the range. To move the branch
pointer back and discard the later commits entirely (local branches only), run
git reset --hard <target-sha>. Use revert on shared branches because it
never rewrites history; use reset only when the commits are local and unpushed.
What is the difference between git revert and git reset?
git revert creates a new commit that applies the inverse of a target commit's
changes, so history grows forward and nothing is rewritten — the safe option for commits
already pushed to a shared branch. git reset moves HEAD (and
optionally the index and working tree) to an earlier commit, shortening the local history;
it is only safe for commits that have not been pushed. Rule of thumb: revert for shared
history, reset for local history.
How do I undo a git revert?
A revert is an ordinary commit, so you revert it again. Find the SHA of the revert commit
with git log, then run git revert <revert-commit-sha>. This
creates a second revert that cancels the first, restoring the original change. Reverting a
revert is the standard way to re-apply a change that was previously rolled back on a shared
branch — it keeps the full audit trail instead of rewriting history.
Does git revert delete the commit?
No. git revert never removes the original commit from history. It leaves the
target commit exactly where it is and adds a new commit whose diff is the inverse of the
target's changes. The end state of your files matches what they would be without the
reverted change, but both the original commit and the revert commit remain visible in
git log. If you need the commit to disappear from history entirely, that
requires git reset or an interactive rebase, not revert.
How do I revert a commit that has already been pushed?
Use git revert <sha> to create a new commit that reverses the pushed
commit, then git push normally — no force-push is needed because history is
not rewritten. This is the correct approach for main, master, or
any branch teammates have pulled. Avoid git reset plus a force-push on shared
branches: it rewrites history and breaks every other clone of the branch. For the full menu
of undo options, see the undo last commit guide.