From: Jonathan Tan <hidden> Date: 2017-08-11 22:49:25
Note that these patches are for "next", depending on the "--color-moved"
patches.
While working on something else [1], I noticed some irregularities with
how "diff --color-moved" treats the minimum block size, occasionally
coloring blocks smaller than that as moved.
I've marked this as RFC because now I'm not sure if minimum number of
lines is the best way to handle the minimum block size. In particular,
an existing test (in t4015) assumes that 2-line blocks can be
legitimately colored as moved (and notice that it is now marked
test_expect_failure in patch 3), and in my work, I would also like the
rules to be relaxed. What do you think of also changing the rule to,
say, "minimum 10 non-whitespace characters"?
[1] https://public-inbox.org/git/cover.1502483486.git.jonathantanmy@google.com/
Jonathan Tan (3):
diff: avoid redundantly clearing a flag
diff: respect MIN_BLOCK_LENGTH for last block
diff: check MIN_BLOCK_LENGTH at start of new block
diff.c | 35 +++++++++++++++-----
t/t4015-diff-whitespace.sh | 80 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 106 insertions(+), 9 deletions(-)
--
2.14.0.434.g98096fd7a8-goog
From: Jonathan Tan <hidden> Date: 2017-08-11 22:49:29
No code in diff.c sets DIFF_SYMBOL_MOVED_LINE except in
mark_color_as_moved(), so it is redundant to clear it for the current
line. Therefore, clear it only for previous lines.
This makes a refactoring in a subsequent patch easier.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Stefan Beller <hidden> Date: 2017-08-14 17:13:54
On Fri, Aug 11, 2017 at 3:49 PM, Jonathan Tan [off-list ref] wrote:
No code in diff.c sets DIFF_SYMBOL_MOVED_LINE except in
mark_color_as_moved(), so it is redundant to clear it for the current
line. Therefore, clear it only for previous lines.
Oh that part. I remember debating with myself if I rather want to have
the upper bound adjusted by one less (block_length instead of
'block_length + 1'), and then add a constant to 'buf[n - i];'
The patch as implemented is fine, too.
quoted hunk
This makes a refactoring in a subsequent patch easier.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Jonathan Tan <hidden> Date: 2017-08-11 22:49:31
Currently, MIN_BLOCK_LENGTH is only checked when diff encounters a line
that does not belong to the current block. In particular, this means
that MIN_BLOCK_LENGTH is not checked after all lines are encountered.
Perform that check.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 29 ++++++++++++++++++++++-------
t/t4015-diff-whitespace.sh | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 7 deletions(-)
@@ -858,6 +858,26 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,returnrp+1;}+/*+*Ifo->color_movedisCOLOR_MOVED_PLAIN,thisfunctiondoesnothing.+*+*Otherwise,ifthelastblockhasfewerlinesthan+*COLOR_MOVED_MIN_BLOCK_LENGTH,unsetDIFF_SYMBOL_MOVED_LINEonalllinesin+*thatblock.+*+*Thelastblockconsistsofthe(n-block_length)'thlineuptobutnot+*includingthenthline.+*/+staticvoidadjust_last_block(structdiff_options*o,intn,intblock_length)+{+inti;+if(block_length>=COLOR_MOVED_MIN_BLOCK_LENGTH||+o->color_moved==COLOR_MOVED_PLAIN)+return;+for(i=1;i<block_length+1;i++)+o->emitted_symbols->buf[n-i].flags&=~DIFF_SYMBOL_MOVED_LINE;+}+/* Find blocks of moved code, delegate actual coloring decision to helper */staticvoidmark_color_as_moved(structdiff_options*o,structhashmap*add_lines,
@@ -1382,6 +1382,41 @@ EOFtest_cmpexpectedactual'+test_expect_success'--color-moved block at end of diff output respects MIN_BLOCK_LENGTH''+gitreset--hard&&+>bar&&+cat<<-\EOF>foo&&+irrelevant_line+line1+EOF+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>bar&&+line1+EOF+cat<<-\EOF>foo&&+irrelevant_line+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1@@<RESET>+<GREEN>+<RESET><GREEN>line1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,2+1@@<RESET>+irrelevant_line<RESET>+<RED>-line1<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Stefan Beller <hidden> Date: 2017-08-14 17:17:32
On Fri, Aug 11, 2017 at 3:49 PM, Jonathan Tan [off-list ref] wrote:
Currently, MIN_BLOCK_LENGTH is only checked when diff encounters a line
that does not belong to the current block. In particular, this means
that MIN_BLOCK_LENGTH is not checked after all lines are encountered.
Perform that check.
Thanks for spotting! This fix looks straightforward correct.
(Also thanks for factoring out the adjustment, I am tempted to
start a bike shedding discussion about its name, though. :P)
quoted hunk
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 29 ++++++++++++++++++++++-------
t/t4015-diff-whitespace.sh | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 7 deletions(-)
@@ -858,6 +858,26 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,returnrp+1;}+/*+*Ifo->color_movedisCOLOR_MOVED_PLAIN,thisfunctiondoesnothing.+*+*Otherwise,ifthelastblockhasfewerlinesthan+*COLOR_MOVED_MIN_BLOCK_LENGTH,unsetDIFF_SYMBOL_MOVED_LINEonalllinesin+*thatblock.+*+*Thelastblockconsistsofthe(n-block_length)'thlineuptobutnot+*includingthenthline.+*/+staticvoidadjust_last_block(structdiff_options*o,intn,intblock_length)+{+inti;+if(block_length>=COLOR_MOVED_MIN_BLOCK_LENGTH||+o->color_moved==COLOR_MOVED_PLAIN)+return;+for(i=1;i<block_length+1;i++)+o->emitted_symbols->buf[n-i].flags&=~DIFF_SYMBOL_MOVED_LINE;+}+/* Find blocks of moved code, delegate actual coloring decision to helper */staticvoidmark_color_as_moved(structdiff_options*o,structhashmap*add_lines,
@@ -1382,6 +1382,41 @@ EOFtest_cmpexpectedactual'+test_expect_success'--color-moved block at end of diff output respects MIN_BLOCK_LENGTH''+gitreset--hard&&+>bar&&+cat<<-\EOF>foo&&+irrelevant_line+line1+EOF+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>bar&&+line1+EOF+cat<<-\EOF>foo&&+irrelevant_line+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1@@<RESET>+<GREEN>+<RESET><GREEN>line1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,2+1@@<RESET>+irrelevant_line<RESET>+<RED>-line1<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&--
From: Jonathan Tan <hidden> Date: 2017-08-11 22:49:33
When noticing that the current line is not the continuation of the
current block, but the start of a new one, mark_color_as_moved() does
not check the length of the current block. Perform that check.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 6 +++++-
t/t4015-diff-whitespace.sh | 45 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 49 insertions(+), 2 deletions(-)
@@ -1015,7 +1015,7 @@ test_expect_success 'detect moved code, complete file' 'test_cmpexpectedactual'-test_expect_success'detect malicious moved code, inside file''+test_expect_failure'detect malicious moved code, inside file''test_configcolor.diff.oldMoved"normal red"&&test_configcolor.diff.newMoved"normal green"&&test_configcolor.diff.oldMovedAlternative"blue"&&
@@ -1417,6 +1417,49 @@ test_expect_success '--color-moved block at end of diff output respects MIN_BLOCtest_cmpexpectedactual'+test_expect_success'--color-moved treats adjacent blocks as separate for MIN_BLOCK_LENGTH''+gitreset--hard&&+cat<<-\EOF>foo&&+line1+irrelevant_line+line2+line3+EOF+>bar&&+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>foo&&+irrelevant_line+EOF+cat<<-\EOF>bar&&+line2+line3+line1+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1,3@@<RESET>+<GREEN>+<RESET><GREEN>line2<RESET>+<GREEN>+<RESET><GREEN>line3<RESET>+<GREEN>+<RESET><GREEN>line1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,4+1@@<RESET>+<RED>-line1<RESET>+irrelevant_line<RESET>+<RED>-line2<RESET>+<RED>-line3<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Stefan Beller <hidden> Date: 2017-08-14 17:22:35
On Fri, Aug 11, 2017 at 3:49 PM, Jonathan Tan [off-list ref] wrote:
When noticing that the current line is not the continuation of the
current block, but the start of a new one, mark_color_as_moved() does
not check the length of the current block. Perform that check.
As far as I remember that behavior is intentional, as indicated by
the succeeding test.
The whole MIN_BLOCK_LENGTH thing is a hack IMHO as we did not have
a better heuristic for suppressing uninteresting "moved" lines such as closing
braces in C.
The information that a thing is moved in between two blocks is more
valuable than pointing out it is just 'new' or 'old'.
As this is changing behavior in a way that seems controversial, can you
give your motivation/example for why this behavior is better?
(Do we want to put it into an option/mode?)
From: Jonathan Tan <hidden> Date: 2017-08-14 21:31:22
These patches are on sb/diff-color-move.
Patches 1 and 2 are unchanged.
It was pointed out to me that the documentation for
"--color-moved=zebra" is ambiguous, but could plausibly describe the
current behavior. I still think that the current behavior is confusing,
so I have retained patch 3, but have updated the commit message and
documentation to describe this situation. I also proceeded with updating
the test mentioned in the previous cover letter to produce the expected
result (describing the modification in the commit message).
(If we do decide that the current behavior is correct, I can send
another patch to update the documentation to be less ambiguous and maybe
update the code to not use a name like MIN_BLOCK_LENGTH, as it is the
number of adjacent moved lines that we are checking, not the length of a
block.)
Jonathan Tan (3):
diff: avoid redundantly clearing a flag
diff: respect MIN_BLOCK_LENGTH for last block
diff: check MIN_BLOCK_LENGTH at start of new block
Documentation/diff-options.txt | 6 +--
diff.c | 35 ++++++++++----
t/t4015-diff-whitespace.sh | 106 +++++++++++++++++++++++++++++++++++------
3 files changed, 122 insertions(+), 25 deletions(-)
--
2.14.1.480.gb18f417b89-goog
From: Jonathan Tan <hidden> Date: 2017-08-14 21:31:24
No code in diff.c sets DIFF_SYMBOL_MOVED_LINE except in
mark_color_as_moved(), so it is redundant to clear it for the current
line. Therefore, clear it only for previous lines.
This makes a refactoring in a subsequent patch easier.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Jonathan Tan <hidden> Date: 2017-08-14 21:31:27
Currently, MIN_BLOCK_LENGTH is only checked when diff encounters a line
that does not belong to the current block. In particular, this means
that MIN_BLOCK_LENGTH is not checked after all lines are encountered.
Perform that check.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 29 ++++++++++++++++++++++-------
t/t4015-diff-whitespace.sh | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 7 deletions(-)
@@ -861,6 +861,26 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,returnrp+1;}+/*+*Ifo->color_movedisCOLOR_MOVED_PLAIN,thisfunctiondoesnothing.+*+*Otherwise,ifthelastblockhasfewerlinesthan+*COLOR_MOVED_MIN_BLOCK_LENGTH,unsetDIFF_SYMBOL_MOVED_LINEonalllinesin+*thatblock.+*+*Thelastblockconsistsofthe(n-block_length)'thlineuptobutnot+*includingthenthline.+*/+staticvoidadjust_last_block(structdiff_options*o,intn,intblock_length)+{+inti;+if(block_length>=COLOR_MOVED_MIN_BLOCK_LENGTH||+o->color_moved==COLOR_MOVED_PLAIN)+return;+for(i=1;i<block_length+1;i++)+o->emitted_symbols->buf[n-i].flags&=~DIFF_SYMBOL_MOVED_LINE;+}+/* Find blocks of moved code, delegate actual coloring decision to helper */staticvoidmark_color_as_moved(structdiff_options*o,structhashmap*add_lines,
@@ -1382,6 +1382,41 @@ EOFtest_cmpexpectedactual'+test_expect_success'--color-moved block at end of diff output respects MIN_BLOCK_LENGTH''+gitreset--hard&&+>bar&&+cat<<-\EOF>foo&&+irrelevant_line+line1+EOF+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>bar&&+line1+EOF+cat<<-\EOF>foo&&+irrelevant_line+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1@@<RESET>+<GREEN>+<RESET><GREEN>line1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,2+1@@<RESET>+irrelevant_line<RESET>+<RED>-line1<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Jonathan Tan <hidden> Date: 2017-08-14 21:31:30
The existing documentation states "If there are fewer than 3 adjacent
moved lines, they are not marked up as moved", which is ambiguous as to
whether "adjacent moved lines" must be adjacent both at the source and
at the destination, or be adjacent merely at the source or merely at the
destination. The behavior of the current code takes the latter
interpretation, but the behavior of blocks being conceptually painted as
blocks and then "unpainted" as lines is confusing to me.
Therefore, clarify the ambiguity in the documentation in the stricter
direction - a block is completely painted or not at all - and update the
code accordingly.
This requires a change in the test "detect malicious moved code, inside
file" in that the malicious change is now marked without the move
colors (because the blocks involved are too small), contrasting with
the subsequent test where the non-malicious change is marked with move
colors.
Signed-off-by: Jonathan Tan <redacted>
---
Documentation/diff-options.txt | 6 ++--
diff.c | 6 +++-
t/t4015-diff-whitespace.sh | 71 +++++++++++++++++++++++++++++++++---------
3 files changed, 65 insertions(+), 18 deletions(-)
@@ -257,10 +257,10 @@ zebra:: Blocks of moved code are detected greedily. The detected blocks are painted using either the 'color.diff.{old,new}Moved' color or 'color.diff.{old,new}MovedAlternative'. The change between- the two colors indicates that a new block was detected. If there- are fewer than 3 adjacent moved lines, they are not marked up+ the two colors indicates that a new block was detected. If a block+ has fewer than 3 adjacent moved lines, it is not marked up as moved, but the regular colors 'color.diff.{old,new}' will be- used.+ used instead. dimmed_zebra:: Similar to 'zebra', but additional dimming of uninteresting parts of moved code is performed. The bordering lines of two adjacent
@@ -1115,13 +1115,13 @@ test_expect_success 'detect malicious moved code, inside file' 'printf("Hello World, but different\n");<RESET>}<RESET><RESET>-<BGREEN>+<RESET><BGREEN>intsecure_foo(structuser*u)<RESET>-<BGREEN>+<RESET><BGREEN>{<RESET>-<YELLOW>+<RESET><YELLOW>foo(u);<RESET>-<BGREEN>+<RESET><BGREEN>if(!u->is_allowed_foo)<RESET>-<BGREEN>+<RESET><BGREEN>return;<RESET>-<YELLOW>+<RESET><YELLOW>}<RESET>-<YELLOW>+<RESET>+<GREEN>+<RESET><GREEN>intsecure_foo(structuser*u)<RESET>+<GREEN>+<RESET><GREEN>{<RESET>+<GREEN>+<RESET><GREEN>foo(u);<RESET>+<GREEN>+<RESET><GREEN>if(!u->is_allowed_foo)<RESET>+<GREEN>+<RESET><GREEN>return;<RESET>+<GREEN>+<RESET><GREEN>}<RESET>+<GREEN>+<RESET>intanother_function()<RESET>{<RESET>bar();<RESET>
@@ -1417,6 +1417,49 @@ test_expect_success '--color-moved block at end of diff output respects MIN_BLOCtest_cmpexpectedactual'+test_expect_success'--color-moved treats adjacent blocks as separate for MIN_BLOCK_LENGTH''+gitreset--hard&&+cat<<-\EOF>foo&&+line1+irrelevant_line+line2+line3+EOF+>bar&&+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>foo&&+irrelevant_line+EOF+cat<<-\EOF>bar&&+line2+line3+line1+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1,3@@<RESET>+<GREEN>+<RESET><GREEN>line2<RESET>+<GREEN>+<RESET><GREEN>line3<RESET>+<GREEN>+<RESET><GREEN>line1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,4+1@@<RESET>+<RED>-line1<RESET>+irrelevant_line<RESET>+<RED>-line2<RESET>+<RED>-line3<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Stefan Beller <hidden> Date: 2017-08-14 22:46:51
On Mon, Aug 14, 2017 at 2:31 PM, Jonathan Tan [off-list ref] wrote:
The existing documentation states "If there are fewer than 3 adjacent
moved lines, they are not marked up as moved", which is ambiguous as to
whether "adjacent moved lines" must be adjacent both at the source and
at the destination, or be adjacent merely at the source or merely at the
destination. The behavior of the current code takes the latter
interpretation, but the behavior of blocks being conceptually painted as
blocks and then "unpainted" as lines is confusing to me.
Therefore, clarify the ambiguity in the documentation in the stricter
direction - a block is completely painted or not at all - and update the
code accordingly.
This requires a change in the test "detect malicious moved code, inside
file" in that the malicious change is now marked without the move
colors (because the blocks involved are too small), contrasting with
the subsequent test where the non-malicious change is marked with move
colors.
Signed-off-by: Jonathan Tan <redacted>
I wonder if these changes ought to be a new mode
(C.f. "mountain zebra" and "imperial zebra" for slight
changes in coloring ;) or if we can settle on one true way.
The 3 lines heuristic is a bad heuristic IMHO (it works reasonable well
for little effort but the fact that we discuss this patch makes it a bad
heuristic as we discuss corner cases that are not relevant. The heuristic
originally wanted to filter out stray single braces that were "moved",
it did not want to suppress small original moved pieces of code),
which this covers up a bit.
Maybe we'll cook this in next for a while to see how people
react to it?
@@ -257,10 +257,10 @@ zebra:: Blocks of moved code are detected greedily. The detected blocks are painted using either the 'color.diff.{old,new}Moved' color or 'color.diff.{old,new}MovedAlternative'. The change between- the two colors indicates that a new block was detected. If there- are fewer than 3 adjacent moved lines, they are not marked up+ the two colors indicates that a new block was detected. If a block+ has fewer than 3 adjacent moved lines, it is not marked up as moved, but the regular colors 'color.diff.{old,new}' will be- used.+ used instead.
This changes the algorithm in a non-obvious way.
When the min-length heuristic is strictly bound to each block,
the function can be simplified more than adding on these tweaks,
1) remove variable block_length, needing to count in the adjust function
2) assign DIFF_SYMBOL_MOVED_LINE either in
COLOR_MOVED_PLAIN case (and continue) or later (where
block_length is increased in this patch)
No need to do these, just as thoughts on how to reduce complexity.
This test would not 'detect malicious moved code, inside file' any more,
I think instead we'd rather want to have a more realistic test case,
which has more lines in it? (This test is about the block detection
not about the omission of short blocks, which was an after thought)
+test_expect_success '--color-moved treats adjacent blocks as separate for MIN_BLOCK_LENGTH' '
Thanks for providing a test here! For testing MIN_BLOCK_LENGTH
for each block I would have imagined the tests would have a block of
length (1,)2,3(,4) lines and then we'd see that the blocks are
highlighted or not.
This only has length=1 blocks?
From: Jonathan Tan <hidden> Date: 2017-08-14 23:57:51
These patches are on sb/diff-color-move.
Patches 1 and 2 are unchanged.
If we're planning to cook a heuristic, we might as well try a better
one. What do you think of this? A heuristic of number of non-whitespace
characters, applied at the block level, and not dependent on the block's
neighbors.
As for the test, good point about testing the boundary conditions - I
have included another test that does that.
Jonathan Tan (3):
diff: avoid redundantly clearing a flag
diff: respect MIN_BLOCK_LENGTH for last block
diff: define block by number of non-space chars
Documentation/diff-options.txt | 8 +--
diff.c | 44 +++++++++++---
diff.h | 2 +-
t/t4015-diff-whitespace.sh | 129 +++++++++++++++++++++++++++++++++++++++--
4 files changed, 163 insertions(+), 20 deletions(-)
--
2.14.1.480.gb18f417b89-goog
From: Jonathan Tan <hidden> Date: 2017-08-14 23:57:53
No code in diff.c sets DIFF_SYMBOL_MOVED_LINE except in
mark_color_as_moved(), so it is redundant to clear it for the current
line. Therefore, clear it only for previous lines.
This makes a refactoring in a subsequent patch easier.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Jonathan Tan <hidden> Date: 2017-08-14 23:57:55
Currently, MIN_BLOCK_LENGTH is only checked when diff encounters a line
that does not belong to the current block. In particular, this means
that MIN_BLOCK_LENGTH is not checked after all lines are encountered.
Perform that check.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 29 ++++++++++++++++++++++-------
t/t4015-diff-whitespace.sh | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 7 deletions(-)
@@ -861,6 +861,26 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,returnrp+1;}+/*+*Ifo->color_movedisCOLOR_MOVED_PLAIN,thisfunctiondoesnothing.+*+*Otherwise,ifthelastblockhasfewerlinesthan+*COLOR_MOVED_MIN_BLOCK_LENGTH,unsetDIFF_SYMBOL_MOVED_LINEonalllinesin+*thatblock.+*+*Thelastblockconsistsofthe(n-block_length)'thlineuptobutnot+*includingthenthline.+*/+staticvoidadjust_last_block(structdiff_options*o,intn,intblock_length)+{+inti;+if(block_length>=COLOR_MOVED_MIN_BLOCK_LENGTH||+o->color_moved==COLOR_MOVED_PLAIN)+return;+for(i=1;i<block_length+1;i++)+o->emitted_symbols->buf[n-i].flags&=~DIFF_SYMBOL_MOVED_LINE;+}+/* Find blocks of moved code, delegate actual coloring decision to helper */staticvoidmark_color_as_moved(structdiff_options*o,structhashmap*add_lines,
@@ -1382,6 +1382,41 @@ EOFtest_cmpexpectedactual'+test_expect_success'--color-moved block at end of diff output respects MIN_BLOCK_LENGTH''+gitreset--hard&&+>bar&&+cat<<-\EOF>foo&&+irrelevant_line+line1+EOF+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>bar&&+line1+EOF+cat<<-\EOF>foo&&+irrelevant_line+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1@@<RESET>+<GREEN>+<RESET><GREEN>line1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,2+1@@<RESET>+irrelevant_line<RESET>+<RED>-line1<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Jonathan Tan <hidden> Date: 2017-08-14 23:57:59
The existing behavior of diff --color-moved=zebra does not define the
minimum size of a block at all, instead relying on a heuristic applied
later to filter out sets of adjacent moved lines that are shorter than 3
lines long. This can be confusing, because a block could thus be colored
as moved at the source but not at the destination (or vice versa),
depending on its neighbors.
Instead, teach diff that the minimum size of a block is 10
non-whitespace characters. This allows diff to still exclude
uninteresting lines appearing on their own (such as those solely
consisting of one or a few closing braces), as was the intention of the
adjacent-moved-line heuristic.
This requires a change in the test "detect malicious moved code, inside
file" in that some of its lines are no longer considered to be part of a
block, because they are too short. The malicious move can still be
observed, however.
Signed-off-by: Jonathan Tan <redacted>
---
Documentation/diff-options.txt | 8 ++--
diff.c | 27 +++++++++---
diff.h | 2 +-
t/t4015-diff-whitespace.sh | 96 +++++++++++++++++++++++++++++++++++++++---
4 files changed, 113 insertions(+), 20 deletions(-)
@@ -254,13 +254,11 @@ plain:: moved line, but it is not very useful in a review to determine if a block of code was moved without permutation. zebra::- Blocks of moved code are detected greedily. The detected blocks are+ Blocks of moved text of at least 10 non-whitespace characters+ are detected greedily. The detected blocks are painted using either the 'color.diff.{old,new}Moved' color or 'color.diff.{old,new}MovedAlternative'. The change between- the two colors indicates that a new block was detected. If there- are fewer than 3 adjacent moved lines, they are not marked up- as moved, but the regular colors 'color.diff.{old,new}' will be- used.+ the two colors indicates that a new block was detected. dimmed_zebra:: Similar to 'zebra', but additional dimming of uninteresting parts of moved code is performed. The bordering lines of two adjacent
@@ -1382,7 +1382,7 @@ EOFtest_cmpexpectedactual'-test_expect_success'--color-moved block at end of diff output respects MIN_BLOCK_LENGTH''+test_expect_success'--color-moved block at end of diff output respects MIN_NON_SPACE_COUNT''gitreset--hard&&>bar&&cat<<-\EOF>foo&&
@@ -1417,6 +1417,88 @@ test_expect_success '--color-moved block at end of diff output respects MIN_BLOCtest_cmpexpectedactual'+test_expect_success'--color-moved respects MIN_NON_SPACE_COUNT''+gitreset--hard&&+cat<<-\EOF>foo&&+ninechars+irrelevant_line+tenchars__+EOF+>bar&&+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>foo&&+irrelevant_line+EOF+cat<<-\EOF>bar&&+tenchars__+ninechars+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1,2@@<RESET>+<BOLD;CYAN>+<RESET><BOLD;CYAN>tenchars__<RESET>+<GREEN>+<RESET><GREEN>ninechars<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,3+1@@<RESET>+<RED>-ninechars<RESET>+irrelevant_line<RESET>+<BOLD;MAGENTA>-tenchars__<RESET>+EOF++test_cmpexpectedactual+'++test_expect_success'--color-moved treats adjacent blocks as separate for MIN_NON_SPACE_COUNT''+gitreset--hard&&+cat<<-\EOF>foo&&+lin1+irrelevant_line+lin2+lin3+EOF+>bar&&+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>foo&&+irrelevant_line+EOF+cat<<-\EOF>bar&&+lin2+lin3+lin1+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1,3@@<RESET>+<GREEN>+<RESET><GREEN>lin2<RESET>+<GREEN>+<RESET><GREEN>lin3<RESET>+<GREEN>+<RESET><GREEN>lin1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,4+1@@<RESET>+<RED>-lin1<RESET>+irrelevant_line<RESET>+<RED>-lin2<RESET>+<RED>-lin3<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Stefan Beller <hidden> Date: 2017-08-15 02:29:25
On Mon, Aug 14, 2017 at 4:57 PM, Jonathan Tan [off-list ref] wrote:
The existing behavior of diff --color-moved=zebra does not define the
minimum size of a block at all, instead relying on a heuristic applied
later to filter out sets of adjacent moved lines that are shorter than 3
lines long. This can be confusing, because a block could thus be colored
as moved at the source but not at the destination (or vice versa),
depending on its neighbors.
Instead, teach diff that the minimum size of a block is 10
non-whitespace characters. This allows diff to still exclude
uninteresting lines appearing on their own (such as those solely
consisting of one or a few closing braces), as was the intention of the
adjacent-moved-line heuristic.
After some thought, I really like this heuristic, however allow me
a moment to bikeshed 10 as a number here.
One could think that 10 equals roughly 3 lines a 3 characters and
in C based languages the shortest meaningful lines have more than
3 characters ("i++;", "a();", "int i;" have 4 or 5 each), but I would still
think that 10 is too much, as we'd want to detect the closing braces
in their own lines.
quoted hunk
dimmed_zebra::
Similar to 'zebra', but additional dimming of uninteresting parts
of moved code is performed. The bordering lines of two adjacent
@@ -864,19 +864,28 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,/**Ifo->color_movedisCOLOR_MOVED_PLAIN,thisfunctiondoesnothing.*-*Otherwise,ifthelastblockhasfewerlinesthan-*COLOR_MOVED_MIN_BLOCK_LENGTH,unsetDIFF_SYMBOL_MOVED_LINEonalllinesin-*thatblock.+*Otherwise,ifthelastblockhasfewernon-spacecharactersthan+*COLOR_MOVED_MIN_NON_SPACE_COUNT,unsetDIFF_SYMBOL_MOVED_LINEonalllines+*inthatblock.**Thelastblockconsistsofthe(n-block_length)'thlineuptobutnot*includingthenthline.*/staticvoidadjust_last_block(structdiff_options*o,intn,intblock_length){-inti;-if(block_length>=COLOR_MOVED_MIN_BLOCK_LENGTH||-o->color_moved==COLOR_MOVED_PLAIN)+inti,non_space_count=0;+if(o->color_moved==COLOR_MOVED_PLAIN)return;+for(i=1;i<block_length+1;i++){+constchar*c=o->emitted_symbols->buf[n-i].line;+for(;*c;c++){+if(isspace(*c))+continue;+non_space_count++;+if(non_space_count>=COLOR_MOVED_MIN_NON_SPACE_COUNT)+return;
When we do this counting, we could count the lines ourselves here as well.
`n-block_count` should be equal to the line that has a different
(flags & (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT))
pattern than those before. (although we'd also have to check for i > 0, too)
Your choice.
quoted hunk
+ }
+ }
for (i = 1; i < block_length + 1; i++)
o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
}
Here we have 2 blocks, the first has 7 character,
which we may want to detect, the second has only 1 char.
The longest "uninteresting" line in C like languages might
be "\t } else {" which has 6 non-ws characters.
Thinking of other languages (shell "fi" is uninteresting, others are
interesting,
Latex \"custom" all bets are off), I think we may want to go lower and have
COLOR_MOVED_MIN_NON_SPACE_COUNT to be about 6
(~ 2 characters on a 3 line block).
That said this is all bikeshedding, feel free to ignore.
Acked-by: Stefan Beller <redacted>
From: Jonathan Tan <hidden> Date: 2017-08-16 01:27:47
These patches are on sb/diff-color-move.
Patches 1 and 2 are unchanged, except for some line wrapping in a test
in patch 2.
This has been updated to use the same alphanumeric heuristic as blame
(20 alnum characters). I tried it out and I thought the results were
reasonable in a patch set that I'm working on (the pack-related function
refactoring one).
As for refactoring blame.c and this file, I'm not sure where best to put
the new function, so I've added a NEEDSWORK for now.
As for detecting block boundaries in adjust_last_block(), I've left it
as-is for now. I think it's clearer if the parent function provides that
information, since it already tracks that. In addition, we avoid corner
cases such as what happens if the block is at the start of the diff
output (we must ensure that we don't read off the beginning edge, for
example).
Jonathan Tan (3):
diff: avoid redundantly clearing a flag
diff: respect MIN_BLOCK_LENGTH for last block
diff: define block by number of alphanumeric chars
Documentation/diff-options.txt | 8 +-
diff.c | 47 ++++++--
diff.h | 2 +-
t/t4015-diff-whitespace.sh | 261 ++++++++++++++++++++++++++++++-----------
4 files changed, 236 insertions(+), 82 deletions(-)
--
2.14.1.480.gb18f417b89-goog
From: Jonathan Tan <hidden> Date: 2017-08-16 01:27:52
No code in diff.c sets DIFF_SYMBOL_MOVED_LINE except in
mark_color_as_moved(), so it is redundant to clear it for the current
line. Therefore, clear it only for previous lines.
This makes a refactoring in a subsequent patch easier.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Jonathan Tan <hidden> Date: 2017-08-16 01:27:56
Currently, MIN_BLOCK_LENGTH is only checked when diff encounters a line
that does not belong to the current block. In particular, this means
that MIN_BLOCK_LENGTH is not checked after all lines are encountered.
Perform that check.
Signed-off-by: Jonathan Tan <redacted>
---
diff.c | 29 ++++++++++++++++++++++-------
t/t4015-diff-whitespace.sh | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+), 7 deletions(-)
@@ -861,6 +861,26 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,returnrp+1;}+/*+*Ifo->color_movedisCOLOR_MOVED_PLAIN,thisfunctiondoesnothing.+*+*Otherwise,ifthelastblockhasfewerlinesthan+*COLOR_MOVED_MIN_BLOCK_LENGTH,unsetDIFF_SYMBOL_MOVED_LINEonalllinesin+*thatblock.+*+*Thelastblockconsistsofthe(n-block_length)'thlineuptobutnot+*includingthenthline.+*/+staticvoidadjust_last_block(structdiff_options*o,intn,intblock_length)+{+inti;+if(block_length>=COLOR_MOVED_MIN_BLOCK_LENGTH||+o->color_moved==COLOR_MOVED_PLAIN)+return;+for(i=1;i<block_length+1;i++)+o->emitted_symbols->buf[n-i].flags&=~DIFF_SYMBOL_MOVED_LINE;+}+/* Find blocks of moved code, delegate actual coloring decision to helper */staticvoidmark_color_as_moved(structdiff_options*o,structhashmap*add_lines,
@@ -1382,6 +1382,43 @@ EOFtest_cmpexpectedactual'+test_expect_success'--color-moved block at end of diff output respects MIN_BLOCK_LENGTH''+gitreset--hard&&+>bar&&+cat<<-\EOF>foo&&+irrelevant_line+line1+EOF+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>bar&&+line1+EOF+cat<<-\EOF>foo&&+irrelevant_line+EOF++gitdiffHEAD--color-moved=zebra--no-renames|+grep-v"index"|+test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1@@<RESET>+<GREEN>+<RESET><GREEN>line1<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,2+1@@<RESET>+irrelevant_line<RESET>+<RED>-line1<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Jonathan Tan <hidden> Date: 2017-08-16 01:27:58
The existing behavior of diff --color-moved=zebra does not define the
minimum size of a block at all, instead relying on a heuristic applied
later to filter out sets of adjacent moved lines that are shorter than 3
lines long. This can be confusing, because a block could thus be colored
as moved at the source but not at the destination (or vice versa),
depending on its neighbors.
Instead, teach diff that the minimum size of a block is 20 alphanumeric
characters, the same heuristic used by "git blame". This allows diff to
still exclude uninteresting lines appearing on their own (such as those
solely consisting of one or a few closing braces), as was the intention
of the adjacent-moved-line heuristic.
This requires a change in some tests in that some of their lines are no
longer considered to be part of a block, because they are too short.
Signed-off-by: Jonathan Tan <redacted>
---
Documentation/diff-options.txt | 8 +-
diff.c | 28 +++--
diff.h | 2 +-
t/t4015-diff-whitespace.sh | 226 ++++++++++++++++++++++++++++-------------
4 files changed, 183 insertions(+), 81 deletions(-)
@@ -254,13 +254,11 @@ plain:: moved line, but it is not very useful in a review to determine if a block of code was moved without permutation. zebra::- Blocks of moved code are detected greedily. The detected blocks are+ Blocks of moved text of at least 20 alphanumeric characters+ are detected greedily. The detected blocks are painted using either the 'color.diff.{old,new}Moved' color or 'color.diff.{old,new}MovedAlternative'. The change between- the two colors indicates that a new block was detected. If there- are fewer than 3 adjacent moved lines, they are not marked up- as moved, but the regular colors 'color.diff.{old,new}' will be- used.+ the two colors indicates that a new block was detected. dimmed_zebra:: Similar to 'zebra', but additional dimming of uninteresting parts of moved code is performed. The bordering lines of two adjacent
@@ -1341,48 +1343,50 @@ line 4 EOFtest_configcolor.diff.oldMoved"magenta"&&test_configcolor.diff.newMoved"cyan"&&-gitdiffHEAD--no-renames--color-moved|test_decode_color>actual&&+gitdiffHEAD--no-renames--color-moved|+grep-v"index"|+test_decode_color>actual&&cat<<-\EOF>expected&&<BOLD>diff--gita/lines.txtb/lines.txt<RESET>-<BOLD>index734156d..eb89ead100644<RESET><BOLD>---a/lines.txt<RESET><BOLD>+++b/lines.txt<RESET><CYAN>@@-1,7+1,7@@<RESET>-<GREEN>+<RESET><GREEN>line5<RESET>-<GREEN>+<RESET><GREEN>line6<RESET>-<GREEN>+<RESET><GREEN>line7<RESET>+<GREEN>+<RESET><GREEN>longline5<RESET>+<GREEN>+<RESET><GREEN>longline6<RESET>+<GREEN>+<RESET><GREEN>longline7<RESET>line1<RESET>line2<RESET>line3<RESET>line4<RESET>-<RED>-line5<RESET>-<RED>-line6<RESET>-<RED>-line7<RESET>+<RED>-longline5<RESET>+<RED>-longline6<RESET>+<RED>-longline7<RESET>EOFtest_cmpexpectedactual&&-gitdiffHEAD--no-renames-w--color-moved|test_decode_color>actual&&+gitdiffHEAD--no-renames-w--color-moved|+grep-v"index"|+test_decode_color>actual&&cat<<-\EOF>expected&&<BOLD>diff--gita/lines.txtb/lines.txt<RESET>-<BOLD>index734156d..eb89ead100644<RESET><BOLD>---a/lines.txt<RESET><BOLD>+++b/lines.txt<RESET><CYAN>@@-1,7+1,7@@<RESET>-<CYAN>+<RESET><CYAN>line5<RESET>-<CYAN>+<RESET><CYAN>line6<RESET>-<CYAN>+<RESET><CYAN>line7<RESET>+<CYAN>+<RESET><CYAN>longline5<RESET>+<CYAN>+<RESET><CYAN>longline6<RESET>+<CYAN>+<RESET><CYAN>longline7<RESET>line1<RESET>line2<RESET>line3<RESET>line4<RESET>-<MAGENTA>-line5<RESET>-<MAGENTA>-line6<RESET>-<MAGENTA>-line7<RESET>+<MAGENTA>-longline5<RESET>+<MAGENTA>-longline6<RESET>+<MAGENTA>-longline7<RESET>EOFtest_cmpexpectedactual'-test_expect_success'--color-moved block at end of diff output respects MIN_BLOCK_LENGTH''+test_expect_success'--color-moved block at end of diff output respects MIN_ALNUM_COUNT''gitreset--hard&&>bar&&cat<<-\EOF>foo&&
@@ -1419,6 +1423,90 @@ test_expect_success '--color-moved block at end of diff output respects MIN_BLOCtest_cmpexpectedactual'+test_expect_success'--color-moved respects MIN_ALNUM_COUNT''+gitreset--hard&&+cat<<-\EOF>foo&&+nineteenchars456789+irrelevant_line+twentychars234567890+EOF+>bar&&+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>foo&&+irrelevant_line+EOF+cat<<-\EOF>bar&&+twentychars234567890+nineteenchars456789+EOF++gitdiffHEAD--color-moved=zebra--no-renames|+grep-v"index"|+test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1,2@@<RESET>+<BOLD;CYAN>+<RESET><BOLD;CYAN>twentychars234567890<RESET>+<GREEN>+<RESET><GREEN>nineteenchars456789<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,3+1@@<RESET>+<RED>-nineteenchars456789<RESET>+irrelevant_line<RESET>+<BOLD;MAGENTA>-twentychars234567890<RESET>+EOF++test_cmpexpectedactual+'++test_expect_success'--color-moved treats adjacent blocks as separate for MIN_ALNUM_COUNT''+gitreset--hard&&+cat<<-\EOF>foo&&+7charsA+irrelevant_line+7charsB+7charsC+EOF+>bar&&+gitaddfoobar&&+gitcommit-mx&&++cat<<-\EOF>foo&&+irrelevant_line+EOF+cat<<-\EOF>bar&&+7charsB+7charsC+7charsA+EOF++gitdiffHEAD--color-moved=zebra--no-renames|grep-v"index"|test_decode_color>actual&&+cat>expected<<-\EOF&&+<BOLD>diff--gita/barb/bar<RESET>+<BOLD>---a/bar<RESET>+<BOLD>+++b/bar<RESET>+<CYAN>@@-0,0+1,3@@<RESET>+<GREEN>+<RESET><GREEN>7charsB<RESET>+<GREEN>+<RESET><GREEN>7charsC<RESET>+<GREEN>+<RESET><GREEN>7charsA<RESET>+<BOLD>diff--gita/foob/foo<RESET>+<BOLD>---a/foo<RESET>+<BOLD>+++b/foo<RESET>+<CYAN>@@-1,4+1@@<RESET>+<RED>-7charsA<RESET>+irrelevant_line<RESET>+<RED>-7charsB<RESET>+<RED>-7charsC<RESET>+EOF++test_cmpexpectedactual+'+ test_expect_success'move detection with submodules''test_create_repobananas&&echoripe>bananas/recipe&&
From: Stefan Beller <hidden> Date: 2017-08-16 05:55:38
On Tue, Aug 15, 2017 at 6:27 PM, Jonathan Tan [off-list ref] wrote:
These patches are on sb/diff-color-move.
Patches 1 and 2 are unchanged, except for some line wrapping in a test
in patch 2.
This has been updated to use the same alphanumeric heuristic as blame
(20 alnum characters). I tried it out and I thought the results were
reasonable in a patch set that I'm working on (the pack-related function
refactoring one).
ok, great!
As for refactoring blame.c and this file, I'm not sure where best to put
the new function, so I've added a NEEDSWORK for now.
Let's just cook this heuristic a bit and see if we want to refactor them
or tweak them differently.
As for detecting block boundaries in adjust_last_block(), I've left it
as-is for now. I think it's clearer if the parent function provides that
information, since it already tracks that. In addition, we avoid corner
cases such as what happens if the block is at the start of the diff
output (we must ensure that we don't read off the beginning edge, for
example).
ok.
Thanks!
Stefan
Jonathan Tan (3):
diff: avoid redundantly clearing a flag
diff: respect MIN_BLOCK_LENGTH for last block
diff: define block by number of alphanumeric chars
Documentation/diff-options.txt | 8 +-
diff.c | 47 ++++++--
diff.h | 2 +-
t/t4015-diff-whitespace.sh | 261 ++++++++++++++++++++++++++++++-----------
4 files changed, 236 insertions(+), 82 deletions(-)
--
2.14.1.480.gb18f417b89-goog