After reverting a merge in Git, why does re-merging that branch restore nothing?
answer
- merging looks at the graph, not the files
- the merge commit is still there
- those commits are already ancestors
- the merge base already contains them
- undo the undo, or make the work new
basics
~20 sThe revert removed the content but left the merge commit in place, so the branch's commits are still ancestors. A second merge finds nothing new to bring in — Git decides what to merge from ancestry, not from what the files currently contain.
solid answer
~40 sMerging is driven by **reachability**, not by content. Once you merged the branch, its commits became ancestors of your branch, and reverting the merge commit only added a commit that undid the *files*; it did not change the graph. When you merge that branch again, Git computes a merge base that already includes those commits, sees no new changes on that side, and either fast-forwards over nothing or brings in only whatever was added since. The reverted content stays absent. The standard remedies are: revert the revert on the target branch before merging again (restoring the content as a new commit), or rebuild the work as fresh commits on a new branch so it is genuinely new to the graph. Choose deliberately — reverting the revert also restores anything else that merge brought in.
code
console · 5 lines$ git merge feature
Already up to date.
$ git merge-base --is-ancestor feature main && echo "already an ancestor"
already an ancestor
$ git log main..feature --onelinego deeper
Remember the headline: reverting a merge undoes the files but not the fact of the merge, so merging that branch again brings nothing back.
Explain it through the merge base — the branch's commits are already ancestors, so that side contributes nothing new while the revert still counts as a mainline change.
Diagnose it in the wild with reachability checks, then choose deliberately between reverting the revert and rebuilding the work as new commits, and say what each costs.
Set the norm that avoids it entirely: how withdrawn work is recorded, whether merges or individual commits get reverted, and how re-landing is planned rather than discovered.
## The setup 1. `feature` is merged into `main`, producing merge commit `M`. 2. Something is wrong, so `main` gets `git revert -m 1 M`, producing revert commit `R`. The tree on `main` no longer contains the feature's changes. 3. The feature is fixed on `feature` and merged into `main` again. 4. The original changes are still missing. This surprises people badly, sometimes in production, because step 3 reports success. ## Why it happens Git's merge decides what to bring in by comparing three trees: the two branch tips and their **merge base**, the best common ancestor. Ancestry is the input, and ancestry was never undone. After step 1, every commit on `feature` is reachable from `main` through `M`. `R` is an ordinary commit that changes files; it adds nothing to and removes nothing from the graph. So at step 3 the merge base already contains all the original feature commits. From the merge's point of view, that side contributed nothing new — the only changes it can bring in are the ones made on `feature` *after* `M`. Meanwhile the mainline side does have a change relative to the base: `R`, which deleted that content. So the merge result keeps the deletion. Nothing is lost or corrupted; Git did exactly what the graph told it to. The general statement is worth memorising: **reverting a merge undoes the content of a merge, but does not undo the fact of the merge.** ## Getting the work back There are two sound approaches. **Revert the revert.** On the target branch, `git revert <R>` produces a commit that re-applies what `R` removed. Now the content is back and the branch can be merged again normally, with the merge only contributing what came after. This is the mechanical, minimal answer, and it makes the whole story visible in history: merged, withdrawn, restored. Its downside is bluntness — if `M` brought in twenty commits and only one was bad, reverting the revert restores all twenty, so you usually pair it with a targeted fix for the actual problem. **Rebuild the work as new commits.** Recreate the changes on a fresh branch so they are new objects that no ancestor contains — for example by rebasing the branch onto the current mainline, or by cherry-picking its commits onto a new branch. The merge then behaves normally because the content genuinely is new to the graph. This is preferable when the original branch was messy anyway, or when you want to re-land a subset rather than everything. What does **not** work is merging harder: repeating the merge, using a different strategy, or forcing it. None of those change reachability. ## Avoiding the situation - **Prefer reverting the individual commits** rather than the merge, when the merged work is likely to come back. Reverting commits one by one has the same content effect, and re-landing later is far less surprising because you can revert those reverts selectively. - **Do not merge back and forth casually** between a branch whose merge was reverted and the mainline; the asymmetry propagates. - **Leave a note in the revert commit message** saying the branch will need special handling if it returns. The next person will not reconstruct this from the graph. ## How to diagnose it when you meet it cold Someone reports "my changes vanished after we merged again". Check three things: does the file's history show a revert of a merge (`git log --oneline --merges` plus looking for a `Revert "Merge …"` subject); is the feature branch's tip already an ancestor of the target (`git merge-base --is-ancestor <feature> <target>`); and does `git log <target>..<feature>` show nothing, meaning the branch has no commits the target lacks. Those three answers together identify this situation precisely, and the fix follows from the choice above.
- How do you confirm this is what happened rather than a merge going wrong?Check reachability. `git merge-base --is-ancestor <feature> <target>` succeeding means the target already contains the branch, and `git log <target>..<feature>` listing nothing means the branch has no commits the target lacks. Combined with a `Revert "Merge …"` commit in the target's log, that is a precise diagnosis rather than a guess.
- What is the downside of simply reverting the revert?It restores everything the original merge brought in, not just the part you wanted back — including whatever caused the rollback, unless that was fixed separately. It is the right minimal move when the whole branch is wanted again, but pair it with the actual fix, and be explicit in the commit message about what is being restored and why.
- Why does merging with a different strategy not help here?Strategies decide how to combine differing content; they do not change which commits are considered already present. The merge base is computed from ancestry, and after the first merge the branch's commits are ancestors regardless of strategy. Nothing that changes only the combining algorithm can make already-merged commits look new.
The merge is a signed receipt saying the goods were delivered. Reverting throws the goods away but leaves the receipt, so the next delivery run sees a signed receipt and brings nothing.
saying these in an interview costs you the question
- Believing a second merge will re-apply the reverted changes
- Thinking a different merge strategy fixes it
- Assuming the reverted content was lost from the repository
- Claiming the revert removed the merge commit
- Force-merging repeatedly instead of changing the graph or the content