Independence-merge union-resolution duplicates import lines when combined with a disjoint function change
Code domain's independence-merge union-resolution duplicates import lines when combined with a disjoint function change
How this was found
Discovered while fixing musehub#219 (imports dimension's reorder+add false conflict). While testing my fix's own gating (confirming it correctly declines to engage on files with real, non-import changes), I found a separate, pre-existing bug in the EXISTING independence-merge path that my fix correctly falls through to.
Repro
base: import sys / import os / def greet(): return "hi"
mixed: import os / import sys / import json / def greet(): return "hello" (reorder+add imports, AND changes greet())
other: import sys / import os / import re / def greet(): return "hi" (reorder+add imports only, greet() untouched)
muse merge mixed -> Fast-forward
muse merge other -> Merge made by the three-way strategy (no conflict reported)
Resulting app.py:
import os
import sys
import json
import os # <-- duplicated
import re
def greet():
return "hello"
import os appears twice. The merge should have produced the deduplicated
set {os, sys, json, re} — it's a genuine bug, not just a cosmetic
oddity: the file now has a redundant import statement that a linter would
flag, and in principle (for a different duplicated construct) this
pattern could silently corrupt merged content.
Confirmed NOT caused by musehub#219's fix
Reproduced identically byte-for-byte with musehub#219's fix fully
shelved-aside (reverted) and fully restored — same duplicate import os
in both cases. musehub#219's new _merge_unordered_set_blob correctly
declines to engage here (it requires the file's content outside every
import/variable symbol's line range to be byte-identical across base/
ours/theirs; greet()'s body differs between mixed and other, so the
function correctly returns None and falls through to the existing path
where this bug already lived).
Suspected location
muse/plugins/code/plugin.py::_independence_merge_blob's "spurious
conflict → union-resolve" fallback (the three_way_merge_lines conflict
region is likely non-empty here because the import reorders on both sides
create overlapping edit regions at the text level — same fundamental
issue musehub#219 diagnosed for the simpler case — and the "union: ours'
lines then theirs' lines" resolution per conflict region doesn't dedupe
semantically-identical content (import os) that ends up in both the
"ours" and "theirs" halves of the union).
Scope
- Confirm the exact code path (likely
_independence_merge_blob's conflict-region union branch, or possibly the independence-merge gating inmerge_ops()that decided this was independence-mergeable at all). - Decide: should the import-set deduplication fix from musehub#219's
_merge_unordered_set_blobbe extended/reused here too (the file does have independent non-import changes, so #219's strict "outside must be identical" gate correctly excludes it today — but the import portion of this specific conflict region could still benefit from set-based dedup even when the rest of the file has unrelated changes elsewhere)? - Add a regression test reproducing this exact scenario once a fix lands.
Acceptance criteria
- The repro above produces
{os, sys, json, re}with no duplicates. - Existing comment-only independence-merge (muse#92) and other independence-merge test suites still pass.