Everything you've learned in this module so far rewrites history. Reset moves the branch pointer, and the removed commits become unreachable. That's fine for local work no one else has seen.
But what about a commit you pushed to main a week ago that
turned out to be a bad idea? A dozen teammates have it in their
clones. Reset-and-force-push would erase the commit from the
remote and cause chaos when those teammates next pull.
The safe answer is git revert: instead of removing the bad
commit from history, add a NEW commit that undoes its effect.
Both commits stay in the log. Anyone else's clone stays
consistent because nothing was rewritten.
The command
git revert HEAD
Or, revert a specific older commit by SHA:
git revert abc1234
Git computes the inverse diff of the target commit and applies it as a new commit. If the target added a line, the revert commit removes it. If the target deleted a line, the revert commit adds it back. If the target changed a line from A to B, the revert commit changes it from B to A.
By default, Git opens your editor to let you edit the revert commit's message. To skip the editor and use the default:
git revert HEAD --no-edit
The default message is Revert "<original message>", which is
usually good enough.
What history looks like after
Before revert:
c3 Bad change ← HEAD
c2 Good change
c1 Initial
After git revert HEAD --no-edit:
r1 Revert "Bad change" ← HEAD
c3 Bad change
c2 Good change
c1 Initial
c3 is still there. So is the revert. Together they cancel out:
the file contents are as if c3 never happened, but history
faithfully records both the mistake and the fix. Anyone reading
git log can see exactly what went wrong and when it was undone.
Reset vs revert: which to use
Ask yourself one question: has anyone else seen this commit?
- If no (local only, unpushed feature branch that only you work on): reset is fine. Simpler, cleaner history.
- If yes (pushed to a shared branch, someone might have pulled): revert. Always.
That's the whole rule. Reset for private history you can rewrite. Revert for shared history you cannot.
Reverting can conflict too
If subsequent commits depended on the changes made by the commit
you're reverting, Git can't cleanly compute the inverse. It
pauses with a merge-conflict-style state, and you resolve the
same way: edit the file, git add, then finish:
git revert --continue
Or bail out with git revert --abort. Same escape hatches as
merge and rebase conflicts.
You can revert a revert
If you revert something and then decide the revert was wrong (the original commit was actually fine), you can revert the revert:
git revert <sha-of-revert-commit>
Now you have three commits: the original, the revert, and the revert-of-the-revert. Everyone can see the whole story. Git logs can get slightly funny to read, but the code is where you want it and no history has been rewritten. Sometimes that's exactly the trade you want to make.
Reverting merge commits
Merge commits (from the diverged module) can be reverted, but the command needs to know which parent line of history to keep:
git revert -m 1 <merge-sha>
The -m 1 means "keep the first parent, undo the second." First
parent is usually the branch you merged into (main); second is
the branch being merged in (feature). So -m 1 typically means
"undo the merge, leave main as it was before the merge landed."
This is one of those commands you rarely need but really want to
remember when you do. Any real "roll back a merged feature"
scenario ends with git revert -m 1.