merge: add an option to select how incomplete hunks are rendered - #88
Merged
Conversation
Commit 31de940 (merge: keep conflict markers on their own lines, #85) changed how conflict markers are rendered when a conflicting hunk ends in an incomplete line (one without a trailing newline), inserting a newline so that every marker starts at the beginning of a line. That matched the behavior of `git merge-file`, but silently diverged from GNU `diff3 -m`, which the diffutils manual documents as appending the succeeding markers directly to the incomplete line. With this commit, the behavior is now selectable via a new two-variant enum, `IncompleteHunkStyle`, on `MergeOptions`: * `Diff3` (the default) appends markers directly to the incomplete line, matching GNU `diff3 -m` and restoring the pre-#85 output. * `Git` inserts a newline after the incomplete line, matching `git merge-file`. Also add a table-driven test covering all eight permutations of the three inputs having or lacking a trailing newline, for both styles and for both the str and bytes paths. The expected outputs were verified against GNU diff3 3.12 and git 2.55.0: git produces byte-identical output for every permutation, while GNU diff3 glues each side's succeeding marker independently.
epic-lore-bot Bot
pushed a commit
to EpicGames/lore
that referenced
this pull request
Sep 1, 2026
… newline When both sides of a conflict end the file without a trailing newline, the merge glues the conflict markers onto the content lines and the file becomes unparseable — markers are only recognizable at the start of a line: ``` <<<<<<< ours This is line 2 changed.||||||| original This is line 2.======= This is line 2 also changed.>>>>>>> theirs ``` git merge-file --diff3 puts every marker on its own line for the same inputs. Repro is easy: commit a file written with no trailing newline on two branches from a common base, then `branch merge start`. With trailing newlines the markers come out fine. The bug was in the diffy crate. It is fixed there now (bmwill/diffy#85), and 0.5.2 makes the behaviour selectable (bmwill/diffy#88). ## Updated per review **No vendoring.** This is now a plain dependency bump to `diffy = "0.5.2"` plus `MergeOptions::set_incomplete_hunk_style(IncompleteHunkStyle::Git)` in `merge3_text`. The bump alone is not enough: 0.5.2 defaults to `IncompleteHunkStyle::Diff3`, which is the old glued behaviour, so the setting is what does the work. **Tests that resolve restores the content unchanged.** `scripts/test/test_merge_resolve.py` gains three cases on a file whose last line has no trailing newline: - every conflict marker occupies a whole line; - `merge resolve mine` restores the committed bytes exactly; - `merge resolve theirs` restores the committed bytes exactly. They compare bytes rather than strings, so an added newline fails the assertion instead of passing unnoticed. The inserted newline belongs to the marker rendering only — resolving through the Lore API reads the `~mine` / `~theirs` sidecars and returns the side as it was committed. `lore-revision/tests/merge.rs` keeps a unit-level regression test for the marker shape. ## Testing - `cargo test -p lore-revision` — 4 merge tests, 364 lib tests - `pytest test_merge_resolve.py test_merge.py test_conflict.py` — 25 passed - `pytest test_diff.py test_diff_git_baseline.py` — 64 passed (`PatchFormatter` is the other diffy consumer, so the diff output is covered too) --- Disclosure: I used Claude to investigate the root cause of this bug and to implement the fix. I reviewed and tested the changes myself. ``` Imported-PR: #119 Imported-From: 70ee9c0 Imported-Base: 715645d Imported-Merge: 627c14d Imported-Merge-Strategy: verbatim Imported-Merged-Paths: 0 Imported-Author: Jochen Hunz (jochenhz) Signed-off-by: Jochen Hunz <j.hunz@anchorpoint.app> GH-URL: #119 ``` Lore-RevId: 864 Lore-Signature: 5fd0a5f2f53be5504b8f1c8c002116d01d80bf35c260225822e787e62478fbc3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Commit 31de940 (merge: keep conflict markers on their own lines, #85) changed how conflict markers are rendered when a conflicting hunk ends in an incomplete line (one without a trailing newline), inserting a newline so that every marker starts at the beginning of a line. That matched the behavior of
git merge-file, but silently diverged from GNUdiff3 -m, which the diffutils manual documents as appending the succeeding markers directly to the incomplete line.With this commit, the behavior is now selectable via a new two-variant enum,
IncompleteHunkStyle, onMergeOptions:Diff3(the default) appends markers directly to the incomplete line, matching GNUdiff3 -mand restoring the pre-merge: keep conflict markers on their own lines when hunks lack a trailing newline #85 output.Gitinserts a newline after the incomplete line, matchinggit merge-file.Also add a table-driven test covering all eight permutations of the three inputs having or lacking a trailing newline, for both styles and for both the str and bytes paths. The expected outputs were verified against GNU diff3 3.12 and git 2.55.0: git produces byte-identical output for every permutation, while GNU diff3 glues each side's succeeding marker independently.