lore-revision: Fix conflict markers glued to hunks lacking a trailing newline - #119
Conversation
caaa1b4 to
f80f8de
Compare
|
Moving the markers to a new line is objectively wrong though, as that introduces a newline character in the content that is not actually there. If you then select mine or theirs hunk, you get a modification to the original mine or theirs file - an extra newline. |
|
I did accept the fix the @jochenhz submitted to Happy to take suggestions on how you'd like this addressed in |
|
I see the point that it creates a newline character that wasn't there before the merge. But Git is doing it like that and tools such as VSCode and Diffs (Conductor) cannot parse the diff created by Lore. |
f80f8de to
4fa1796
Compare
|
Could this now be done with an update to diffy dependency instead of vendoring? What happens in tools when selecting the mine/theirs/base that should have no newline? Do they introduce the newline and thereby generating the wrong result (i.e you're not getting the content you selected, but rather the content + a modification, the addition of the newline?) |
|
@mjansson Yes I just added this in |
|
@mjansson I double-checked what the tools actually do. Git itself always puts the conflict markers on their own line — it inserts that
Happy to drop the vendored copy and switch to the released crate with |
|
Gotcha, so a tool that actually uses Lore API to do the resolve would get the correct content, a tool that does the edit itself (or if user manually edit resolve) would get the modified content. I think I could live with that, as long as there are tests that verify that lore merge resolve restores the content without the added newline. |
4fa1796 to
c3b735a
Compare
|
Just a code spell listing issue, check https://github.com/EpicGames/lore/actions/runs/33496482388/job/99819750774?pr=119 |
A conflicting hunk whose last line has no trailing newline gets the next marker glued onto it. Tools such as VSCode cannot parse this. diffy 0.5.2 makes this selectable; `IncompleteHunkStyle::Git` inserts the newline, as `git merge-file` does. Signed-off-by: Jochen Hunz <j.hunz@anchorpoint.app>
c3b735a to
70ee9c0
Compare
|
I removed the vendoring and updated the PR to match latest of diffy with |
|
Imported as Lore CR-510. |
… 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
|
Closed by mirrored commit cb6f575. |
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:
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 comeout 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"plusMergeOptions::set_incomplete_hunk_style(IncompleteHunkStyle::Git)inmerge3_text.The bump alone is not enough: 0.5.2 defaults to
IncompleteHunkStyle::Diff3, which isthe old glued behaviour, so the setting is what does the work.
Tests that resolve restores the content unchanged.
scripts/test/test_merge_resolve.pygains three cases on a file whose last line has no trailing newline:
merge resolve minerestores the committed bytes exactly;merge resolve theirsrestores 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/~theirssidecars and returns the side as it wascommitted.
lore-revision/tests/merge.rskeeps a unit-level regression test for the marker shape.Testing
cargo test -p lore-revision— 4 merge tests, 364 lib testspytest test_merge_resolve.py test_merge.py test_conflict.py— 25 passedpytest test_diff.py test_diff_git_baseline.py— 64 passed (PatchFormatteris theother 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.