Skip to content

Commit eb89352

Browse files
phillipwoodgitster
authored andcommitted
diff --color-moved: avoid false short line matches and bad zebra coloring
When marking moved lines it is possible for a block of potential matched lines to extend past a change in sign when there is a sequence of added lines whose text matches the text of a sequence of deleted and added lines. Most of the time either `match` will be NULL or `pmb_advance_or_null()` will fail when the loop encounters a change of sign but there are corner cases where `match` is non-NULL and `pmb_advance_or_null()` successfully advances the moved block despite the change in sign. One consequence of this is highlighting a short line as moved when it should not be. For example -moved line # Correctly highlighted as moved +short line # Wrongly highlighted as moved context +moved line # Correctly highlighted as moved +short line context -short line The other consequence is coloring a moved addition following a moved deletion in the wrong color. In the example below the first "+moved line 3" should be highlighted as newMoved not newMovedAlternate. -moved line 1 # Correctly highlighted as oldMoved -moved line 2 # Correctly highlighted as oldMovedAlternate +moved line 3 # Wrongly highlighted as newMovedAlternate context # Everything else is highlighted correctly +moved line 2 +moved line 3 context +moved line 1 -moved line 3 These false matches are more likely when using --color-moved-ws with the exception of --color-moved-ws=allow-indentation-change which ties the sign of the current whitespace delta to the sign of the line to avoid this problem. The fix is to check that the sign of the new line being matched is the same as the sign of the line that started the block of potential matches. Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent eb31545 commit eb89352

File tree

2 files changed

+76
-6
lines changed

2 files changed

+76
-6
lines changed

diff.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,7 @@ static void mark_color_as_moved(struct diff_options *o,
11761176
struct moved_block *pmb = NULL; /* potentially moved blocks */
11771177
int pmb_nr = 0, pmb_alloc = 0;
11781178
int n, flipped_block = 0, block_length = 0;
1179-
enum diff_symbol last_symbol = 0;
1179+
enum diff_symbol moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
11801180

11811181

11821182
for (n = 0; n < o->emitted_symbols->nr; n++) {
@@ -1202,7 +1202,7 @@ static void mark_color_as_moved(struct diff_options *o,
12021202
flipped_block = 0;
12031203
}
12041204

1205-
if (!match) {
1205+
if (pmb_nr && (!match || l->s != moved_symbol)) {
12061206
int i;
12071207

12081208
if (!adjust_last_block(o, n, block_length) &&
@@ -1219,12 +1219,13 @@ static void mark_color_as_moved(struct diff_options *o,
12191219
pmb_nr = 0;
12201220
block_length = 0;
12211221
flipped_block = 0;
1222-
last_symbol = l->s;
1222+
}
1223+
if (!match) {
1224+
moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
12231225
continue;
12241226
}
12251227

12261228
if (o->color_moved == COLOR_MOVED_PLAIN) {
1227-
last_symbol = l->s;
12281229
l->flags |= DIFF_SYMBOL_MOVED_LINE;
12291230
continue;
12301231
}
@@ -1251,11 +1252,16 @@ static void mark_color_as_moved(struct diff_options *o,
12511252
&pmb, &pmb_alloc,
12521253
&pmb_nr);
12531254

1254-
if (contiguous && pmb_nr && last_symbol == l->s)
1255+
if (contiguous && pmb_nr && moved_symbol == l->s)
12551256
flipped_block = (flipped_block + 1) % 2;
12561257
else
12571258
flipped_block = 0;
12581259

1260+
if (pmb_nr)
1261+
moved_symbol = l->s;
1262+
else
1263+
moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
1264+
12591265
block_length = 0;
12601266
}
12611267

@@ -1265,7 +1271,6 @@ static void mark_color_as_moved(struct diff_options *o,
12651271
if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
12661272
l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
12671273
}
1268-
last_symbol = l->s;
12691274
}
12701275
adjust_last_block(o, n, block_length);
12711276

t/t4015-diff-whitespace.sh

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,71 @@ test_expect_success 'zebra alternate color is only used when necessary' '
15141514
test_cmp expected actual
15151515
'
15161516

1517+
test_expect_success 'short lines of opposite sign do not get marked as moved' '
1518+
cat >old.txt <<-\EOF &&
1519+
this line should be marked as moved
1520+
unchanged
1521+
unchanged
1522+
unchanged
1523+
unchanged
1524+
too short
1525+
this line should be marked as oldMoved newMoved
1526+
this line should be marked as oldMovedAlternate newMoved
1527+
unchanged 1
1528+
unchanged 2
1529+
unchanged 3
1530+
unchanged 4
1531+
this line should be marked as oldMoved newMoved/newMovedAlternate
1532+
EOF
1533+
cat >new.txt <<-\EOF &&
1534+
too short
1535+
unchanged
1536+
unchanged
1537+
this line should be marked as moved
1538+
too short
1539+
unchanged
1540+
unchanged
1541+
this line should be marked as oldMoved newMoved/newMovedAlternate
1542+
unchanged 1
1543+
unchanged 2
1544+
this line should be marked as oldMovedAlternate newMoved
1545+
this line should be marked as oldMoved newMoved/newMovedAlternate
1546+
unchanged 3
1547+
this line should be marked as oldMoved newMoved
1548+
unchanged 4
1549+
EOF
1550+
test_expect_code 1 git diff --no-index --color --color-moved=zebra \
1551+
old.txt new.txt >output && cat output &&
1552+
grep -v index output | test_decode_color >actual &&
1553+
cat >expect <<-\EOF &&
1554+
<BOLD>diff --git a/old.txt b/new.txt<RESET>
1555+
<BOLD>--- a/old.txt<RESET>
1556+
<BOLD>+++ b/new.txt<RESET>
1557+
<CYAN>@@ -1,13 +1,15 @@<RESET>
1558+
<BOLD;MAGENTA>-this line should be marked as moved<RESET>
1559+
<GREEN>+<RESET><GREEN>too short<RESET>
1560+
unchanged<RESET>
1561+
unchanged<RESET>
1562+
<BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as moved<RESET>
1563+
<GREEN>+<RESET><GREEN>too short<RESET>
1564+
unchanged<RESET>
1565+
unchanged<RESET>
1566+
<RED>-too short<RESET>
1567+
<BOLD;MAGENTA>-this line should be marked as oldMoved newMoved<RESET>
1568+
<BOLD;BLUE>-this line should be marked as oldMovedAlternate newMoved<RESET>
1569+
<BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
1570+
unchanged 1<RESET>
1571+
unchanged 2<RESET>
1572+
<BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMovedAlternate newMoved<RESET>
1573+
<BOLD;YELLOW>+<RESET><BOLD;YELLOW>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
1574+
unchanged 3<RESET>
1575+
<BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved<RESET>
1576+
unchanged 4<RESET>
1577+
<BOLD;MAGENTA>-this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
1578+
EOF
1579+
test_cmp expect actual
1580+
'
1581+
15171582
test_expect_success 'cmd option assumes configured colored-moved' '
15181583
test_config color.diff.oldMoved "magenta" &&
15191584
test_config color.diff.newMoved "cyan" &&

0 commit comments

Comments
 (0)