diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index ba56169de3..80e29e3985 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -292,6 +292,23 @@ dimmed_zebra:: blocks are considered interesting, the rest is uninteresting. -- +--color-moved-ws=:: + This configures how white spaces are ignored when performing the + move detection for `--color-moved`. 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. +-- + --word-diff[=]:: Show a word diff, using the to delimit changed words. By default, words are delimited by whitespace; see diff --git a/diff.c b/diff.c index 95c51c0b7d..70eeb40c5f 100644 --- a/diff.c +++ b/diff.c @@ -283,6 +283,36 @@ static int parse_color_moved(const char *arg) 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 + error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf); + + strbuf_release(&sb); + } + + string_list_clear(&l, 0); + + return ret; +} + int git_diff_ui_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { @@ -717,10 +747,12 @@ static int moved_entry_cmp(const void *hashmap_cmp_fn_data, 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; 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, @@ -728,8 +760,9 @@ 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; @@ -4710,6 +4743,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; diff --git a/diff.h b/diff.h index 7bd4f182c3..de5dc68005 100644 --- a/diff.h +++ b/diff.h @@ -214,6 +214,7 @@ struct diff_options { } color_moved; #define COLOR_MOVED_DEFAULT COLOR_MOVED_ZEBRA #define COLOR_MOVED_MIN_ALNUM_COUNT 20 + int color_moved_ws_handling; }; void diff_emit_submodule_del(struct diff_options *o, const char *line); diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index e54529f026..000c3a2b7d 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -1460,7 +1460,8 @@ test_expect_success 'move detection ignoring whitespace ' ' EOF test_cmp expected actual && - git diff HEAD --no-renames -w --color-moved --color >actual.raw && + 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 && diff --git a/lines.txt b/lines.txt @@ -1522,7 +1523,8 @@ test_expect_success 'move detection ignoring whitespace changes' ' EOF test_cmp expected actual && - git diff HEAD --no-renames -b --color-moved --color >actual.raw && + 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 && diff --git a/lines.txt b/lines.txt @@ -1587,7 +1589,8 @@ 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 >actual.raw && + 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 && diff --git a/lines.txt b/lines.txt @@ -1757,7 +1760,58 @@ 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 && + diff --git a/text.txt b/text.txt + --- a/text.txt + +++ b/text.txt + @@ -1,3 +1,3 @@ + Qa long line to exceed per-line minimum + Qanother long line to exceed per-line minimum + -original file + +new file + 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 && + diff --git a/text.txt b/text.txt + --- a/text.txt + +++ b/text.txt + @@ -1,3 +1,3 @@ + -a long line to exceed per-line minimum + -another long line to exceed per-line minimum + -original file + +Qa long line to exceed per-line minimum + +Qanother long line to exceed per-line minimum + +new file + EOF + test_cmp expected actual ' test_done