1
0
mirror of https://github.com/git/git synced 2024-07-04 16:48:40 +00:00

Support 'diff=pgm' attribute

This enhances the attributes mechanism so that external programs
meant for existing GIT_EXTERNAL_DIFF interface can be specifed
per path.

To configure such a custom diff driver, first define a custom
diff driver in the configuration:

	[diff "my-c-diff"]
		command = <<your command string comes here>>

Then mark the paths that you want to use this custom driver
using the attribute mechanism.

	*.c	diff=my-c-diff

The intent of this separation is that the attribute mechanism is
used for specifying the type of the contents, while the
configuration mechanism is used to define what needs to be done
to that type of the contents, which would be specific to both
platform and personal taste.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2007-04-22 17:52:55 -07:00
parent d83c9af5c6
commit f1af60bdba
5 changed files with 182 additions and 5 deletions

View File

@ -225,6 +225,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (diff_setup_done(&rev.diffopt) < 0)
die("diff_setup_done failed");
}
rev.diffopt.allow_external = 1;
/* Do we have --cached and not have a pending object, then
* default to HEAD by hand. Eek.

View File

@ -943,6 +943,7 @@ void diff_tree_combined(const unsigned char *sha1,
diffopts = *opt;
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
diffopts.recursive = 1;
diffopts.allow_external = 0;
show_log_first = !!rev->loginfo && !rev->no_commit_id;
needsep = 0;

87
diff.c
View File

@ -52,6 +52,49 @@ static int parse_diff_color_slot(const char *var, int ofs)
die("bad config variable '%s'", var);
}
static struct ll_diff_driver {
const char *name;
struct ll_diff_driver *next;
char *cmd;
} *user_diff, **user_diff_tail;
/*
* Currently there is only "diff.<drivername>.command" variable;
* because there are "diff.color.<slot>" variables, we are parsing
* this in a bit convoluted way to allow low level diff driver
* called "color".
*/
static int parse_lldiff_command(const char *var, const char *ep, const char *value)
{
const char *name;
int namelen;
struct ll_diff_driver *drv;
name = var + 5;
namelen = ep - name;
for (drv = user_diff; drv; drv = drv->next)
if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
break;
if (!drv) {
char *namebuf;
drv = xcalloc(1, sizeof(struct ll_diff_driver));
namebuf = xmalloc(namelen + 1);
memcpy(namebuf, name, namelen);
namebuf[namelen] = 0;
drv->name = namebuf;
drv->next = NULL;
if (!user_diff_tail)
user_diff_tail = &user_diff;
*user_diff_tail = drv;
user_diff_tail = &(drv->next);
}
if (!value)
return error("%s: lacks value", var);
drv->cmd = strdup(value);
return 0;
}
/*
* These are to give UI layer defaults.
* The core-level commands such as git-diff-files should
@ -78,11 +121,18 @@ int git_diff_ui_config(const char *var, const char *value)
diff_detect_rename_default = DIFF_DETECT_RENAME;
return 0;
}
if (!prefixcmp(var, "diff.")) {
const char *ep = strrchr(var, '.');
if (ep != var + 4 && !strcmp(ep, ".command"))
return parse_lldiff_command(var, ep, value);
}
if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
color_parse(value, var, diff_colors[slot]);
return 0;
}
return git_default_config(var, value);
}
@ -1074,11 +1124,6 @@ static int file_is_binary(struct diff_filespec *one)
return 0;
else if (ATTR_FALSE(value))
return 1;
else if (ATTR_UNSET(value))
;
else
die("unknown value %s given to 'diff' attribute",
value);
}
if (!one->data) {
@ -1752,6 +1797,30 @@ static void run_external_diff(const char *pgm,
}
}
static const char *external_diff_attr(const char *name)
{
struct git_attr_check attr_diff_check;
setup_diff_attr_check(&attr_diff_check);
if (!git_checkattr(name, 1, &attr_diff_check)) {
const char *value = attr_diff_check.value;
if (!ATTR_TRUE(value) &&
!ATTR_FALSE(value) &&
!ATTR_UNSET(value)) {
struct ll_diff_driver *drv;
if (!user_diff_tail) {
user_diff_tail = &user_diff;
git_config(git_diff_ui_config);
}
for (drv = user_diff; drv; drv = drv->next)
if (!strcmp(drv->name, value))
return drv->cmd;
}
}
return NULL;
}
static void run_diff_cmd(const char *pgm,
const char *name,
const char *other,
@ -1761,6 +1830,14 @@ static void run_diff_cmd(const char *pgm,
struct diff_options *o,
int complete_rewrite)
{
if (!o->allow_external)
pgm = NULL;
else {
const char *cmd = external_diff_attr(name);
if (cmd)
pgm = cmd;
}
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
complete_rewrite);

1
diff.h
View File

@ -59,6 +59,7 @@ struct diff_options {
color_diff_words:1,
has_changes:1,
quiet:1,
allow_external:1,
exit_with_status:1;
int context;
int break_opt;

97
t/t4020-diff-external.sh Executable file
View File

@ -0,0 +1,97 @@
#!/bin/sh
test_description='external diff interface test'
. ./test-lib.sh
_z40=0000000000000000000000000000000000000000
test_expect_success setup '
test_tick &&
echo initial >file &&
git add file &&
git commit -m initial &&
test_tick &&
echo second >file &&
git add file &&
git commit -m second &&
test_tick &&
echo third >file
'
test_expect_success 'GIT_EXTERNAL_DIFF environment' '
GIT_EXTERNAL_DIFF=echo git diff | {
read path oldfile oldhex oldmode newfile newhex newmode &&
test "z$path" = zfile &&
test "z$oldmode" = z100644 &&
test "z$newhex" = "z$_z40" &&
test "z$newmode" = z100644 &&
oh=$(git rev-parse --verify HEAD:file) &&
test "z$oh" = "z$oldhex"
}
'
test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD |
grep "^diff --git a/file b/file"
'
test_expect_success 'diff attribute' '
git config diff.parrot.command echo &&
echo >.gitattributes "file diff=parrot" &&
git diff | {
read path oldfile oldhex oldmode newfile newhex newmode &&
test "z$path" = zfile &&
test "z$oldmode" = z100644 &&
test "z$newhex" = "z$_z40" &&
test "z$newmode" = z100644 &&
oh=$(git rev-parse --verify HEAD:file) &&
test "z$oh" = "z$oldhex"
}
'
test_expect_success 'diff attribute should apply only to diff' '
git log -p -1 HEAD |
grep "^diff --git a/file b/file"
'
test_expect_success 'diff attribute' '
git config --unset diff.parrot.command &&
git config diff.color.command echo &&
echo >.gitattributes "file diff=color" &&
git diff | {
read path oldfile oldhex oldmode newfile newhex newmode &&
test "z$path" = zfile &&
test "z$oldmode" = z100644 &&
test "z$newhex" = "z$_z40" &&
test "z$newmode" = z100644 &&
oh=$(git rev-parse --verify HEAD:file) &&
test "z$oh" = "z$oldhex"
}
'
test_expect_success 'diff attribute should apply only to diff' '
git log -p -1 HEAD |
grep "^diff --git a/file b/file"
'
test_done