Git revert is the command you use to undo a specific commit without rewriting history. It creates a new commit that does the exact opposite of the commit you want to undo.
It doesn't erase history — it fixes the mistake by adding a new page on top of the existing one.
In Git, every commit is a snapshot. The git revert command never deletes that snapshot. Instead, it creates a new commit that reverses the changes of the target commit. This way history stays intact and simply moves forward with an added commit.
Think of it like a bookkeeping ledger. If you make a mistake on a line, you don't erase it. You add a new line that says "this entry is canceled." The ledger stays consistent, and nothing becomes a hidden, scratched-out record.
When to Use Git Revert
Once you push a commit to a remote repository, rewriting history becomes risky. If you delete or change a commit locally, your local branch diverges from the remote. When your teammates pull, they end up with a broken commit chain — and that's a quick way to cause chaos across the team.
In short, git revert is the safe and widely accepted way to undo commits that have already been pushed.
Why Use Git Revert Instead of Git Reset?
At first glance git reset and git revert look similar, but they work in fundamentally different ways. git reset rewrites commit history, while git revert undoes changes while preserving history. That's exactly why git revert is the preferred choice for commits that have already been pushed.
| Command | Rewrites History? | Safe? | Use on Pushed Commits? |
|---|---|---|---|
git reset |
Yes | Can be risky | Generally no |
git revert |
No | Yes | Yes |
In short: if a commit has been pushed and you're working on a team, git revert is almost always the safest option.
Example Usage Scenario
Let's say we added the following line to a file and committed it:
+ background-color: red;
This commit now lives in history as a1b2c3d. To quickly find the commit, you can run git log --oneline. (To learn more about commit history, check out our git log article.) In this case:
- A) git reset — Git removes that commit from history as if it never happened.
- B) git revert — Git looks at that commit and reverses it: whatever was added gets removed, whatever was removed gets added back. In other words, you create an "inverse commit." For example:
- background-color: red;
We end up creating an "undo" commit that applies the change above. The original commit stays in the Git history.
The old record isn't deleted — we've simply made a deliberate note that we reverted it.
Now our git log output looks like this:
d4e5f6a Revert "Add red background color"
a1b2c3d Add red background color
9f8e7d6 Initial commit
How to Find the Commit Hash for Git Revert
You can quickly find the hash of the commit you want to undo with git log --oneline. If it's the most recent commit, git revert HEAD is the handiest option. To undo the previous commit or the one before that, you'd use git revert HEAD~1 and git revert HEAD~2.
How to Use Git Revert
git revert <commit-hash>
When you run this command, Git:
- Calculates the inverse of whatever changed in that commit.
- Creates a new commit.
- By default, opens your editor with the commit message: Revert "original message"
Undoing the Last Commit Without a Hash
You won't always know the commit hash off the top of your head. To undo the last commit or one a few steps back, you can use HEAD.
git revert HEAD # revert the last commit
git revert HEAD~1 # revert the previous commit
git revert HEAD~3 # revert the commit 3 steps back
HEAD always points to where you currently are.
The ~ sign tells Git how many steps to go back.
A simple rule of thumb:HEAD= now,HEAD~1= one back,HEAD~3= three back.
How Git Revert Works Internally
Even though every commit in Git is a snapshot, git revert actually works on the diff. It compares the target commit with its parent, applies that diff in reverse to the working tree, and records the result as a new commit. Technically this is a three-way merge: your current HEAD is one side, and the parent of the target commit is the other.
Here's the key difference: git reset moves the branch pointer backward (commits get "left behind"). git revert, on the other hand, never moves any pointer backward; just like a normal commit, it moves HEAD one step forward. That's exactly why history stays intact.
# Starting point — we want to revert commit B
A --- B --- C (HEAD, main)
# git revert B → a new commit (B') that reverses B is added
A --- B --- C --- B' (HEAD, main)
# ↑ ↑
# B stays B's changes are undone
# Compare: if we had run git reset --hard B
A --- B (HEAD, main) # C would be erased from history (dangerous)
resetmoves the pointer,revertadds a commit. That's what we mean by "preserving history."
Most Common Git Revert Parameters
The --no-edit Parameter
git revert <hash> --no-edit
This commits directly without opening the editor. Use it when you don't want to tweak the message. It's used a lot in practice.
The --no-commit Parameter
git revert <hash> --no-commit
This performs the revert but doesn't create a commit — it stages the changes instead. Why use it? When you want to revert several commits at once and roll them all into a single commit.
git revert A --no-commit
git revert B --no-commit
git revert C --no-commit
git commit -m "Revert A, B, C"
Process Control Parameters: --continue, --abort, --skip
If a conflict comes up during a revert, the operation pauses and the repo enters a "revert in progress" state. These three parameters manage that state:
git revert --continue # finish the revert after resolving the conflict
git revert --abort # cancel the operation and restore the repo to its pre-revert state
git revert --skip # (during a range revert) skip this commit and move on to the next
We walk through how these parameters are used inside a real conflict workflow, step by step, in the How to Resolve Git Revert Conflicts section.
Reverting Multiple Commits with Git Revert
git revert A..D
This reverts the range from A to D. A is excluded, D is included. A separate revert commit is created for each commit in the range.
git revert A^..D
If you want to include A as well, use the ^ sign.
Reverting a Merge Commit
Reverting a merge commit takes an extra step, because a merge commit has two parents. Since Git can't know which one to treat as the mainline, it throws the following error:
error: commit is a merge but no -m option was given
The fix is to add the -m parameter.
git revert <merge-hash> -m 1
-m 1 means "treat the first parent (usually the main branch) as the mainline and undo the changes that came in from the merge."
You can also use -m 2, but that's rarely needed.
How to Resolve Git Revert Conflicts
If the same lines were changed in other commits after the commit you're trying to revert, Git can't apply the reverse change cleanly and pauses the revert at that point. At this stage the repo is in a "revert in progress" state.
First, it's worth checking git status to see what's going on, then you can fix the conflict markers by hand:
<<<<<<< HEAD
background-color: blue;
=======
background-color: green;
>>>>>>> parent of abc1234 (Add red background)
Keep the version you want, remove the markers, then stage the file and finish the revert:
git add style.css
git revert --continue # create the revert commit with your resolved version
If you want to back out of the operation entirely, restore the repo to its pre-revert state:
git revert --abort # restore everything to the state before the revert started
When reverting several commits at once (for example git revert A..D), if you want to skip one of the commits in the queue:
git revert --skip # skip this commit and continue with the next one
Three keys:--continueto finish,--abortto back out completely,--skipto skip this commit. If you already know how to resolve a merge conflict, the logic is exactly the same — the only difference is that the command that wraps things up isgit revert --continue.
Git Revert vs Git Reset: What's the Difference?
| Revert | Reset | |
|---|---|---|
| History | preserved, added on top | rewritten |
| After a push | safe | dangerous |
| Teamwork | suitable | not suitable |
| Purpose | leave a "I undid this" record | say "this never happened" |
In short: if the commit has been pushed → git revert. If it's only local → git reset is an option. If there's a team involved → always git revert.
When You SHOULD Use Revert
- The commit has already been pushed.
- You're working on a team.
- You want to leave a trace of the undo.
- You need to roll something back in production urgently.
When You SHOULD NOT Use Revert
- If the commit is only local, reset is cleaner.
- If the commit you want to undo is very old with dozens of commits in between, you can run into a lot of conflicts.
- If you want to revert the revert itself, it adds extra complexity (especially with merge reverts).
Real-World Scenario
# we built a feature and pushed it
git push origin main
# then we realized production broke
# to roll it back
git revert abc1234 --no-edit
git push origin main
# Now when our teammates pull, they hit no issues at all.
What Happens If You Revert a Reverted Commit?
A revert commit is really just an ordinary commit. So you can revert it too. When you do, the original change comes back.
9f8e7d6 Initial commit
a1b2c3d Add red background ← original change
d4e5f6a Revert "Add red..." ← we reverted it
e5f6b7a Revert "Revert "Add..." ← reverting the revert = the original is back
So reverting a revert means reapplying that change. Again, history isn't broken — one more commit is simply added on top.
This is the move you reach for when, especially after reverting a merge commit, you decide "actually, we did need that feature after all."