Home DevOps Git Revert: Undo Pushed Commits Without Rewriting History
Intermediate 3 min · July 07, 2026

Git Revert: Undo Pushed Commits Without Rewriting History

Git revert safely undoes pushed commits by creating new commits.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 07, 2026
last updated
214
articles · all by Naren
Before you start⏱ 20 min
  • Basic Git commit/push workflow
  • Understanding of Git history as a DAG
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

Use git revert to create a new commit that reverses the changes of a specific commit. Unlike reset, revert is safe for public branches because it doesn't alter existing history.

✦ Definition~90s read
What is Git Revert?

Git revert creates a new commit that undoes the changes from a previous commit. It's the safe way to undo pushed commits because it doesn't rewrite shared history — it adds a forward-moving undo.

Imagine you're editing a shared document and someone accidentally deletes a paragraph.
Plain-English First

Imagine you're editing a shared document and someone accidentally deletes a paragraph. Instead of erasing their edit from the history (which would confuse everyone), you write a new paragraph that says 'Ignore the deletion — here's the original text back.' That's revert: a new commit that cancels out the old one.

Most devs learn git reset first. It's clean, it's fast, and it's absolutely the wrong tool for shared branches. I've seen teams rewrite a release branch's history at 2 AM and spend the next day untangling merge conflicts across 12 developers. Reset is for local-only work. Revert is for anything pushed.

The core problem: once you push a commit, other people may have based work on it. Rewriting that commit (via reset --hard, amend, rebase) breaks their history. Git revert solves this by creating a new commit that applies the inverse of the target commit's diff. History stays linear and everyone's local branches remain compatible.

By the end of this, you'll know exactly when to revert, how to revert merges safely, and the three gotchas that will burn you if you only read the man page.

Why Revert Exists: The Shared Branch Problem

You pushed a commit. Someone pulled it. Now you can't rewrite history without breaking their clone. Reset --hard is a local-only tool — it moves the branch pointer backward, discarding commits. If you force-push that, everyone else's local branches point to commits that no longer exist on remote. The next pull creates a mess of diverged history and merge conflicts.

Revert solves this by moving forward. It creates a new commit whose diff is the exact inverse of the target commit. The branch history remains a straight line. Everyone's local branches can fast-forward or merge cleanly. This is why every CI/CD pipeline and shared branch workflow mandates revert over reset.

Here's the production scenario: a developer pushes a commit that accidentally deletes a critical config file. The commit is now in main. Three other devs have pulled it. You can't reset — you'd break their repos. Instead, you revert: git revert a1b2c3d. This creates a new commit that restores the deleted file. Everyone pulls, gets the fix, and moves on.

revert-basic.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// io.thecodeforge — DevOps tutorial

# Simulate a bad commit
echo "delete me" > critical_config.txt
git add critical_config.txt
git commit -m "feat: add critical config"

# Oops, that file was wrong. Let's revert.
git revert HEAD --no-edit

# Now critical_config.txt is gone again (reverted).
# The revert commit message is "Revert \"feat: add critical config\""

# Verify log
git log --oneline -3
# Output:
# 3f6e2b1 Revert "feat: add critical config"
# a1b2c3d feat: add critical config
# ...
Output
3f6e2b1 Revert "feat: add critical config"
a1b2c3d feat: add critical config
...
Senior Shortcut:
Use git revert --no-edit to skip the editor and auto-generate a revert commit message. Saves time in automated rollback scripts.

Reverting a Merge Commit: The -m Flag

Merges are special. A merge commit has two parents: the mainline branch and the feature branch. When you revert a merge, Git needs to know which parent to keep. The -m flag specifies the parent number (1 = first parent, usually mainline; 2 = second parent, the feature branch).

Without -m, Git refuses to revert a merge. It's too dangerous — it can't guess which side to undo. The classic mistake: reverting a merge without -m 1 and getting an error. Then using -m 2 by accident, which reverts the mainline changes and keeps the feature branch's changes — the opposite of what you want.

Production scenario: a feature branch was merged into main, but the feature has a critical bug. You need to undo the merge, but keep mainline history intact. Run git revert -m 1 <merge-hash>. This reverts the diff that the merge introduced, effectively undoing the feature branch's changes while preserving mainline history.

revert-merge.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
// io.thecodeforge — DevOps tutorial

# Assume we have a merge commit M with parents P1 (main) and P2 (feature)
# Revert the merge, keeping mainline (parent 1)
git revert -m 1 M --no-edit

# Now the revert commit undoes all changes from the feature branch.
# But note: the original merge commit M still exists in history.

# If you later try to merge the same feature branch again, Git says:
# "Already up to date." because the feature's commits are already ancestors.
# To re-merge, you must first revert the revert:
git revert <revert-hash> --no-edit
Output
[main 9f8e7d6] Revert "Merge branch 'feature'"
1 file changed, 0 insertions(+), 10 deletions(-)
Production Trap:
Reverting a merge does NOT allow you to re-merge the same feature branch later. Git sees the feature's commits as already merged. To re-introduce the feature, revert the revert commit. This is the #1 gotcha with merge reverts.

Reverting Multiple Commits: Order Matters

To revert a range of commits, use git revert OLD..NEW. But Git applies reverts in reverse chronological order — it starts from the newest commit and works backward. This avoids conflicts: if commit B depends on commit A, reverting A first would break B's context. By reverting B first, then A, each revert applies cleanly.

You can also revert a list of individual commits. Git will process them in the order you specify, but it's smart enough to reorder if needed to avoid conflicts. For safety, always revert from newest to oldest.

Production scenario: the last three commits all introduced bugs. Instead of reverting one by one, run git revert HEAD~3..HEAD. This reverts HEAD, then HEAD~1, then HEAD~2, creating three new revert commits. Your history stays linear and clean.

revert-range.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
// io.thecodeforge — DevOps tutorial

# Revert the last 3 commits (HEAD~2, HEAD~1, HEAD)
git revert HEAD~3..HEAD --no-edit

# This creates 3 new commits, each reverting one of the original commits.
# Order: first revert HEAD, then HEAD~1, then HEAD~2.

# To revert a specific range of hashes:
git revert abc123..def456 --no-edit

# Note: the range is exclusive of the first commit (abc123 is NOT reverted).
# To include abc123, use git revert abc123^..def456
Output
[main 1a2b3c4] Revert "commit 3"
[main 2b3c4d5] Revert "commit 2"
[main 3c4d5e6] Revert "commit 1"
Never Do This:
Don't use git revert --no-commit with a range unless you understand the conflict risk. It stages all reverts as a single commit, which can cause merge conflicts if the commits touch the same lines.

When Revert Breaks Down: Conflicts and Revert of Revert

Revert is not magic. If the files changed by the target commit have been modified by later commits, revert can cause merge conflicts. Git tries to apply the inverse diff, but if the context has shifted, it can't. You'll see the same conflict markers as any merge.

Another failure mode: reverting a commit that was itself a revert. This is common when you need to re-introduce a feature that was reverted. The fix is to revert the revert — but if there have been intervening commits, you may get conflicts.

Production scenario: a feature was merged, then reverted. Later, the team decides to bring it back. They try to revert the revert, but another commit has modified the same files. Now they have to resolve conflicts manually. The lesson: revert is not a time machine. It's a forward-moving undo that respects the current state of the branch.

revert-conflict.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// io.thecodeforge — DevOps tutorial

# Simulate conflict scenario
echo "line1" > file.txt
git add file.txt && git commit -m "initial"

echo "line2" >> file.txt
git add file.txt && git commit -m "add line2"

git revert HEAD --no-edit  # reverts "add line2"

echo "line3" >> file.txt  # another commit after revert
git add file.txt && git commit -m "add line3"

# Now try to revert the revert (to bring back line2)
git revert HEAD~1 --no-edit  # this will conflict because file.txt now has line3
# Error: CONFLICT (content): Merge conflict in file.txt
# Resolve manually, then git revert --continue
Output
error: could not revert 2b3c4d5... Revert "add line2"
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
The Classic Bug:
Reverting a revert is not a simple undo. If other commits have touched the same files, you'll get merge conflicts. Always check git log --oneline for intervening commits before reverting a revert.

Revert vs Reset vs Restore: When to Use What

Three commands for undoing changes. Here's the cheat sheet
  • git reset: moves the branch pointer backward. Destructive for shared branches. Use only for local, unpushed commits.
  • git revert: creates a new commit that undoes a previous commit. Safe for shared branches. Use for any pushed commit.
  • git restore: unstages or discards changes in the working directory. Doesn't affect commit history. Use for local changes you haven't committed.

The rule of thumb: if the commit has been pushed to a shared branch, use revert. If it's only local, you can use reset. If you haven't committed yet, use restore.

Production scenario: a junior dev pushes a commit with a typo in a variable name. The commit is on main. They ask if they can reset. No — use revert. Then they can fix the typo in a new commit. The history shows the mistake and the fix, which is honest and auditable.

compare-undo.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
// io.thecodeforge — DevOps tutorial

# Local commit (not pushed): use reset
git commit -m "wip"
git reset --soft HEAD~1  # undo commit, keep changes staged

# Pushed commit: use revert
git revert HEAD --no-edit

# Uncommitted changes: use restore
git restore --staged file.txt  # unstage
git restore file.txt           # discard working directory changes
Interview Gold:
Interviewers love this question: 'You pushed a commit with a bug. How do you undo it?' The answer is revert, not reset. Explain why reset breaks other developers' repos.
● Production incidentPOST-MORTEMseverity: high

The Merge Revert That Lost a Feature

Symptom
After reverting a merge commit, the feature branch couldn't be re-merged — Git said 'already up to date'.
Assumption
The team assumed revert was a perfect undo, so they could re-merge the same branch later.
Root cause
Revert of a merge commit creates a new commit that reverses the merge's diff. But Git's merge logic sees the original merge's commits as already present, so a subsequent merge of the same branch is a no-op.
Fix
Revert the revert commit before re-merging: git revert <revert-hash>. Or use git rebase --onto to rewrite the feature branch.
Key lesson
  • Reverting a merge doesn't erase the merge from history — it just adds an anti-merge.
  • To re-introduce the same changes, you must revert the revert.
Production debug guideSystematic recovery paths for the failure modes engineers actually hit.3 entries
Symptom · 01
fatal: commit <hash> is a merge but no -m option was given
Fix
1. Identify the merge commit hash. 2. Run git revert -m 1 <hash> to keep the mainline branch. 3. Resolve any conflicts, then git revert --continue.
Symptom · 02
CONFLICT (content): Merge conflict in <file> during revert
Fix
1. Run git status to see conflicted files. 2. Edit files to resolve conflicts. 3. git add <file> for each resolved file. 4. git revert --continue to finish.
Symptom · 03
After reverting a merge, re-merging the same feature branch says 'Already up to date'
Fix
1. Find the revert commit hash. 2. Revert that revert: git revert <revert-hash>. 3. Now the feature branch can be merged again.
★ Git Revert Triage Cheat SheetFirst-response commands for when things go wrong — copy-paste ready.
`fatal: commit is a merge but no -m option`
Immediate action
Check if commit is a merge
Commands
`git cat-file -p <hash> | head -5`
`git log --oneline --graph <hash>^..<hash>`
Fix now
git revert -m 1 <hash>
`CONFLICT (content)`+
Immediate action
Identify conflicted files
Commands
`git diff --name-only --diff-filter=U`
`git status`
Fix now
Edit files, git add, then git revert --continue
`Already up to date` after re-merge+
Immediate action
Check if a revert exists
Commands
`git log --oneline --grep="Revert"`
`git show <revert-hash> --stat`
Fix now
git revert <revert-hash>
Revert created wrong changes+
Immediate action
Verify revert target
Commands
`git show <revert-hash> --stat`
`git diff <revert-hash>^..<revert-hash>`
Fix now
git revert <revert-hash> then re-revert correctly
Featuregit revertgit reset
History rewritingNo — adds new commitYes — moves branch pointer
Safe for shared branchesYesNo (requires force push)
Undo merge commitsYes, with -m flagYes, but dangerous
Conflict riskPossible if files changed laterNone (local only)
Audit trailPreserves original commitLoses original commit
⚙ Quick Reference
5 commands from this guide
FileCommand / CodePurpose
revert-basic.shecho "delete me" > critical_config.txtWhy Revert Exists
revert-merge.shgit revert -m 1 M --no-editReverting a Merge Commit
revert-range.shgit revert HEAD~3..HEAD --no-editReverting Multiple Commits
revert-conflict.shecho "line1" > file.txtWhen Revert Breaks Down
compare-undo.shgit commit -m "wip"Revert vs Reset vs Restore

Key takeaways

1
Revert is the only safe way to undo pushed commits on shared branches
it adds history, doesn't rewrite it.
2
Always use -m 1 when reverting merge commits to keep the mainline branch.
3
Reverting a merge does not allow re-merging the same feature branch
you must revert the revert first.
4
Revert can cause merge conflicts if later commits modified the same files
resolve them manually.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
You pushed a commit to main, then realized it's buggy. How do you undo i...
Q02SENIOR
How does `git revert` handle merge commits? What's the -m flag for?
Q03SENIOR
You reverted a merge commit. Now you need to merge the same feature bran...
Q04SENIOR
What happens if you revert a commit that has been partially modified by ...
Q05SENIOR
Compare `git revert`, `git reset`, and `git restore`. When would you use...
Q06SENIOR
How would you automate a rollback using git revert in a CI/CD pipeline?
Q01 of 06JUNIOR

You pushed a commit to main, then realized it's buggy. How do you undo it without breaking other developers' clones?

ANSWER
Use git revert <hash>. It creates a new commit that undoes the changes. Unlike reset, revert doesn't rewrite history, so other developers can pull without conflicts.
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
How do I undo a pushed commit in Git without rewriting history?
02
What's the difference between git revert and git reset?
03
How do I revert a merge commit in Git?
04
Can I re-merge a feature branch after reverting its merge?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 07, 2026
last updated
214
articles · all by Naren
🔥

That's Git. Mark it forged?

3 min read · try the examples if you haven't

Previous
Git Add, Commit and Status — The Core Workflow
22 / 47 · Git
Next
Interactive Rebase: Squash, Reword, Edit and Reorder Commits