Skip to content

Commit a4cf900

Browse files
john-caigitster
authored andcommitted
diff: teach diff to read algorithm from diff driver
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. The diff machinery already checks attributes for a diff driver. Teach the diff driver parser a new type "algorithm" to look for in the config, which will be used if a driver has been specified through the attributes. Enforce precedence of the diff algorithm by favoring the command line option, then looking at the driver attributes & config combination, then finally the diff.algorithm config. To enforce precedence order, use a new `ignore_driver_algorithm` member during options parsing to indicate the diff algorithm was set via command line args. Signed-off-by: John Cai <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 11e95e1 commit a4cf900

File tree

6 files changed

+97
-11
lines changed

6 files changed

+97
-11
lines changed

Documentation/gitattributes.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,37 @@ with the above configuration, i.e. `j-c-diff`, with 7
758758
parameters, just like `GIT_EXTERNAL_DIFF` program is called.
759759
See linkgit:git[1] for details.
760760

761+
Setting the internal diff algorithm
762+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
763+
764+
The diff algorithm can be set through the `diff.algorithm` config key, but
765+
sometimes it may be helpful to set the diff algorithm per path. For example,
766+
one may want to use the `minimal` diff algorithm for .json files, and the
767+
`histogram` for .c files, and so on without having to pass in the algorithm
768+
through the command line each time.
769+
770+
First, in `.gitattributes`, assign the `diff` attribute for paths.
771+
772+
------------------------
773+
*.json diff=<name>
774+
------------------------
775+
776+
Then, define a "diff.<name>.algorithm" configuration to specify the diff
777+
algorithm, choosing from `myers`, `patience`, `minimal`, or `histogram`.
778+
779+
----------------------------------------------------------------
780+
[diff "<name>"]
781+
algorithm = histogram
782+
----------------------------------------------------------------
783+
784+
This diff algorithm applies to user facing diff output like git-diff(1),
785+
git-show(1) and is used for the `--stat` output as well. The merge machinery
786+
will not use the diff algorithm set through this method.
787+
788+
NOTE: If `diff.<name>.command` is defined for path with the
789+
`diff=<name>` attribute, it is executed as an external diff driver
790+
(see above), and adding `diff.<name>.algorithm` has no effect, as the
791+
algorithm is not passed to the external diff driver.
761792

762793
Defining a custom hunk-header
763794
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

diff.c

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4460,15 +4460,13 @@ static void run_diff_cmd(const char *pgm,
44604460
const char *xfrm_msg = NULL;
44614461
int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
44624462
int must_show_header = 0;
4463+
struct userdiff_driver *drv = NULL;
44634464

4464-
4465-
if (o->flags.allow_external) {
4466-
struct userdiff_driver *drv;
4467-
4465+
if (o->flags.allow_external || !o->ignore_driver_algorithm)
44684466
drv = userdiff_find_by_path(o->repo->index, attr_path);
4469-
if (drv && drv->external)
4470-
pgm = drv->external;
4471-
}
4467+
4468+
if (o->flags.allow_external && drv && drv->external)
4469+
pgm = drv->external;
44724470

44734471
if (msg) {
44744472
/*
@@ -4485,12 +4483,16 @@ static void run_diff_cmd(const char *pgm,
44854483
run_external_diff(pgm, name, other, one, two, xfrm_msg, o);
44864484
return;
44874485
}
4488-
if (one && two)
4486+
if (one && two) {
4487+
if (!o->ignore_driver_algorithm && drv && drv->algorithm)
4488+
set_diff_algorithm(o, drv->algorithm);
4489+
44894490
builtin_diff(name, other ? other : name,
44904491
one, two, xfrm_msg, must_show_header,
44914492
o, complete_rewrite);
4492-
else
4493+
} else {
44934494
fprintf(o->file, "* Unmerged path %s\n", name);
4495+
}
44944496
}
44954497

44964498
static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate)
@@ -4587,6 +4589,14 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
45874589
const char *name;
45884590
const char *other;
45894591

4592+
if (!o->ignore_driver_algorithm) {
4593+
struct userdiff_driver *drv = userdiff_find_by_path(o->repo->index,
4594+
p->one->path);
4595+
4596+
if (drv && drv->algorithm)
4597+
set_diff_algorithm(o, drv->algorithm);
4598+
}
4599+
45904600
if (DIFF_PAIR_UNMERGED(p)) {
45914601
/* unmerged */
45924602
builtin_diffstat(p->one->path, NULL, NULL, NULL,
@@ -5140,6 +5150,8 @@ static int diff_opt_diff_algorithm(const struct option *opt,
51405150
return error(_("option diff-algorithm accepts \"myers\", "
51415151
"\"minimal\", \"patience\" and \"histogram\""));
51425152

5153+
options->ignore_driver_algorithm = 1;
5154+
51435155
return 0;
51445156
}
51455157

@@ -5155,6 +5167,8 @@ static int diff_opt_diff_algorithm_no_arg(const struct option *opt,
51555167
BUG("available diff algorithms include \"myers\", "
51565168
"\"minimal\", \"patience\" and \"histogram\"");
51575169

5170+
options->ignore_driver_algorithm = 1;
5171+
51585172
return 0;
51595173
}
51605174

@@ -5295,6 +5309,7 @@ static int diff_opt_patience(const struct option *opt,
52955309
for (i = 0; i < options->anchors_nr; i++)
52965310
free(options->anchors[i]);
52975311
options->anchors_nr = 0;
5312+
options->ignore_driver_algorithm = 1;
52985313

52995314
return set_diff_algorithm(options, "patience");
53005315
}

diff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ struct diff_options {
333333
int prefix_length;
334334
const char *stat_sep;
335335
int xdl_opts;
336+
int ignore_driver_algorithm;
336337

337338
/* see Documentation/diff-options.txt */
338339
char **anchors;

t/lib-diff-alternative.sh

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,46 @@ index $file1..$file2 100644
105105
}
106106
EOF
107107

108+
cat >expect_diffstat <<EOF
109+
file1 => file2 | 21 ++++++++++-----------
110+
1 file changed, 10 insertions(+), 11 deletions(-)
111+
EOF
112+
108113
STRATEGY=$1
109114

115+
test_expect_success "$STRATEGY diff from attributes" '
116+
echo "file* diff=driver" >.gitattributes &&
117+
git config diff.driver.algorithm "$STRATEGY" &&
118+
test_must_fail git diff --no-index file1 file2 > output &&
119+
cat expect &&
120+
cat output &&
121+
test_cmp expect output
122+
'
123+
124+
test_expect_success "$STRATEGY diff from attributes has valid diffstat" '
125+
echo "file* diff=driver" >.gitattributes &&
126+
git config diff.driver.algorithm "$STRATEGY" &&
127+
test_must_fail git diff --stat --no-index file1 file2 > output &&
128+
test_cmp expect_diffstat output
129+
'
130+
110131
test_expect_success "$STRATEGY diff" '
111-
test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
132+
test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
133+
test_cmp expect output
134+
'
135+
136+
test_expect_success "$STRATEGY diff command line precedence before attributes" '
137+
echo "file* diff=driver" >.gitattributes &&
138+
git config diff.driver.algorithm myers &&
139+
test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
140+
test_cmp expect output
141+
'
142+
143+
test_expect_success "$STRATEGY diff attributes precedence before config" '
144+
git config diff.algorithm default &&
145+
echo "file* diff=driver" >.gitattributes &&
146+
git config diff.driver.algorithm "$STRATEGY" &&
147+
test_must_fail git diff --no-index file1 file2 > output &&
112148
test_cmp expect output
113149
'
114150

userdiff.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ PATTERNS("scheme",
293293
"|([^][)(}{[ \t])+"),
294294
PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
295295
"\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
296-
{ "default", NULL, -1, { NULL, 0 } },
296+
{ "default", NULL, NULL, -1, { NULL, 0 } },
297297
};
298298
#undef PATTERNS
299299
#undef IPATTERN
@@ -393,6 +393,8 @@ int userdiff_config(const char *k, const char *v)
393393
return parse_bool(&drv->textconv_want_cache, k, v);
394394
if (!strcmp(type, "wordregex"))
395395
return git_config_string(&drv->word_regex, k, v);
396+
if (!strcmp(type, "algorithm"))
397+
return git_config_string(&drv->algorithm, k, v);
396398

397399
return 0;
398400
}

userdiff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct userdiff_funcname {
1414
struct userdiff_driver {
1515
const char *name;
1616
const char *external;
17+
const char *algorithm;
1718
int binary;
1819
struct userdiff_funcname funcname;
1920
const char *word_regex;

0 commit comments

Comments
 (0)