Repository navigation
2 fixes for 3-way-merge - #3671
Conversation
1. Blocks added by both sides in the same place were merged twice 2. `/noninteractive` auto-merge never exits
|
Sorry, I simplified too much. Attached is a more complete example of where it should occur. Real-world case: Both sides add |
|
Thanks for attaching the example. I was able to reproduce the issue. Since this is related to the comparison algorithm, I'd like to think about it a little more. |
|
Yes, I tested about 500 merges on random repositories, and only 2 failed, so this isn't a common case. The main problem is that it can produce incorrect behaviour without the user possibly noticing it. Sorry to bundle 2 fixes into one PR; I thought it was the fastest way to get out of beta. Happy to leave these for you. |
There was a problem hiding this comment.
🟡 Changes recommended
Freshly built conflict-only results can exit without writing the requested output, and the regression test currently masks this path.
2 open findings
What changed in this PR
Improves three-way merge correctness and unattended auto-merge shutdown behavior.
Changes:
- Aligns ambiguous insertions from both sides into one conflict.
- Saves unattended text/image merge output without prompting.
- Adds regression and GUI tests.
| File | Description |
|---|---|
Src/DiffWrapper.cpp |
Aligns equivalent insertion positions. |
Src/MergeDoc.cpp |
Handles unattended text saving. |
Src/MergeDocResultPane.cpp |
Suppresses unattended conflict prompts. |
Src/ImgMergeFrm.cpp |
Handles unattended image saving. |
Testing/GoogleTest/DiffWrapper/DiffWrapper_test.cpp |
Tests insertion alignment. |
Testing/GoogleTest/GUITests/NonInteractiveTest.cpp |
Tests unattended merges. |
Testing/GoogleTest/GUITests/GUITestUtils.cpp |
Adds process execution helper. |
Testing/GoogleTest/GUITests/GUITestUtils.h |
Declares execution helper. |
Testing/GoogleTest/GUITests/GUITests.vcxproj |
Registers the new test. |
Testing/GoogleTest/GUITests/GUITests.vcxproj.filters |
Adds the test to project filters. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
Thanks for the interesting PR! I'll merge it. For future PRs, it would be helpful if you could keep unrelated changes in separate PRs. In particular, PR titles are used directly in the changelog and release notes, so a title like "2 fixes for 3-way merge" doesn't clearly describe the changes, especially when the fixes are unrelated. |
Brings in the merge-result panel fixes (WinMerge#3515 parts 2-4), 3-way merge fixes (WinMerge#3671), Moved line filtering (WinMerge#3681) and the recent comparison list (WinMerge#3663, WinMerge#3673). Conflicts: - Src/resource.h: master's IDC_RECENT_COMPARES_SHOW/LIST took 1698/1699; IDC_IGNORETRAILINGPUNCT_CHECK and IDC_TREE_SITTER_OCCURRENCE_CHECK move to 1720/1721. - Src/MergeDocResultPane.cpp: both sides skipped the unresolved prompt when non-interactive; kept master's comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Two fixes for 3-way merging, each with a test that fails on master.
Fix 1 makes the line merge more cautious: both-sided additions that used to be merged silently are now conflicts.
1. Blocks added by both sides in the same place were merged twice
A 3-way compare combines two comparisons (BASE–LOCAL, BASE–REMOTE). An inserted block whose first line matches the line after it can sit one line higher or lower, and the two comparisons don't always pick the same position. The two additions then count as unrelated one-sided changes, and
Auto Merge takes both.
Before: two differences (LOCAL only, REMOTE only); Auto Merge writes
comment x / member x / resulttwice, with no conflict.After: one difference, reported as a conflict (or as "both sides agree" when the two blocks are identical).
Fix:
AlignInsertions()inSrc/DiffWrapper.cppmoves an inserted block only when it can meet a block inserted by the other side, and only to that position. Differences the two comparisons already agree on are not touched, and blocks that are part of a detected moved block stay where they are.Test:
DiffWrapper.RunFileDiff_3way_BothSidesAddInTheSamePlace(all four diff algorithms, with and without moved-block detection).2.
/noninteractiveauto-merge never exitsBefore: the process stays open; closing a modified document shows the "save changes?" prompt even in an unattended run. Happens with and without the merge result pane, and for image compare. After: the merged content is written to the
/opath and WinMerge exits. Unresolved conflicts in a merge result are written as conflict sections. Without/o, the changes are discarded.Fix:
CMergeDoc::PromptAndSaveIfNeeded(),CMergeDoc::SaveMergeResult()andCImgMergeFrame::PromptAndSaveIfNeeded()no longer prompt when/noninteractiveis set.Test:
NonInteractiveTestinTesting/GoogleTest/GUITests(clean merge and merge with a conflict, each with the result pane on and off; image merge).