Skip to content

Commit 20acec2

Browse files
committedMar 16, 2025
Handle conflicts with missing terminating newline
Since v0.26.0, when Jujutsu encounters a missing terminating newline character in a conflict, it will add a comment to the conflict markers. The way we are handling this use case is by actually making the regular expressions for matching conflict markers less strict: we now only check that there are trailing comments, regardless of whether they are in a format that we expect or not. In theory, this could have the potential of matching lines that are not conflict markers, but I believe that this is not likely now that Jujutsu uses long conflict markers to disambiguate conflict markers from file content. References: - https://jj-vcs.github.io/jj/latest/conflicts/#conflicts-with-missing-terminating-newline - https://jj-vcs.github.io/jj/latest/conflicts/#long-conflict-markers
1 parent e47766a commit 20acec2

File tree

7 files changed

+91
-7
lines changed

7 files changed

+91
-7
lines changed
 

‎README.md

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ It can be used to test if the merge tool can handle markers of length higher tha
7070

7171
The fourth file is `multiple_conflicts.txt`, which contains two conflict sections.
7272

73+
The fifth file is `missing_newline.txt`, which contains a conflict for which one side is missing a terminating newline character.
74+
7375
## Troubleshooting
7476

7577
The plugin includes a health check to detect potential issues that would prevent it from functioning properly.

‎lua/jj-diffconflicts/init.lua

+4-7
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,11 @@ h.get_patterns = function(jj_version, marker_length)
112112
}
113113
else
114114
return {
115-
top = "^" .. marker.top .. " Conflict %d+ of %d+$",
116-
bottom = "^" .. marker.bottom .. " Conflict %d+ of %d+ ends$",
115+
top = "^" .. marker.top .. " .+$",
116+
bottom = "^" .. marker.bottom .. " .+$",
117117
-- We need to double `marker.diff` to escape the `%` symbols
118-
diff = "^"
119-
.. marker.diff
120-
.. marker.diff
121-
.. " Changes from base ?#?%d* to side #%d+$",
122-
snapshot = "^" .. marker.snapshot .. " Contents of side #%d+$",
118+
diff = "^" .. marker.diff .. marker.diff .. " .+$",
119+
snapshot = "^" .. marker.snapshot .. " .+$",
123120
}
124121
end
125122
end

‎scripts/make-conflicts.sh

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ apple
5050
grape
5151
orange
5252
EOF
53+
printf "grape" >missing_newline.txt
5354
${JJ} bookmark create -r @ base
5455
${JJ} commit -m 'Initial revision'
5556

@@ -90,6 +91,7 @@ apple
9091
grape
9192
blood orange
9293
EOF
94+
printf "grapefruit" >missing_newline.txt
9395
${JJ} bookmark create -r @ left
9496
${JJ} describe -m 'Fix syntax mistakes, eat grapefruit'
9597

@@ -131,6 +133,7 @@ APPLE
131133
GRAPE
132134
ORANGE
133135
EOF
136+
printf "grape\n" >missing_newline.txt
134137
${JJ} bookmark create -r @ right
135138
${JJ} describe -m 'Fix syntax mistakes, ALL CAPS fruits'
136139

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<<<<<<< Conflict 1 of 1
2+
+++++++ Contents of side #1 (no terminating newline)
3+
grapefruit
4+
%%%%%%% Changes from base to side #2 (adds terminating newline)
5+
-grape
6+
+grape
7+
>>>>>>> Conflict 1 of 1 ends

‎tests/screenshots/missing_newline_ui

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--|---------|---------|---------|---------|---------|---------|---------|---------|
2+
01| grape │ grapefruit
3+
02|~ │~
4+
03|~ │~
5+
04|~ │~
6+
05|~ │~
7+
06|~ │~
8+
07|~ │~
9+
08|~ │~
10+
09|~ │~
11+
10|~ │~
12+
11|~ │~
13+
12|~ │~
14+
13|~ │~
15+
14|~ │~
16+
15|~ │~
17+
16|~ │~
18+
17|~ │~
19+
18|~ │~
20+
19|~ │~
21+
20|~ │~
22+
21|~ │~
23+
22|~ │~
24+
23|[No Name] [+] 1,1 All snapshot [RO] 1,1 All
25+
24|Resolve conflicts leftward then save. Use :cq to abort.
26+
27+
--|---------|---------|---------|---------|---------|---------|---------|---------|
28+
01|00111111111111111111111111111111111111120011111333331111111111111111111111111111
29+
02|00000000000000000000000000000000000000020000000000000000000000000000000000000000
30+
03|00000000000000000000000000000000000000020000000000000000000000000000000000000000
31+
04|00000000000000000000000000000000000000020000000000000000000000000000000000000000
32+
05|00000000000000000000000000000000000000020000000000000000000000000000000000000000
33+
06|00000000000000000000000000000000000000020000000000000000000000000000000000000000
34+
07|00000000000000000000000000000000000000020000000000000000000000000000000000000000
35+
08|00000000000000000000000000000000000000020000000000000000000000000000000000000000
36+
09|00000000000000000000000000000000000000020000000000000000000000000000000000000000
37+
10|00000000000000000000000000000000000000020000000000000000000000000000000000000000
38+
11|00000000000000000000000000000000000000020000000000000000000000000000000000000000
39+
12|00000000000000000000000000000000000000020000000000000000000000000000000000000000
40+
13|00000000000000000000000000000000000000020000000000000000000000000000000000000000
41+
14|00000000000000000000000000000000000000020000000000000000000000000000000000000000
42+
15|00000000000000000000000000000000000000020000000000000000000000000000000000000000
43+
16|00000000000000000000000000000000000000020000000000000000000000000000000000000000
44+
17|00000000000000000000000000000000000000020000000000000000000000000000000000000000
45+
18|00000000000000000000000000000000000000020000000000000000000000000000000000000000
46+
19|00000000000000000000000000000000000000020000000000000000000000000000000000000000
47+
20|00000000000000000000000000000000000000020000000000000000000000000000000000000000
48+
21|00000000000000000000000000000000000000020000000000000000000000000000000000000000
49+
22|00000000000000000000000000000000000000020000000000000000000000000000000000000000
50+
23|44444444444444444444444444444444444444445555555555555555555555555555555555555555
51+
24|66666666666666666666666666666666666666666666666666666667777777777777777777777777

‎tests/test_internal.lua

+18
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,24 @@ T["extract_conflicts"]["handles multiple conflicts"] = function()
168168
}
169169
eq(conflict, expected)
170170
end
171+
T["extract_conflicts"]["handles conflicts with missing newline markers"] = function()
172+
local lines = read_file("tests/data/missing_newline_markers.txt")
173+
local conflict = jj.extract_conflicts(default_patterns, lines)
174+
local expected = {
175+
{
176+
top = 1,
177+
bottom = 7,
178+
lines = {
179+
"+++++++ Contents of side #1 (no terminating newline)",
180+
"grapefruit",
181+
"%%%%%%% Changes from base to side #2 (adds terminating newline)",
182+
"-grape",
183+
"+grape",
184+
},
185+
},
186+
}
187+
eq(conflict, expected)
188+
end
171189
T["extract_conflicts"]["handles conflict numbered higher than 10"] = function()
172190
local lines = {
173191
"<<<<<<< Conflict 11 of 12",

‎tests/test_jj_diffconflicts.lua

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ local SCREENSHOT = {
1414
fruits = "tests/screenshots/fruits_ui",
1515
long_markers = "tests/screenshots/long_markers_ui",
1616
multiple_conflicts = "tests/screenshots/multiple_conflicts_ui",
17+
missing_newline = "tests/screenshots/missing_newline_ui",
1718
}
1819

1920
local T = MiniTest.new_set({
@@ -75,6 +76,11 @@ T["run"]["handles multiple conflicts"] = function()
7576
child.lua("jj.run(false, 7)")
7677
expect.reference_screenshot(child.get_screenshot(), SCREENSHOT.multiple_conflicts)
7778
end
79+
T["run"]["handles missing newlines conflicts"] = function()
80+
set_lines(read_file("tests/data/missing_newline_markers.txt"))
81+
child.lua("jj.run(false, 7)")
82+
expect.reference_screenshot(child.get_screenshot(), SCREENSHOT.missing_newline)
83+
end
7884

7985
T["history view"] = MiniTest.new_set()
8086
T["history view"]["displays UI"] = function()

0 commit comments

Comments
 (0)
Please sign in to comment.