Git, CI/CD & GitOpsXIV · RevertFoundations
What revert does — producing a new commit that undoes a change
What you'll learn
- Explain what `git revert <commit>` does to the working tree, index, and branch tip
- Distinguish reverting (adds a new commit) from resetting (rewrites history)
- Identify why the revert commit has a new OID and a normal parent pointer
- Recognise when revert is the right undo tool on a shared branch
- Read a revert commit as a forward-going statement: "production no longer contains the change from <commit>"
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git revert <commit> is the production-grade undo. It takes the
changes introduced by a single commit, computes the inverse of those
changes, and records the inverse as a brand-new commit on top of
the current branch. The original commit is not touched. The branch
tip moves forward by one commit. The history is preserved.
The mental model is “the change from <commit> is no longer in the
tree at HEAD, and there is a commit on the branch that says so
explicitly.” This is fundamentally different from git reset, which
moves the branch tip backward and silently forgets the commits it
skips. Revert is additive; reset is subtractive.
The shape of a revert
A revert is a three-way merge, just like git cherry-pick and
git merge. The merge base is the parent of the commit being
reverted; the two heads are the current branch tip and that parent.
Git computes the inverse patch and produces a new commit whose
parent is the current branch tip:
gitGraph
commit id: "feature change"
commit id: "parent of feature"
commit id: "current tip" tag: "HEAD"
commit id: "Revert feature" tag: "new HEAD"
The new commit’s diff, applied to the tree at the parent of
<commit>, would reproduce the tree at the current branch tip
minus the changes from <commit>. The original commit is still
in the repository, still referenced by its OID, and still visible to
git log, git show, and git blame if you ask the right way.
COMMIT=abc1234
git revert $COMMIT
git log -1 --format='%H %s'
# new OID, default message: "Revert \"<original subject>\"\n\nThis reverts commit <hash>."
The default commit message is exactly the format the auditor will
look for: it names the original commit and states that the change
has been undone. Do not rewrite that message into “fix thing” — the
explicit “Revert” subject line is how git log --grep=^Revert
finds the reversal.
Why the revert commit has a new OID
A commit’s OID is the SHA of its content object: tree, parents,
author, committer, message. The revert commit’s tree is, by
construction, different from the tree at the original commit’s
parent (the changes from <commit> have been undone). Its parent
is the current branch tip, not the original parent. Its message is
the “Revert …” message. All three differ from any commit already
in the repository, so the OID is necessarily new.
COMMIT=abc1234
git revert $COMMIT
git show HEAD --stat
# tree diff: inverse of the changes in $COMMIT
git log --format='%H %P %s' -2
# new HEAD with HEAD~1 as parent
This is the property that makes revert safe on shared branches.
Every collaborator’s repository already has the OID of
<commit> (it was on the shared history before the revert). The
revert is a new commit on top of that history, so every clone can
fetch it without anyone rewriting a commit that someone else has
already cached.
Revert versus the alternatives
There are four ways to undo a commit, and they are not interchangeable:
| Tool | History effect | Safe on shared branch? |
|---|---|---|
git revert <commit> | Adds a new commit that undoes the change | Yes |
git reset --soft <commit> | Moves branch tip back, keeps changes staged | No |
git reset --mixed <commit> | Moves branch tip back, unstages changes | No |
git reset --hard <commit> | Moves branch tip back, discards changes | No |
git commit --amend | Rewrites the most recent commit | No |
Revert is the only one of the five that adds a commit. The other
four all move the branch tip backward in some way, which means they
rewrite the OID of every commit that came after <commit>, which
means every collaborator must re-fetch and rebase their in-flight
work. On a branch that only you use, any of the five is fine; on a
shared branch, revert is the only one that doesn’t break the rest
of the team.
UnderTheHood: the merge algorithm re-used
The same three-way merge that powers git merge, git rebase,
and git cherry-pick powers git revert. The merge base is the
parent of the commit being reverted; the two heads are the
current branch tip and that parent. The difference from the other
three is that Git internally negates the diff of the named commit
before performing the merge, so the resulting tree is the merge
base plus the inverse of the changes from <commit>.
Production discipline
The production discipline for git revert has three rules:
- Revert is the undo on shared branches. On
main,release-*, and any branch more than one person pushes to, revert is the only undo. The other tools rewrite history and force every collaborator to reconcile. - Keep the default message. The “Revert “<original>"" subject
line and the “This reverts commit <hash>.” body are the audit
trail. Rewriting them into a generic “fix” message breaks the
ability of
git log --grep=^Revertto find every rollback. - Revert through the same review and CI pipeline as any other change. A revert is a commit. It goes through code review. It runs through CI. It triggers a deployment if the branch is wired to one. There is no “fast path” for reverts in a production repository; the audit is the point.
Cross-course references
- Git, CI/CD & GitOps — Part IX (Three-way merges) — the
three-way merge that powers
git revertis the same merge that powersgit merge,git rebase, andgit cherry-pick. - Git, CI/CD & GitOps — Part XI (Rebase) — the history-rewrite contrast that motivates revert is the same contrast that makes rebase safe only on unpushed branches.
- Git, CI/CD & GitOps — Part XIII (Cherry-pick) —
git revertis a cherry-pick of the inverse of a commit; the merge-base mechanics are shared. - Linux for Production Sysadmins — Part XXVII (Stable Kernels) — stable-kernel “revert” patches follow the same rule: an explicit commit that says what was undone and why.
Quiz
Knowledge check · 4 questions
Q1. A team has merged a Terraform change to `main` and applied it to production. The change is broken and must be removed from production. What does `git revert <commit>` do?
Q2. `git revert <commit>` rewrites the OID of `<commit>` so that anyone who has already fetched it must reconcile against the new history.
Q3. Name the command that adds a new commit which inverts the changes of an earlier commit, and the property of that new commit that makes it safe on a shared branch.
Q4. Decide what to do when an engineer proposes to `git reset --hard` a bad change on `main` because it is faster than `git revert`.
A misconfigured IAM policy was merged to `main` as commit `b4d1a11` and applied to production. An engineer proposes to run `git reset --hard b4d1a11^`, force-push `main`, and then re-apply the rest of the merged work as a fresh PR. The team lead asks why this is worse than `git revert b4d1a11`.
Passing score: 75%. Answers are checked in this browser.