CI: detect evil merges in test files (a conflict resolution that matches neither parent) - #2967
Conversation
The guard previously compared the merge result only against the git merge-tree conflict-marker baseline. That incorrectly flagged legitimate conflict resolutions that took one side entirely, because the conflict-marker blob never matches a clean parent blob. The fix adds a second check: when head differs from merge-tree, verify that it also differs from both parents before declaring a violation. If head matches either parent exactly, the resolution is a clean wholesale take and the guard stays green. Updated CLI output to match the documented gate contract: EVIL-MERGE FAIL: <path> matches neither parent merge M <hash> parent1 P1 <hash> parent2 P2 <hash> Added control test for the conflict-resolution-by-taking-one-side case, which was the untested false positive that motivated the fix. RED-FIRST proof: ``` FAILED tests/test_check_evil_merge.py::TestEvilMergeGuard::test_conflict_resolved_by_taking_one_side_wholesale_stays_green - AssertionError ``` Green after fix: ``` tests/test_check_evil_merge.py tests/test_check_gate_integrity.py tests/test_check_doc_gate.py -q 120 passed in 4.12s ```
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe evil-merge guard now accepts conflict resolutions identical to either parent. It reports failures only when content differs from both parents and includes the merge and parent hashes. A regression test covers wholesale resolution of a conflict using one parent’s content. ChangesEvil-merge guard correction
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Merge Risk: ⚪ Minimal · up to No actionable merge risk remains. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (3 files)
Reviewed by step-3.7-flash:free · Input: 0 · Output: 0 · Cached: 0 |
Bounced — the relaxation is unconditional, so the guard now passes a clean merge that silently deletes the other parent's testThe false positive you fixed is real: on a conflicted path But the escape hatch was added to the Measured, same probe against both treesBase test file; side-a edits the top of it; side-b appends a new
The fix
elif head_blob != expected:
if path in conflict_blobs:
# conflicted path: merge-tree's blob carries markers, so a wholesale
# take-one-side resolution is legitimate.
p1_blob, p2_blob = p1_blobs.get(path), p2_blobs.get(path)
if head_blob != p1_blob and head_blob != p2_blob:
violations.append(...)
else:
# clean auto-merge that the merge overrode -> still a violation.
violations.append(...)While you are in there: TestsYour new CONTROL A (conflict variant) is a good test and should stay. Add the arm above it as a RED case: clean The CLI output change is fine, but once the scoping above lands, Note for the re-push: |
|
Lead review — blocked, and the The false positive you're fixing is real: a conflict resolved by taking one parent wholesale can never match the conflict-marker blob, so the old guard flagged it. Keep that intent. The problem is that the exemption isn't conditioned on the path having actually conflicted. The new escape hatch sits in the Measured rather than argued. Repo built as: base Why the PR's own tests can't catch it: The fix is small: The card asks for a red-first test on exactly the clean-merge-then-take-one-side case, with the existing conflict-variant control kept green, so the commit body proves both directions. One more thing: I'll add |
fix-forward #2967 (tsk-hlnd2t): the 'matches a parent' exemption is not conditioned on merge-tree having conflicted, so the guard now MISSES a clean merge resolved by taking one side wholesale
CARD TITLE (intent, not commit subject): CI: detect evil merges in test files (a conflict resolution that matches neither parent)
Autonomous build of board card tsk-hlnd2t.
The guard previously compared the merge result only against the
git merge-tree conflict-marker baseline. That incorrectly flagged
legitimate conflict resolutions that took one side entirely, because
the conflict-marker blob never matches a clean parent blob.
The fix adds a second check: when head differs from merge-tree,
verify that it also differs from both parents before declaring a
violation. If head matches either parent exactly, the resolution
is a clean wholesale take and the guard stays green.
Updated CLI output to match the documented gate contract:
EVIL-MERGE FAIL: matches neither parent
merge M
parent1 P1
parent2 P2
Added control test for the conflict-resolution-by-taking-one-side
case, which was the untested false positive that motivated the fix.
RED-FIRST proof:
Green after fix:
Files:
changelog.d/tsk-hlnd2t-evil-merge-parent-match.md | 8 ++++
scripts/check_evil_merge.py | 18 ++++----
tests/test_check_evil_merge.py | 52 +++++++++++++++++++++--
3 files changed, 66 insertions(+), 12 deletions(-)
Summary by CodeRabbit