{"thread":{"id":"7788","subject":"[PATCH] Support 'diff=pgm' attribute","startedAt":"2007-04-23T01:02:31Z","lastAt":"2007-04-23T01:02:31Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"40171","messageId":"7vtzv7kimw.fsf@assigned-by-dhcp.cox.net","threadId":"7788","inReplyTo":null,"subject":"[PATCH] Support 'diff=pgm' attribute","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-23T01:02:31Z","receivedAt":"2007-04-23T01:02:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This enhances the attributes mechanism so that external programs\nmeant for existing GIT_EXTERNAL_DIFF interface can be specifed\nper path.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n * This ought to be almost trivial, but the only complicating\n   factor is that we need to disable any custom diff generation\n   when commands other than \"git diff\" calls the textual diff\n   generation.  Most notably, invoking kdiff3 when \"git\n   format-patch\" is asked for does not make any sense.\n\n   The existing GIT_EXTERNAL_DIFF already had this problem; the\n   patch fixes it by allowing external diff hook (both the new\n   attribute mechanism and the old environment based one) only\n   when running the toplevel \"git-diff\" command.\n\n builtin-diff.c           |    1 +\n combine-diff.c           |    1 +\n diff.c                   |   27 ++++++++++++++++-\n diff.h                   |    1 +\n t/t4020-diff-external.sh |   69 ++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 97 insertions(+), 2 deletions(-)\n create mode 100755 t/t4020-diff-external.sh\n\ndiff --git a/builtin-diff.c b/builtin-diff.c\nindex 21d13f0..2ae6009 100644\n--- a/builtin-diff.c\n+++ b/builtin-diff.c\n@@ -225,6 +225,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \t\tif (diff_setup_done(&rev.diffopt) < 0)\n \t\t\tdie(\"diff_setup_done failed\");\n \t}\n+\trev.diffopt.allow_external = 1;\n \n \t/* Do we have --cached and not have a pending object, then\n \t * default to HEAD by hand.  Eek.\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 3a9b32f..cff9c5d 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -943,6 +943,7 @@ void diff_tree_combined(const unsigned char *sha1,\n \tdiffopts = *opt;\n \tdiffopts.output_format = DIFF_FORMAT_NO_OUTPUT;\n \tdiffopts.recursive = 1;\n+\tdiffopts.allow_external = 0;\n \n \tshow_log_first = !!rev->loginfo && !rev->no_commit_id;\n \tneedsep = 0;\ndiff --git a/diff.c b/diff.c\nindex f516664..bdbfeb6 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1077,8 +1077,8 @@ static int file_is_binary(struct diff_filespec *one)\n \t\telse if (ATTR_UNSET(value))\n \t\t\t;\n \t\telse\n-\t\t\tdie(\"unknown value %s given to 'diff' attribute\",\n-\t\t\t    value);\n+\t\t\t/* has external diff attribute */\n+\t\t\treturn 0;\n \t}\n \n \tif (!one->data) {\n@@ -1752,6 +1752,21 @@ static void run_external_diff(const char *pgm,\n \t}\n }\n \n+static const char *external_diff_attr(const char *name)\n+{\n+\tstruct git_attr_check attr_diff_check;\n+\n+\tsetup_diff_attr_check(&attr_diff_check);\n+\tif (!git_checkattr(name, 1, &attr_diff_check)) {\n+\t\tconst char *value = attr_diff_check.value;\n+\t\tif (!ATTR_TRUE(value) &&\n+\t\t    !ATTR_FALSE(value) &&\n+\t\t    !ATTR_UNSET(value))\n+\t\t\treturn value;\n+\t}\n+\treturn NULL;\n+}\n+\n static void run_diff_cmd(const char *pgm,\n \t\t\t const char *name,\n \t\t\t const char *other,\n@@ -1761,6 +1776,14 @@ static void run_diff_cmd(const char *pgm,\n \t\t\t struct diff_options *o,\n \t\t\t int complete_rewrite)\n {\n+\tif (!o->allow_external)\n+\t\tpgm = NULL;\n+\telse {\n+\t\tconst char *cmd = external_diff_attr(name);\n+\t\tif (cmd)\n+\t\t\tpgm = cmd;\n+\t}\n+\n \tif (pgm) {\n \t\trun_external_diff(pgm, name, other, one, two, xfrm_msg,\n \t\t\t\t  complete_rewrite);\ndiff --git a/diff.h b/diff.h\nindex a0d2ce1..63738c1 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -59,6 +59,7 @@ struct diff_options {\n \t\t color_diff_words:1,\n \t\t has_changes:1,\n \t\t quiet:1,\n+\t\t allow_external:1,\n \t\t exit_with_status:1;\n \tint context;\n \tint break_opt;\ndiff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh\nnew file mode 100755\nindex 0000000..60a93a7\n--- /dev/null\n+++ b/t/t4020-diff-external.sh\n@@ -0,0 +1,69 @@\n+#!/bin/sh\n+\n+test_description='external diff interface test'\n+\n+. ./test-lib.sh\n+\n+_z40=0000000000000000000000000000000000000000\n+\n+test_expect_success setup '\n+\n+\ttest_tick &&\n+\techo initial >file &&\n+\tgit add file &&\n+\tgit commit -m initial &&\n+\n+\ttest_tick &&\n+\techo second >file &&\n+\tgit add file &&\n+\tgit commit -m second &&\n+\n+\ttest_tick &&\n+\techo third >file\n+'\n+\n+test_expect_success 'GIT_EXTERNAL_DIFF environment' '\n+\n+\tGIT_EXTERNAL_DIFF=echo git diff | {\n+\t\tread path oldfile oldhex oldmode newfile newhex newmode &&\n+\t\ttest \"z$path\" = zfile &&\n+\t\ttest \"z$oldmode\" = z100644 &&\n+\t\ttest \"z$newhex\" = \"z$_z40\" &&\n+\t\ttest \"z$newmode\" = z100644 &&\n+\t\toh=$(git rev-parse --verify HEAD:file) &&\n+\t\ttest \"z$oh\" = \"z$oldhex\"\n+\t}\n+\n+'\n+\n+test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '\n+\n+\tGIT_EXTERNAL_DIFF=echo git log -p -1 HEAD |\n+\tgrep \"^diff --git a/file b/file\"\n+\n+'\n+\n+test_expect_success 'diff attribute' '\n+\n+\techo >.gitattributes \"file diff=echo\" &&\n+\n+\tgit diff | {\n+\t\tread path oldfile oldhex oldmode newfile newhex newmode &&\n+\t\ttest \"z$path\" = zfile &&\n+\t\ttest \"z$oldmode\" = z100644 &&\n+\t\ttest \"z$newhex\" = \"z$_z40\" &&\n+\t\ttest \"z$newmode\" = z100644 &&\n+\t\toh=$(git rev-parse --verify HEAD:file) &&\n+\t\ttest \"z$oh\" = \"z$oldhex\"\n+\t}\n+\n+'\n+\n+test_expect_success 'diff attribute should apply only to diff' '\n+\n+\tgit log -p -1 HEAD |\n+\tgrep \"^diff --git a/file b/file\"\n+\n+'\n+\n+test_done\n-- \n1.5.1.2.928.ge9eadf\n"}]}