Merge branch 'sb/diff-color-move-more'

"git diff --color-moved" feature has further been tweaked.

* sb/diff-color-move-more:
  diff.c: offer config option to control ws handling in move detection
  diff.c: add white space mode to move detection that allows indent changes
  diff.c: factor advance_or_nullify out of mark_color_as_moved
  diff.c: decouple white space treatment from move detection algorithm
  diff.c: add a blocks mode for moved code detection
  diff.c: adjust hash function signature to match hashmap expectation
  diff.c: do not pass diff options as keydata to hashmap
  t4015: avoid git as a pipe input
  xdiff/xdiffi.c: remove unneeded function declarations
  xdiff/xdiff.h: remove unused flags
This commit is contained in:
Junio C Hamano 2018-08-02 15:30:40 -07:00
commit a81575aa91
7 changed files with 489 additions and 88 deletions

View File

@ -1152,6 +1152,11 @@ diff.colorMoved::
true the default color mode will be used. When set to false,
moved lines are not colored.
diff.colorMovedWS::
When moved lines are colored using e.g. the `diff.colorMoved` setting,
this option controls the `<mode>` how spaces are treated
for details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
color.diff.<slot>::
Use customized color for diff colorization. `<slot>` specifies
which part of the patch to use the specified color, and is one

View File

@ -276,10 +276,14 @@ plain::
that are added somewhere else in the diff. This mode picks up any
moved line, but it is not very useful in a review to determine
if a block of code was moved without permutation.
zebra::
blocks::
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
painted using either the 'color.diff.{old,new}Moved' color.
Adjacent blocks cannot be told apart.
zebra::
Blocks of moved text are detected as in 'blocks' mode. The 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.
dimmed_zebra::
@ -288,6 +292,31 @@ dimmed_zebra::
blocks are considered interesting, the rest is uninteresting.
--
--color-moved-ws=<modes>::
This configures how white spaces are ignored when performing the
move detection for `--color-moved`.
ifdef::git-diff[]
It can be set by the `diff.colorMovedWS` configuration setting.
endif::git-diff[]
These modes can be given as a comma separated list:
+
--
ignore-space-at-eol::
Ignore changes in whitespace at EOL.
ignore-space-change::
Ignore changes in amount of whitespace. This ignores whitespace
at line end, and considers all other sequences of one or
more whitespace characters to be equivalent.
ignore-all-space::
Ignore whitespace when comparing lines. This ignores differences
even if one line has whitespace where the other line has none.
allow-indentation-change::
Initially ignore any white spaces in the move detection, then
group the moved code blocks only into a block if the change in
whitespace is the same per line. This is incompatible with the
other modes.
--
--word-diff[=<mode>]::
Show a word diff, using the <mode> to delimit changed words.
By default, words are delimited by whitespace; see

262
diff.c
View File

@ -37,6 +37,7 @@ static int diff_rename_limit_default = 400;
static int diff_suppress_blank_empty;
static int diff_use_color_default = -1;
static int diff_color_moved_default;
static int diff_color_moved_ws_default;
static int diff_context_default = 3;
static int diff_interhunk_context_default;
static const char *diff_word_regex_cfg;
@ -264,6 +265,8 @@ static int parse_color_moved(const char *arg)
return COLOR_MOVED_NO;
else if (!strcmp(arg, "plain"))
return COLOR_MOVED_PLAIN;
else if (!strcmp(arg, "blocks"))
return COLOR_MOVED_BLOCKS;
else if (!strcmp(arg, "zebra"))
return COLOR_MOVED_ZEBRA;
else if (!strcmp(arg, "default"))
@ -271,7 +274,43 @@ static int parse_color_moved(const char *arg)
else if (!strcmp(arg, "dimmed_zebra"))
return COLOR_MOVED_ZEBRA_DIM;
else
return error(_("color moved setting must be one of 'no', 'default', 'zebra', 'dimmed_zebra', 'plain'"));
return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed_zebra', 'plain'"));
}
static int parse_color_moved_ws(const char *arg)
{
int ret = 0;
struct string_list l = STRING_LIST_INIT_DUP;
struct string_list_item *i;
string_list_split(&l, arg, ',', -1);
for_each_string_list_item(i, &l) {
struct strbuf sb = STRBUF_INIT;
strbuf_addstr(&sb, i->string);
strbuf_trim(&sb);
if (!strcmp(sb.buf, "ignore-space-change"))
ret |= XDF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(sb.buf, "ignore-space-at-eol"))
ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
else if (!strcmp(sb.buf, "ignore-all-space"))
ret |= XDF_IGNORE_WHITESPACE;
else if (!strcmp(sb.buf, "allow-indentation-change"))
ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
else
error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf);
strbuf_release(&sb);
}
if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
(ret & XDF_WHITESPACE_FLAGS))
die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));
string_list_clear(&l, 0);
return ret;
}
int git_diff_ui_config(const char *var, const char *value, void *cb)
@ -287,6 +326,13 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
diff_color_moved_default = cm;
return 0;
}
if (!strcmp(var, "diff.colormovedws")) {
int cm = parse_color_moved_ws(value);
if (cm < 0)
return -1;
diff_color_moved_ws_default = cm;
return 0;
}
if (!strcmp(var, "diff.context")) {
diff_context_default = git_config_int(var, value);
if (diff_context_default < 0)
@ -698,16 +744,116 @@ struct moved_entry {
struct hashmap_entry ent;
const struct emitted_diff_symbol *es;
struct moved_entry *next_line;
struct ws_delta *wsd;
};
static int moved_entry_cmp(const struct diff_options *diffopt,
const struct moved_entry *a,
const struct moved_entry *b,
/**
* The struct ws_delta holds white space differences between moved lines, i.e.
* between '+' and '-' lines that have been detected to be a move.
* The string contains the difference in leading white spaces, before the
* rest of the line is compared using the white space config for move
* coloring. The current_longer indicates if the first string in the
* comparision is longer than the second.
*/
struct ws_delta {
char *string;
unsigned int current_longer : 1;
};
#define WS_DELTA_INIT { NULL, 0 }
static int compute_ws_delta(const struct emitted_diff_symbol *a,
const struct emitted_diff_symbol *b,
struct ws_delta *out)
{
const struct emitted_diff_symbol *longer = a->len > b->len ? a : b;
const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
int d = longer->len - shorter->len;
out->string = xmemdupz(longer->line, d);
out->current_longer = (a == longer);
return !strncmp(longer->line + d, shorter->line, shorter->len);
}
static int cmp_in_block_with_wsd(const struct diff_options *o,
const struct moved_entry *cur,
const struct moved_entry *match,
struct moved_entry *pmb,
int n)
{
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
int al = cur->es->len, cl = l->len;
const char *a = cur->es->line,
*b = match->es->line,
*c = l->line;
int wslen;
/*
* We need to check if 'cur' is equal to 'match'.
* As those are from the same (+/-) side, we do not need to adjust for
* indent changes. However these were found using fuzzy matching
* so we do have to check if they are equal.
*/
if (strcmp(a, b))
return 1;
if (!pmb->wsd)
/*
* No white space delta was carried forward? This can happen
* when we exit early in this function and do not carry
* forward ws.
*/
return 1;
/*
* The indent changes of the block are known and carried forward in
* pmb->wsd; however we need to check if the indent changes of the
* current line are still the same as before.
*
* To do so we need to compare 'l' to 'cur', adjusting the
* one of them for the white spaces, depending which was longer.
*/
wslen = strlen(pmb->wsd->string);
if (pmb->wsd->current_longer) {
c += wslen;
cl -= wslen;
} else {
a += wslen;
al -= wslen;
}
if (strcmp(a, c))
return 1;
return 0;
}
static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
const void *entry,
const void *entry_or_key,
const void *keydata)
{
const struct diff_options *diffopt = hashmap_cmp_fn_data;
const struct moved_entry *a = entry;
const struct moved_entry *b = entry_or_key;
unsigned flags = diffopt->color_moved_ws_handling
& XDF_WHITESPACE_FLAGS;
if (diffopt->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
/*
* As there is not specific white space config given,
* we'd need to check for a new block, so ignore all
* white space. The setup of the white space
* configuration for the next block is done else where
*/
flags |= XDF_IGNORE_WHITESPACE;
return !xdiff_compare_lines(a->es->line, a->es->len,
b->es->line, b->es->len,
diffopt->xdl_opts);
flags);
}
static struct moved_entry *prepare_entry(struct diff_options *o,
@ -715,10 +861,12 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
{
struct moved_entry *ret = xmalloc(sizeof(*ret));
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts);
ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
ret->es = l;
ret->next_line = NULL;
ret->wsd = NULL;
return ret;
}
@ -755,6 +903,56 @@ static void add_lines_to_move_detection(struct diff_options *o,
}
}
static void pmb_advance_or_null(struct diff_options *o,
struct moved_entry *match,
struct hashmap *hm,
struct moved_entry **pmb,
int pmb_nr)
{
int i;
for (i = 0; i < pmb_nr; i++) {
struct moved_entry *prev = pmb[i];
struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL;
if (cur && !hm->cmpfn(o, cur, match, NULL)) {
pmb[i] = cur;
} else {
pmb[i] = NULL;
}
}
}
static void pmb_advance_or_null_multi_match(struct diff_options *o,
struct moved_entry *match,
struct hashmap *hm,
struct moved_entry **pmb,
int pmb_nr, int n)
{
int i;
char *got_match = xcalloc(1, pmb_nr);
for (; match; match = hashmap_get_next(hm, match)) {
for (i = 0; i < pmb_nr; i++) {
struct moved_entry *prev = pmb[i];
struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL;
if (!cur)
continue;
if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
got_match[i] |= 1;
}
}
for (i = 0; i < pmb_nr; i++) {
if (got_match[i]) {
/* Carry the white space delta forward */
pmb[i]->next_line->wsd = pmb[i]->wsd;
pmb[i] = pmb[i]->next_line;
} else
pmb[i] = NULL;
}
}
static int shrink_potential_moved_blocks(struct moved_entry **pmb,
int pmb_nr)
{
@ -772,6 +970,10 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,
if (lp < pmb_nr && rp > -1 && lp < rp) {
pmb[lp] = pmb[rp];
if (pmb[rp]->wsd) {
free(pmb[rp]->wsd->string);
FREE_AND_NULL(pmb[rp]->wsd);
}
pmb[rp] = NULL;
rp--;
lp++;
@ -829,19 +1031,18 @@ static void mark_color_as_moved(struct diff_options *o,
struct moved_entry *key;
struct moved_entry *match = NULL;
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
int i;
switch (l->s) {
case DIFF_SYMBOL_PLUS:
hm = del_lines;
key = prepare_entry(o, n);
match = hashmap_get(hm, key, o);
match = hashmap_get(hm, key, NULL);
free(key);
break;
case DIFF_SYMBOL_MINUS:
hm = add_lines;
key = prepare_entry(o, n);
match = hashmap_get(hm, key, o);
match = hashmap_get(hm, key, NULL);
free(key);
break;
default:
@ -860,17 +1061,11 @@ static void mark_color_as_moved(struct diff_options *o,
if (o->color_moved == COLOR_MOVED_PLAIN)
continue;
/* Check any potential block runs, advance each or nullify */
for (i = 0; i < pmb_nr; i++) {
struct moved_entry *p = pmb[i];
struct moved_entry *pnext = (p && p->next_line) ?
p->next_line : NULL;
if (pnext && !hm->cmpfn(o, pnext, match, NULL)) {
pmb[i] = p->next_line;
} else {
pmb[i] = NULL;
}
}
if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
else
pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
@ -881,7 +1076,17 @@ static void mark_color_as_moved(struct diff_options *o,
*/
for (; match; match = hashmap_get_next(hm, match)) {
ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
pmb[pmb_nr++] = match;
if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
if (compute_ws_delta(l, match->es, wsd)) {
match->wsd = wsd;
pmb[pmb_nr++] = match;
} else
free(wsd);
} else {
pmb[pmb_nr++] = match;
}
}
flipped_block = (flipped_block + 1) % 2;
@ -892,7 +1097,7 @@ static void mark_color_as_moved(struct diff_options *o,
block_length++;
if (flipped_block)
if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
}
adjust_last_block(o, n, block_length);
@ -4125,6 +4330,7 @@ void diff_setup(struct diff_options *options)
}
options->color_moved = diff_color_moved_default;
options->color_moved_ws_handling = diff_color_moved_ws_default;
}
void diff_setup_done(struct diff_options *options)
@ -4704,6 +4910,8 @@ int diff_opt_parse(struct diff_options *options,
if (cm < 0)
die("bad --color-moved argument: %s", arg);
options->color_moved = cm;
} else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
options->color_moved_ws_handling = parse_color_moved_ws(arg);
} else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
@ -5534,10 +5742,12 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
if (o->color_moved) {
struct hashmap add_lines, del_lines;
hashmap_init(&del_lines,
(hashmap_cmp_fn)moved_entry_cmp, o, 0);
hashmap_init(&add_lines,
(hashmap_cmp_fn)moved_entry_cmp, o, 0);
if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;
hashmap_init(&del_lines, moved_entry_cmp, o, 0);
hashmap_init(&add_lines, moved_entry_cmp, o, 0);
add_lines_to_move_detection(o, &add_lines, &del_lines);
mark_color_as_moved(o, &add_lines, &del_lines);

9
diff.h
View File

@ -208,11 +208,16 @@ struct diff_options {
enum {
COLOR_MOVED_NO = 0,
COLOR_MOVED_PLAIN = 1,
COLOR_MOVED_ZEBRA = 2,
COLOR_MOVED_ZEBRA_DIM = 3,
COLOR_MOVED_BLOCKS = 2,
COLOR_MOVED_ZEBRA = 3,
COLOR_MOVED_ZEBRA_DIM = 4,
} color_moved;
#define COLOR_MOVED_DEFAULT COLOR_MOVED_ZEBRA
#define COLOR_MOVED_MIN_ALNUM_COUNT 20
/* XDF_WHITESPACE_FLAGS regarding block detection are set at 2, 3, 4 */
#define COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE (1<<5)
int color_moved_ws_handling;
};
void diff_emit_submodule_del(struct diff_options *o, const char *line);

View File

@ -1223,7 +1223,7 @@ test_expect_success 'plain moved code, inside file' '
test_cmp expected actual
'
test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
test_expect_success 'detect blocks of moved code' '
git reset --hard &&
cat <<-\EOF >lines.txt &&
long line 1
@ -1271,9 +1271,52 @@ test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
test_config color.diff.newMovedDimmed "normal cyan" &&
test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
git diff HEAD --no-renames --color-moved=dimmed_zebra --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved=blocks --color >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
<BOLD>+++ b/lines.txt<RESET>
<CYAN>@@ -1,16 +1,16 @@<RESET>
<MAGENTA>-long line 1<RESET>
<MAGENTA>-long line 2<RESET>
<MAGENTA>-long line 3<RESET>
line 4<RESET>
line 5<RESET>
line 6<RESET>
line 7<RESET>
line 8<RESET>
line 9<RESET>
<CYAN>+<RESET><CYAN>long line 1<RESET>
<CYAN>+<RESET><CYAN>long line 2<RESET>
<CYAN>+<RESET><CYAN>long line 3<RESET>
<CYAN>+<RESET><CYAN>long line 14<RESET>
<CYAN>+<RESET><CYAN>long line 15<RESET>
<CYAN>+<RESET><CYAN>long line 16<RESET>
line 10<RESET>
line 11<RESET>
line 12<RESET>
line 13<RESET>
<MAGENTA>-long line 14<RESET>
<MAGENTA>-long line 15<RESET>
<MAGENTA>-long line 16<RESET>
EOF
test_cmp expected actual
'
test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
# reuse setup from test before!
test_config color.diff.oldMoved "magenta" &&
test_config color.diff.newMoved "cyan" &&
test_config color.diff.oldMovedAlternative "blue" &&
test_config color.diff.newMovedAlternative "yellow" &&
test_config color.diff.oldMovedDimmed "normal magenta" &&
test_config color.diff.newMovedDimmed "normal cyan" &&
test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
git diff HEAD --no-renames --color-moved=dimmed_zebra --color >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1315,9 +1358,8 @@ test_expect_success 'cmd option assumes configured colored-moved' '
test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
test_config diff.colorMoved zebra &&
git diff HEAD --no-renames --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved --color >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1395,9 +1437,8 @@ test_expect_success 'move detection ignoring whitespace ' '
line 4
line 5
EOF
git diff HEAD --no-renames --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved --color >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1419,9 +1460,9 @@ test_expect_success 'move detection ignoring whitespace ' '
EOF
test_cmp expected actual &&
git diff HEAD --no-renames -w --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved --color \
--color-moved-ws=ignore-all-space >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1459,9 +1500,8 @@ test_expect_success 'move detection ignoring whitespace changes' '
line 5
EOF
git diff HEAD --no-renames --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved --color >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1483,9 +1523,9 @@ test_expect_success 'move detection ignoring whitespace changes' '
EOF
test_cmp expected actual &&
git diff HEAD --no-renames -b --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved --color \
--color-moved-ws=ignore-space-change >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1526,9 +1566,8 @@ test_expect_success 'move detection ignoring whitespace at eol' '
# avoid cluttering the output with complaints about our eol whitespace
test_config core.whitespace -blank-at-eol &&
git diff HEAD --no-renames --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved --color >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1550,9 +1589,9 @@ test_expect_success 'move detection ignoring whitespace at eol' '
EOF
test_cmp expected actual &&
git diff HEAD --no-renames --ignore-space-at-eol --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --no-renames --color-moved --color \
--color-moved-ws=ignore-space-at-eol >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
@ -1597,9 +1636,8 @@ test_expect_success '--color-moved block at end of diff output respects MIN_ALNU
irrelevant_line
EOF
git diff HEAD --color-moved=zebra --color --no-renames |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat >expected <<-\EOF &&
<BOLD>diff --git a/bar b/bar<RESET>
<BOLD>--- a/bar<RESET>
@ -1636,9 +1674,8 @@ test_expect_success '--color-moved respects MIN_ALNUM_COUNT' '
nineteen chars 456789
EOF
git diff HEAD --color-moved=zebra --color --no-renames |
grep -v "index" |
test_decode_color >actual &&
git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat >expected <<-\EOF &&
<BOLD>diff --git a/bar b/bar<RESET>
<BOLD>--- a/bar<RESET>
@ -1679,7 +1716,8 @@ test_expect_success '--color-moved treats adjacent blocks as separate for MIN_AL
7charsA
EOF
git diff HEAD --color-moved=zebra --color --no-renames | grep -v "index" | test_decode_color >actual &&
git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
cat >expected <<-\EOF &&
<BOLD>diff --git a/bar b/bar<RESET>
<BOLD>--- a/bar<RESET>
@ -1722,7 +1760,146 @@ test_expect_success 'move detection with submodules' '
# nor did we mess with it another way
git diff --submodule=diff --color | test_decode_color >expect &&
test_cmp expect decoded_actual
test_cmp expect decoded_actual &&
rm -rf bananas &&
git submodule deinit bananas
'
test_expect_success 'only move detection ignores white spaces' '
git reset --hard &&
q_to_tab <<-\EOF >text.txt &&
a long line to exceed per-line minimum
another long line to exceed per-line minimum
original file
EOF
git add text.txt &&
git commit -m "add text" &&
q_to_tab <<-\EOF >text.txt &&
Qa long line to exceed per-line minimum
Qanother long line to exceed per-line minimum
new file
EOF
# Make sure we get a different diff using -w
git diff --color --color-moved -w >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
q_to_tab <<-\EOF >expected &&
<BOLD>diff --git a/text.txt b/text.txt<RESET>
<BOLD>--- a/text.txt<RESET>
<BOLD>+++ b/text.txt<RESET>
<CYAN>@@ -1,3 +1,3 @@<RESET>
Qa long line to exceed per-line minimum<RESET>
Qanother long line to exceed per-line minimum<RESET>
<RED>-original file<RESET>
<GREEN>+<RESET><GREEN>new file<RESET>
EOF
test_cmp expected actual &&
# And now ignoring white space only in the move detection
git diff --color --color-moved \
--color-moved-ws=ignore-all-space,ignore-space-change,ignore-space-at-eol >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
q_to_tab <<-\EOF >expected &&
<BOLD>diff --git a/text.txt b/text.txt<RESET>
<BOLD>--- a/text.txt<RESET>
<BOLD>+++ b/text.txt<RESET>
<CYAN>@@ -1,3 +1,3 @@<RESET>
<BOLD;MAGENTA>-a long line to exceed per-line minimum<RESET>
<BOLD;MAGENTA>-another long line to exceed per-line minimum<RESET>
<RED>-original file<RESET>
<BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>a long line to exceed per-line minimum<RESET>
<BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>another long line to exceed per-line minimum<RESET>
<GREEN>+<RESET><GREEN>new file<RESET>
EOF
test_cmp expected actual
'
test_expect_success 'compare whitespace delta across moved blocks' '
git reset --hard &&
q_to_tab <<-\EOF >text.txt &&
QIndented
QText across
Qsome lines
QBut! <- this stands out
QAdjusting with
QQdifferent starting
Qwhite spaces
QAnother outlier
QQQIndented
QQQText across
QQQfive lines
QQQthat has similar lines
QQQto previous blocks, but with different indent
QQQYetQAnotherQoutlierQ
EOF
git add text.txt &&
git commit -m "add text.txt" &&
q_to_tab <<-\EOF >text.txt &&
QQIndented
QQText across
QQsome lines
QQQBut! <- this stands out
Adjusting with
Qdifferent starting
white spaces
AnotherQoutlier
QQIndented
QQText across
QQfive lines
QQthat has similar lines
QQto previous blocks, but with different indent
QQYetQAnotherQoutlier
EOF
git diff --color --color-moved --color-moved-ws=allow-indentation-change >actual.raw &&
grep -v "index" actual.raw | test_decode_color >actual &&
q_to_tab <<-\EOF >expected &&
<BOLD>diff --git a/text.txt b/text.txt<RESET>
<BOLD>--- a/text.txt<RESET>
<BOLD>+++ b/text.txt<RESET>
<CYAN>@@ -1,14 +1,14 @@<RESET>
<BOLD;MAGENTA>-QIndented<RESET>
<BOLD;MAGENTA>-QText across<RESET>
<BOLD;MAGENTA>-Qsome lines<RESET>
<RED>-QBut! <- this stands out<RESET>
<BOLD;MAGENTA>-QAdjusting with<RESET>
<BOLD;MAGENTA>-QQdifferent starting<RESET>
<BOLD;MAGENTA>-Qwhite spaces<RESET>
<RED>-QAnother outlier<RESET>
<BOLD;MAGENTA>-QQQIndented<RESET>
<BOLD;MAGENTA>-QQQText across<RESET>
<BOLD;MAGENTA>-QQQfive lines<RESET>
<BOLD;MAGENTA>-QQQthat has similar lines<RESET>
<BOLD;MAGENTA>-QQQto previous blocks, but with different indent<RESET>
<RED>-QQQYetQAnotherQoutlierQ<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>some lines<RESET>
<GREEN>+<RESET>QQQ<GREEN>But! <- this stands out<RESET>
<BOLD;CYAN>+<RESET><BOLD;CYAN>Adjusting with<RESET>
<BOLD;CYAN>+<RESET>Q<BOLD;CYAN>different starting<RESET>
<BOLD;CYAN>+<RESET><BOLD;CYAN>white spaces<RESET>
<GREEN>+<RESET><GREEN>AnotherQoutlier<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>five lines<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>that has similar lines<RESET>
<BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>to previous blocks, but with different indent<RESET>
<GREEN>+<RESET>QQ<GREEN>YetQAnotherQoutlier<RESET>
EOF
test_cmp expected actual
'
test_expect_success 'compare whitespace delta incompatible with other space options' '
test_must_fail git diff \
--color-moved-ws=allow-indentation-change,ignore-all-space \
2>err &&
test_i18ngrep allow-indentation-change err
'
test_done

View File

@ -52,14 +52,6 @@ extern "C" {
#define XDL_EMIT_FUNCNAMES (1 << 0)
#define XDL_EMIT_FUNCCONTEXT (1 << 2)
#define XDL_MMB_READONLY (1 << 0)
#define XDL_MMF_ATOMIC (1 << 0)
#define XDL_BDOP_INS 1
#define XDL_BDOP_CPY 2
#define XDL_BDOP_INSB 3
/* merge simplification levels */
#define XDL_MERGE_MINIMAL 0
#define XDL_MERGE_EAGER 1

View File

@ -22,34 +22,17 @@
#include "xinclude.h"
#define XDL_MAX_COST_MIN 256
#define XDL_HEUR_MIN_COST 256
#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
#define XDL_SNAKE_CNT 20
#define XDL_K_HEUR 4
typedef struct s_xdpsplit {
long i1, i2;
int min_lo, min_hi;
} xdpsplit_t;
static long xdl_split(unsigned long const *ha1, long off1, long lim1,
unsigned long const *ha2, long off2, long lim2,
long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
xdalgoenv_t *xenv);
static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
/*
* See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
* Basically considers a "box" (off1, off2, lim1, lim2) and scan from both