From: John Cai <johncai86@xxxxxxxxx> The previous commit allows the diff machinery to read gitattributes from HEAD when a bare repository is being used. Some users may want to specify which commit to read gitattributes from. Teach diff a new --attr-source that can be used to pass in a commit. Signed-off-by: John Cai <johncai86@xxxxxxxxx> --- Documentation/diff-options.txt | 4 ++++ Documentation/gitattributes.txt | 8 ++++++-- diff.c | 16 ++++++++++----- diff.h | 1 + t/lib-diff-alternative.sh | 35 ++++++++++++++++++++++----------- t/t4018-diff-funcname.sh | 18 +++++++++++++++++ 6 files changed, 64 insertions(+), 18 deletions(-) diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 7d73e976d99..d3a04937dde 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -173,6 +173,10 @@ endif::git-log[] --anchored=<text>:: Generate a diff using the "anchored diff" algorithm. + +--attr-source=<commit>:: + Specify a commit to read gitattributes from when using a bare + repository when defining external drivers, or internal algorithms. + This option may be specified more than once. + diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 15488bd92b2..7f9451b56fa 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -758,7 +758,9 @@ with the above configuration, i.e. `j-c-diff`, with 7 parameters, just like `GIT_EXTERNAL_DIFF` program is called. See linkgit:git[1] for details. -When using a bare repository, the gitattributes from HEAD will be used. +When using a bare repository, the gitattributes from HEAD will be used, unless +the --attr-source option is used to specify a commit from which the +gitattributes will be read. Setting the internal diff algorithm ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -787,7 +789,9 @@ This diff algorithm applies to user facing diff output like git-diff(1), git-show(1) and is used for the `--stat` output as well. The merge machinery will not use the diff algorithm set through this method. -When using a bare repository, the gitattributes from HEAD will be used. +When using a bare repository, the gitattributes from HEAD will be used, unless +the --attr-source option is used to specify a commit from which the +gitattributes will be read. NOTE: If `diff.<name>.command` is defined for path with the `diff=<name>` attribute, it is executed as an external diff driver diff --git a/diff.c b/diff.c index 51baf893bb0..e86cf33b7a9 100644 --- a/diff.c +++ b/diff.c @@ -4448,10 +4448,13 @@ static void get_userdiff(struct diff_options *o, struct userdiff_driver **drv, const char *attr_path) { - const char *commit = "HEAD"; + const char *commit = o->attr_source; struct object_id *tree_oid = NULL; - if (is_bare_repository() && o->repo->gitdir) { + if (!commit) + commit = "HEAD"; + + if ((o->attr_source || is_bare_repository()) && o->repo->gitdir) { struct object_id oid; if (!get_oid(commit, &oid)) { @@ -4459,6 +4462,8 @@ static void get_userdiff(struct diff_options *o, if (t) tree_oid = &t->object.oid; + } else if (o->attr_source) { + die(_("%s is not a valid object"), commit); } } @@ -4480,10 +4485,8 @@ static void run_diff_cmd(const char *pgm, int must_show_header = 0; struct userdiff_driver *drv = NULL; - if (o->flags.allow_external || !o->ignore_driver_algorithm) { - + if (o->flags.allow_external || !o->ignore_driver_algorithm) get_userdiff(o, &drv, attr_path); - } if (o->flags.allow_external && drv && drv->external) pgm = drv->external; @@ -5699,6 +5702,9 @@ struct option *add_diff_options(const struct option *opts, N_("disable all output of the program")), OPT_BOOL(0, "ext-diff", &options->flags.allow_external, N_("allow an external diff helper to be executed")), + OPT_STRING(0, "attr-source", &options->attr_source, + N_("attributes-source"), + N_("the commit to read attributes from")), OPT_CALLBACK_F(0, "textconv", options, NULL, N_("run external text conversion filters when comparing binary files"), PARSE_OPT_NOARG, diff_opt_textconv), diff --git a/diff.h b/diff.h index 8d770b1d579..59db40f66be 100644 --- a/diff.h +++ b/diff.h @@ -334,6 +334,7 @@ struct diff_options { const char *stat_sep; int xdl_opts; int ignore_driver_algorithm; + const char *attr_source; /* see Documentation/diff-options.txt */ char **anchors; diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh index 0d99af83dd2..a7cd4966749 100644 --- a/t/lib-diff-alternative.sh +++ b/t/lib-diff-alternative.sh @@ -112,25 +112,38 @@ EOF STRATEGY=$1 - test_expect_success "$STRATEGY diff from attributes" ' + test_expect_success "setup attributes files for tests with $STRATEGY" ' + git checkout -b master && echo "file* diff=driver" >.gitattributes && - git config diff.driver.algorithm "$STRATEGY" && - test_must_fail git diff --no-index file1 file2 > output && - cat expect && - cat output && + git add file1 file2 .gitattributes && + git commit -m "adding files" && + git checkout -b branchA && + echo "file* diff=driverA" >.gitattributes && + git add .gitattributes && + git commit -m "adding driverA as diff driver" && + git checkout master && + git clone --bare --no-local . bare.git + ' + + test_expect_success "$STRATEGY diff from attributes" ' + test_must_fail git -c diff.driver.algorithm=$STRATEGY diff --no-index file1 file2 > output && test_cmp expect output ' test_expect_success "$STRATEGY diff from attributes with bare repo" ' - echo "file* diff=driver" >.gitattributes && - git add file1 file2 .gitattributes && - git commit -m "adding files" && - git clone --bare --no-local . bare.git && - git -C bare.git config diff.driver.algorithm "$STRATEGY" && - git -C bare.git diff HEAD:file1 HEAD:file2 > output && + git -C bare.git -c diff.driver.algorithm=$STRATEGY diff HEAD:file1 HEAD:file2 >output && + test_cmp expect output + ' + + test_expect_success "diff from attributes with bare repo when branch different than HEAD" ' + git -C bare.git -c diff.driver.algorithm=myers -c diff.driverA.algorithm=$STRATEGY diff --attr-source branchA HEAD:file1 HEAD:file2 >output && test_cmp expect output ' + test_expect_success "diff from attributes with bare repo with invalid source" ' + test_must_fail git -C bare.git diff --attr-source invalid-branch HEAD:file1 HEAD:file2 + ' + test_expect_success "$STRATEGY diff from attributes has valid diffstat" ' echo "file* diff=driver" >.gitattributes && git config diff.driver.algorithm "$STRATEGY" && diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index 451af08c611..30babcfae91 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -63,6 +63,7 @@ do test_i18ngrep ! fatal msg && test_i18ngrep ! error msg ' + test_expect_success "builtin $p pattern compiles on bare repo" ' test_when_finished "rm -rf bare.git" && echo "*.java diff=$p" >.gitattributes && @@ -74,6 +75,23 @@ do test_i18ngrep ! fatal msg && test_i18ngrep ! error msg ' + test_expect_success "builtin $p pattern compiles on bare repo with --attr-source" ' + test_when_finished "rm -rf bare.git" && + git checkout -B master && + echo "*.java diff=notexist" > .gitattributes && + git add .gitattributes && + git commit -am "changing gitattributes" && + git checkout -B branchA && + echo "*.java diff=$p" >.gitattributes && + git add .gitattributes && + git commit -am "changing gitattributes" && + git clone --bare --no-local . bare.git && + git -C bare.git symbolic-ref HEAD refs/heads/master && + test_expect_code 1 git -C bare.git diff --exit-code \ + --attr-source branchA HEAD:A.java HEAD:B.java 2>msg && + test_i18ngrep ! fatal msg && + test_i18ngrep ! error msg + ' done test_expect_success 'last regexp must not be negated' ' -- gitgitgadget