From: John Cai <johncai86@xxxxxxxxx> It can be useful to specify diff algorithms per file type. For example, one may want to use the minimal diff algorithm for .json files, another for .c files, etc. Teach the diff machinery to check attributes for a diff algorithm. Enforce precedence by favoring the command line option, then looking at attributes, then finally the config. To enforce precedence order, set the `xdl_opts_command_line` member during options pasing to indicate the diff algorithm was set via command line args. Signed-off-by: John Cai <johncai86@xxxxxxxxx> --- Documentation/gitattributes.txt | 23 +++++++++++++++++++++++ diff.c | 25 +++++++++++++++++++++++++ diff.h | 2 ++ t/lib-diff-alternative.sh | 27 ++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index c19e64ea0ef..501dd536037 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -736,6 +736,29 @@ String:: by the configuration variables in the "diff.foo" section of the Git config file. +`diff-algorithm` +^^^^^^^^^^^^^^^^ + +The attribute `diff-algorithm` affects which algorithm Git uses to generate +diffs. This allows defining diff algorithms per file extension. Precedence rules +are as follows, in order from highest to lowest: + +*Command line option* + +Pass in the `--diff-algorithm` command line option int git-diff(1) + +*Git attributes* + +------------------------ +*.json diff-algorithm=histogram +------------------------ + +*Git config* + +---------------------------------------------------------------- +[diff] + algorithm = histogram +---------------------------------------------------------------- Defining an external diff driver ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/diff.c b/diff.c index a8a31c81fe7..c78e28daeb0 100644 --- a/diff.c +++ b/diff.c @@ -3652,6 +3652,27 @@ static void builtin_diff(const char *name_a, ecbdata.opt = o; if (header.len && !o->flags.suppress_diff_headers) ecbdata.header = &header; + + if (!o->xdl_opts_command_line) { + static struct attr_check *check; + const char *one_diff_algo; + const char *two_diff_algo; + + check = attr_check_alloc(); + attr_check_append(check, git_attr("diff-algorithm")); + + git_check_attr(the_repository->index, NULL, one->path, check); + one_diff_algo = check->items[0].value; + git_check_attr(the_repository->index, NULL, two->path, check); + two_diff_algo = check->items[0].value; + + if (!ATTR_UNSET(one_diff_algo) && !ATTR_UNSET(two_diff_algo) && + !strcmp(one_diff_algo, two_diff_algo)) + set_diff_algorithm(o, one_diff_algo); + + attr_check_free(check); + } + xpp.flags = o->xdl_opts; xpp.ignore_regex = o->ignore_regex; xpp.ignore_regex_nr = o->ignore_regex_nr; @@ -5130,6 +5151,8 @@ static int diff_opt_diff_algorithm(const struct option *opt, return error(_("option diff-algorithm accepts \"myers\", " "\"minimal\", \"patience\" and \"histogram\"")); + options->xdl_opts_command_line = 1; + return 0; } @@ -5157,6 +5180,8 @@ static int diff_opt_diff_algorithm_no_arg(const struct option *opt, return error(_("available diff algorithms include \"myers\", " "\"minimal\", \"patience\" and \"histogram\"")); + options->xdl_opts_command_line = 1; + return 0; } diff --git a/diff.h b/diff.h index 41eb2c3d428..46b565abfd4 100644 --- a/diff.h +++ b/diff.h @@ -333,6 +333,8 @@ struct diff_options { int prefix_length; const char *stat_sep; int xdl_opts; + /* If xdl_opts has been set via the command line. */ + int xdl_opts_command_line; /* see Documentation/diff-options.txt */ char **anchors; diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh index 8d1e408bb58..630c98ea65a 100644 --- a/t/lib-diff-alternative.sh +++ b/t/lib-diff-alternative.sh @@ -107,8 +107,27 @@ EOF STRATEGY=$1 + test_expect_success "$STRATEGY diff from attributes" ' + echo "file* diff-algorithm=$STRATEGY" >.gitattributes && + test_must_fail git diff --no-index file1 file2 > output && + test_cmp expect output + ' + test_expect_success "$STRATEGY diff" ' - test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output && + test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output && + test_cmp expect output + ' + + test_expect_success "$STRATEGY diff command line precedence before attributes" ' + echo "file* diff-algorithm=meyers" >.gitattributes && + test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output && + test_cmp expect output + ' + + test_expect_success "$STRATEGY diff attributes precedence before config" ' + git config diff.algorithm default && + echo "file* diff-algorithm=$STRATEGY" >.gitattributes && + test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output && test_cmp expect output ' @@ -166,5 +185,11 @@ EOF test_must_fail git diff --no-index "--$STRATEGY" uniq1 uniq2 > output && test_cmp expect output ' + + test_expect_success "$STRATEGY diff from attributes" ' + echo "file* diff-algorithm=$STRATEGY" >.gitattributes && + test_must_fail git diff --no-index uniq1 uniq2 > output && + test_cmp expect output + ' } -- gitgitgadget