git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm
@ 2023-03-14  1:53 John Cai via GitGitGadget
  2023-03-14  1:53 ` [PATCH 1/2] diff: use HEAD for attributes when using bare repository John Cai via GitGitGadget
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: John Cai via GitGitGadget @ 2023-03-14  1:53 UTC (permalink / raw)
  To: git; +Cc: John Cai

This patch series adds support for bare repositories for the feature added
in [1]. When using a bare repository, by default we will look for
gitattributes from HEAD. When the --attr-source option is passed, we will
try to read gitattributes from the commit.

A side effect of this patch series is that custom drivers will now also work
with bare repositories.

 1. (a4cf900ee7 diff: teach diff to read algorithm from diff driver,
    2022-02-20)

John Cai (2):
  diff: use HEAD for attributes when using bare repository
  diff: add --attr-source to read gitattributes from a commit

 Documentation/diff-options.txt  |  4 ++++
 Documentation/gitattributes.txt |  8 +++++++
 diff.c                          | 37 ++++++++++++++++++++++++++++++---
 diff.h                          |  1 +
 t/lib-diff-alternative.sh       | 33 ++++++++++++++++++++++++-----
 t/t4018-diff-funcname.sh        | 29 ++++++++++++++++++++++++++
 userdiff.c                      |  9 +++++++-
 userdiff.h                      |  4 ++++
 8 files changed, 116 insertions(+), 9 deletions(-)


base-commit: d15644fe0226af7ffc874572d968598564a230dd
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1459%2Fjohn-cai%2Fjc%2Fdiff-attr-bare-repo-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1459/john-cai/jc/diff-attr-bare-repo-v1
Pull-Request: https://github.com/git/git/pull/1459
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-14  1:53 [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm John Cai via GitGitGadget
@ 2023-03-14  1:53 ` John Cai via GitGitGadget
  2023-03-14 16:54   ` Junio C Hamano
  2023-03-14  1:53 ` [PATCH 2/2] diff: add --attr-source to read gitattributes from a commit John Cai via GitGitGadget
  2023-03-14 17:21 ` [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm Philippe Blain
  2 siblings, 1 reply; 15+ messages in thread
From: John Cai via GitGitGadget @ 2023-03-14  1:53 UTC (permalink / raw)
  To: git; +Cc: John Cai, John Cai

From: John Cai <johncai86@gmail.com>

(a4cf900e diff: teach diff to read algorithm from diff driver,
2022-02-20) does not support bare repositories. Since running diff
on bare repositories is often done on Git servers, it would be useful to
allow bare repositories to also take advantage of setting the algorithm
via the diff driver.

Teach diff to check against the attributes from HEAD if a bare
repository is being operated on. This change also allows custom diff
drivers to work with bare repositories.

Signed-off-by: John Cai <johncai86@gmail.com>
---
 Documentation/gitattributes.txt |  4 ++++
 diff.c                          | 33 +++++++++++++++++++++++++++++----
 t/lib-diff-alternative.sh       | 10 ++++++++++
 t/t4018-diff-funcname.sh        | 11 +++++++++++
 userdiff.c                      |  9 ++++++++-
 userdiff.h                      |  4 ++++
 6 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 39bfbca1ffe..15488bd92b2 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -758,6 +758,8 @@ 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.
+
 Setting the internal diff algorithm
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -785,6 +787,8 @@ 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.
+
 NOTE: If `diff.<name>.command` is defined for path with the
 `diff=<name>` attribute, it is executed as an external diff driver
 (see above), and adding `diff.<name>.algorithm` has no effect, as the
diff --git a/diff.c b/diff.c
index 469e18aed20..51baf893bb0 100644
--- a/diff.c
+++ b/diff.c
@@ -29,6 +29,7 @@
 #include "promisor-remote.h"
 #include "dir.h"
 #include "strmap.h"
+#include "tree.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -4443,6 +4444,27 @@ static void fill_metainfo(struct strbuf *msg,
 	}
 }
 
+static void get_userdiff(struct diff_options *o,
+			     struct userdiff_driver **drv,
+			     const char *attr_path)
+{
+	const char *commit = "HEAD";
+	struct object_id *tree_oid = NULL;
+
+	if (is_bare_repository() && o->repo->gitdir) {
+		struct object_id oid;
+
+		if (!get_oid(commit, &oid)) {
+			struct tree *t = parse_tree_indirect(&oid);
+
+			if (t)
+				tree_oid = &t->object.oid;
+		}
+	}
+
+	*drv = userdiff_find_by_tree_and_path(o->repo->index, tree_oid, attr_path);
+}
+
 static void run_diff_cmd(const char *pgm,
 			 const char *name,
 			 const char *other,
@@ -4458,8 +4480,10 @@ 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)
-		drv = userdiff_find_by_path(o->repo->index, attr_path);
+	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;
@@ -4586,8 +4610,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
 	const char *other;
 
 	if (!o->ignore_driver_algorithm) {
-		struct userdiff_driver *drv = userdiff_find_by_path(o->repo->index,
-								    p->one->path);
+		struct userdiff_driver *drv = NULL;
+
+		get_userdiff(o, &drv, p->one->path);
 
 		if (drv && drv->algorithm)
 			set_diff_algorithm(o, drv->algorithm);
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
index a8f5d3274a5..0d99af83dd2 100644
--- a/t/lib-diff-alternative.sh
+++ b/t/lib-diff-alternative.sh
@@ -121,6 +121,16 @@ EOF
 		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 &&
+		test_cmp expect output
+	'
+
 	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 42a2b9a13b7..451af08c611 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -63,6 +63,17 @@ 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 &&
+		git add . &&
+		git commit -am "adding files" &&
+		git clone --bare --no-local . bare.git &&
+		test_expect_code 1 git -C bare.git diff --exit-code \
+			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' '
diff --git a/userdiff.c b/userdiff.c
index 58a3d59ef8f..2305d363244 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -408,6 +408,13 @@ struct userdiff_driver *userdiff_find_by_name(const char *name)
 
 struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 					      const char *path)
+{
+	return userdiff_find_by_tree_and_path(istate, NULL, path);
+}
+
+struct userdiff_driver *userdiff_find_by_tree_and_path(struct index_state *istate,
+						       const struct object_id *tree_oid,
+						       const char *path)
 {
 	static struct attr_check *check;
 
@@ -415,7 +422,7 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 		check = attr_check_initl("diff", NULL);
 	if (!path)
 		return NULL;
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, tree_oid, path, check);
 
 	if (ATTR_TRUE(check->items[0].value))
 		return &driver_true;
diff --git a/userdiff.h b/userdiff.h
index 24419db6973..f9cd7c238de 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -33,6 +33,10 @@ int userdiff_config(const char *k, const char *v);
 struct userdiff_driver *userdiff_find_by_name(const char *name);
 struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 					      const char *path);
+struct userdiff_driver *userdiff_find_by_tree_and_path(struct index_state *istate,
+						       const struct object_id *tree_oid,
+						       const char *path);
+
 
 /*
  * Initialize any textconv-related fields in the driver and return it, or NULL
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/2] diff: add --attr-source to read gitattributes from a commit
  2023-03-14  1:53 [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm John Cai via GitGitGadget
  2023-03-14  1:53 ` [PATCH 1/2] diff: use HEAD for attributes when using bare repository John Cai via GitGitGadget
@ 2023-03-14  1:53 ` John Cai via GitGitGadget
  2023-03-14 17:21 ` [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm Philippe Blain
  2 siblings, 0 replies; 15+ messages in thread
From: John Cai via GitGitGadget @ 2023-03-14  1:53 UTC (permalink / raw)
  To: git; +Cc: John Cai, John Cai

From: John Cai <johncai86@gmail.com>

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@gmail.com>
---
 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

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-14  1:53 ` [PATCH 1/2] diff: use HEAD for attributes when using bare repository John Cai via GitGitGadget
@ 2023-03-14 16:54   ` Junio C Hamano
  2023-03-14 17:43     ` Junio C Hamano
  0 siblings, 1 reply; 15+ messages in thread
From: Junio C Hamano @ 2023-03-14 16:54 UTC (permalink / raw)
  To: John Cai via GitGitGadget; +Cc: git, John Cai

"John Cai via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: John Cai <johncai86@gmail.com>
>
> (a4cf900e diff: teach diff to read algorithm from diff driver,
> 2022-02-20) does not support bare repositories.

Wrong placement of opening parenthesis, i.e. "a4cf900e (diff: teach
diff to read algorithm from diff driver, 2023-02-20)".  The date is
also wrong.  Use "git show -s --format=reference a4cf900e".  Insert
its output directly to your editor instead of transcribing manually.

I do not think the commit is to blame in the first place for two
reasons.

 * Even in a bare repository, the attributes defined in the
   $GIT_DIR/info/attributes file are in effect, and the new feature
   added by a4cf900e should work just fine with it.

 * By definition, there is no working tree in a bare repository, and
   it always has been naturally expected we won't read attributes
   from working tree files.  This is in no way limited to the diff
   driver feature recently added.

So the above does not look like a good first sentence to explain
this change.

> Since running diff
> on bare repositories is often done on Git servers, it would be useful to
> allow bare repositories to also take advantage of setting the algorithm
> via the diff driver.

Since referring to in-tree attributes is often useful with any
command, not limited to "diff", I wonder if it is feasible to add
support for the --attr-source=<tree> option globally, instead of
implementing such an option piecemeal.  If your "git diff" in a bare
repository starts honoring attributes recorded in HEAD, don't your
users expect your "git log" and "git show" to also honor them?

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm
  2023-03-14  1:53 [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm John Cai via GitGitGadget
  2023-03-14  1:53 ` [PATCH 1/2] diff: use HEAD for attributes when using bare repository John Cai via GitGitGadget
  2023-03-14  1:53 ` [PATCH 2/2] diff: add --attr-source to read gitattributes from a commit John Cai via GitGitGadget
@ 2023-03-14 17:21 ` Philippe Blain
  2023-03-14 19:18   ` John Cai
  2 siblings, 1 reply; 15+ messages in thread
From: Philippe Blain @ 2023-03-14 17:21 UTC (permalink / raw)
  To: John Cai via GitGitGadget, git; +Cc: John Cai

Hi John,

Le 2023-03-13 à 21:53, John Cai via GitGitGadget a écrit :
> This patch series adds support for bare repositories for the feature added
> in [1]. When using a bare repository, by default we will look for
> gitattributes from HEAD. When the --attr-source option is passed, we will
> try to read gitattributes from the commit.

When I read that I immediately thought of the config settings 'mailmap.file', 
'mailmap.blob' which allow the same sort of thing, but for the mailmap.

For the sake of consistency of the whole system, maybe we would want to support
'attr.file' and 'attr.blob' ? In that case, maybe we don't need a new command
line option at all...

Cheers,

Philippe.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-14 16:54   ` Junio C Hamano
@ 2023-03-14 17:43     ` Junio C Hamano
  2023-03-14 19:38       ` John Cai
  0 siblings, 1 reply; 15+ messages in thread
From: Junio C Hamano @ 2023-03-14 17:43 UTC (permalink / raw)
  To: John Cai via GitGitGadget; +Cc: git, John Cai

Junio C Hamano <gitster@pobox.com> writes:

> Since referring to in-tree attributes is often useful with any
> command, not limited to "diff", I wonder if it is feasible to add
> support for the --attr-source=<tree> option globally, instead of
> implementing such an option piecemeal.  If your "git diff" in a bare
> repository starts honoring attributes recorded in HEAD, don't your
> users expect your "git log" and "git show" to also honor them?

Just for illustration, here is one way to do so.

The implementation goes in the opposite direction from the more
recent trend, which is why I am not making it an official patch, but
with this you can do things like:

  $ git --attr-source=e83c5163 check-attr whitespace cache.h
  cache.h: whitespace: unspecified
  $ git --attr-source=e2f6331a142^ check-attr whitespace cache.h
  cache.h: whitespace: set
  $ git --attr-source=HEAD check-attr whitespace cache.h
  cache.h: whitespace: indent,trail,space

where e83c5163 is the very first version of Git from 2005 where
.gitattributes did not exist, e2f6331a142^ is the last version
before we set whitespace to indent,trail,space, and HEAD is a more
recent version of our source tree.

In the following illustration patch, the attr-source tree object
name is kept as a string as long as possible and at the very last
minute when we call git_check_attr() for the first time we turn it
into an object id.  This is because at the very early stage when we
parse the global option we may not even know where our repository is
(hence do not have enough information to parse HEAD).  We also figure
out is_bare_repository() late in the process for the same reason.



 attr.c | 29 +++++++++++++++++++++++++++++
 attr.h |  6 ++++++
 git.c  |  3 +++
 3 files changed, 38 insertions(+)

diff --git c/attr.c w/attr.c
index 1053dfcd4b..2fd6e0eab2 100644
--- c/attr.c
+++ w/attr.c
@@ -1165,12 +1165,41 @@ static void collect_some_attrs(struct index_state *istate,
 	fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
 }
 
+static const char *default_attr_source_tree_object_name;
+
+void set_git_attr_source(const char *tree_object_name)
+{
+	default_attr_source_tree_object_name = xstrdup(tree_object_name);
+}
+
+
+static struct object_id *default_attr_source(void)
+{
+	static struct object_id attr_source;
+
+	if (is_null_oid(&attr_source)) {
+		if (!default_attr_source_tree_object_name &&
+		    is_bare_repository())
+			default_attr_source_tree_object_name = "HEAD";
+
+		if (default_attr_source_tree_object_name &&
+		    is_null_oid(&attr_source))
+			get_oid(default_attr_source_tree_object_name, &attr_source);
+	}
+	if (is_null_oid(&attr_source))
+		return NULL;
+	return &attr_source;
+}
+
 void git_check_attr(struct index_state *istate,
 		    const struct object_id *tree_oid, const char *path,
 		    struct attr_check *check)
 {
 	int i;
 
+	if (!tree_oid)
+		tree_oid = default_attr_source();
+
 	collect_some_attrs(istate, tree_oid, path, check);
 
 	for (i = 0; i < check->nr; i++) {
diff --git c/attr.h w/attr.h
index 9884ea2bc6..a77e3713b2 100644
--- c/attr.h
+++ w/attr.h
@@ -135,6 +135,12 @@ struct git_attr;
 struct all_attrs_item;
 struct attr_stack;
 
+/*
+ * The textual object name for the tree-ish used by git_check_attr()
+ * when NULL is given to tree_oid.
+ */
+void set_git_attr_source(const char *);
+
 /*
  * Given a string, return the gitattribute object that
  * corresponds to it.
diff --git c/git.c w/git.c
index 6171fd6769..21bddc5718 100644
--- c/git.c
+++ w/git.c
@@ -4,6 +4,7 @@
 #include "help.h"
 #include "run-command.h"
 #include "alias.h"
+#include "attr.h"
 #include "shallow.h"
 
 #define RUN_SETUP		(1<<0)
@@ -307,6 +308,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 			} else {
 				exit(list_cmds(cmd));
 			}
+		} else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
+			set_git_attr_source(cmd);
 		} else {
 			fprintf(stderr, _("unknown option: %s\n"), cmd);
 			usage(git_usage_string);

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm
  2023-03-14 17:21 ` [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm Philippe Blain
@ 2023-03-14 19:18   ` John Cai
  2023-03-14 20:44     ` Junio C Hamano
  0 siblings, 1 reply; 15+ messages in thread
From: John Cai @ 2023-03-14 19:18 UTC (permalink / raw)
  To: Philippe Blain; +Cc: John Cai via GitGitGadget, git

On 23/03/14 01:21PM, Philippe Blain wrote:
> Hi John,

Hi Philippe,

> 
> Le 2023-03-13 à 21:53, John Cai via GitGitGadget a écrit :
> > This patch series adds support for bare repositories for the feature added
> > in [1]. When using a bare repository, by default we will look for
> > gitattributes from HEAD. When the --attr-source option is passed, we will
> > try to read gitattributes from the commit.
> 
> When I read that I immediately thought of the config settings 'mailmap.file', 
> 'mailmap.blob' which allow the same sort of thing, but for the mailmap.
> 
> For the sake of consistency of the whole system, maybe we would want to support
> 'attr.file' and 'attr.blob' ? In that case, maybe we don't need a new command
> line option at all...

I wasn't aware of those config options. Indeed that's a good option! My only
concern with that is that there is already some precedence for passing a
<tree-ish> as a source for attributes in [1], so I thought adding a command line
option would be somewhat consistent.

But I can also see the benefit of using a config value since there is also
precedence because of mailmap.file and mailmap.blob. Not sure what others think,
but this may be the less invasive way to go.

1. https://git-scm.com/docs/git-check-attr#Documentation/git-check-attr.txt---sourcelttree-ishgt

> 
> Cheers,
> 
> Philippe.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-14 17:43     ` Junio C Hamano
@ 2023-03-14 19:38       ` John Cai
  2023-03-14 20:37         ` Junio C Hamano
  0 siblings, 1 reply; 15+ messages in thread
From: John Cai @ 2023-03-14 19:38 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: John Cai via GitGitGadget, git

On 23/03/14 10:43AM, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:

Hi Junio,

> 
> > Since referring to in-tree attributes is often useful with any
> > command, not limited to "diff", I wonder if it is feasible to add
> > support for the --attr-source=<tree> option globally, instead of
> > implementing such an option piecemeal.  If your "git diff" in a bare
> > repository starts honoring attributes recorded in HEAD, don't your
> > users expect your "git log" and "git show" to also honor them?
> 
> Just for illustration, here is one way to do so.
> 
> The implementation goes in the opposite direction from the more
> recent trend, which is why I am not making it an official patch, but

Could you explain why this goes against the "more recent trend" for my
understanding?

> with this you can do things like:
> 
>   $ git --attr-source=e83c5163 check-attr whitespace cache.h
>   cache.h: whitespace: unspecified
>   $ git --attr-source=e2f6331a142^ check-attr whitespace cache.h
>   cache.h: whitespace: set
>   $ git --attr-source=HEAD check-attr whitespace cache.h
>   cache.h: whitespace: indent,trail,space

I like the idea of an option that is global. For git-check-attr however, we
already have a --source option. Not sure how big of a deal that is. If we wanted
to, we could put --source on a deprecation path by making it hidden for a
while, etc.

> 
> where e83c5163 is the very first version of Git from 2005 where
> .gitattributes did not exist, e2f6331a142^ is the last version
> before we set whitespace to indent,trail,space, and HEAD is a more
> recent version of our source tree.
> 
> In the following illustration patch, the attr-source tree object
> name is kept as a string as long as possible and at the very last
> minute when we call git_check_attr() for the first time we turn it
> into an object id.  This is because at the very early stage when we
> parse the global option we may not even know where our repository is
> (hence do not have enough information to parse HEAD).  We also figure
> out is_bare_repository() late in the process for the same reason.
> 
> 
> 
>  attr.c | 29 +++++++++++++++++++++++++++++
>  attr.h |  6 ++++++
>  git.c  |  3 +++
>  3 files changed, 38 insertions(+)
> 
> diff --git c/attr.c w/attr.c
> index 1053dfcd4b..2fd6e0eab2 100644
> --- c/attr.c
> +++ w/attr.c
> @@ -1165,12 +1165,41 @@ static void collect_some_attrs(struct index_state *istate,
>  	fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
>  }
>  
> +static const char *default_attr_source_tree_object_name;
> +
> +void set_git_attr_source(const char *tree_object_name)
> +{
> +	default_attr_source_tree_object_name = xstrdup(tree_object_name);
> +}
> +
> +
> +static struct object_id *default_attr_source(void)
> +{
> +	static struct object_id attr_source;
> +
> +	if (is_null_oid(&attr_source)) {
> +		if (!default_attr_source_tree_object_name &&
> +		    is_bare_repository())
> +			default_attr_source_tree_object_name = "HEAD";
> +
> +		if (default_attr_source_tree_object_name &&
> +		    is_null_oid(&attr_source))
> +			get_oid(default_attr_source_tree_object_name, &attr_source);
> +	}
> +	if (is_null_oid(&attr_source))
> +		return NULL;
> +	return &attr_source;
> +}
> +
>  void git_check_attr(struct index_state *istate,
>  		    const struct object_id *tree_oid, const char *path,
>  		    struct attr_check *check)
>  {
>  	int i;
>  
> +	if (!tree_oid)
> +		tree_oid = default_attr_source();
> +
>  	collect_some_attrs(istate, tree_oid, path, check);
>  
>  	for (i = 0; i < check->nr; i++) {
> diff --git c/attr.h w/attr.h
> index 9884ea2bc6..a77e3713b2 100644
> --- c/attr.h
> +++ w/attr.h
> @@ -135,6 +135,12 @@ struct git_attr;
>  struct all_attrs_item;
>  struct attr_stack;
>  
> +/*
> + * The textual object name for the tree-ish used by git_check_attr()
> + * when NULL is given to tree_oid.
> + */
> +void set_git_attr_source(const char *);
> +
>  /*
>   * Given a string, return the gitattribute object that
>   * corresponds to it.
> diff --git c/git.c w/git.c
> index 6171fd6769..21bddc5718 100644
> --- c/git.c
> +++ w/git.c
> @@ -4,6 +4,7 @@
>  #include "help.h"
>  #include "run-command.h"
>  #include "alias.h"
> +#include "attr.h"
>  #include "shallow.h"
>  
>  #define RUN_SETUP		(1<<0)
> @@ -307,6 +308,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
>  			} else {
>  				exit(list_cmds(cmd));
>  			}
> +		} else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
> +			set_git_attr_source(cmd);
>  		} else {
>  			fprintf(stderr, _("unknown option: %s\n"), cmd);
>  			usage(git_usage_string);

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-14 19:38       ` John Cai
@ 2023-03-14 20:37         ` Junio C Hamano
  2023-03-16 16:46           ` John Cai
  0 siblings, 1 reply; 15+ messages in thread
From: Junio C Hamano @ 2023-03-14 20:37 UTC (permalink / raw)
  To: John Cai; +Cc: John Cai via GitGitGadget, git

John Cai <johncai86@gmail.com> writes:

>> Just for illustration, here is one way to do so.
>> 
>> The implementation goes in the opposite direction from the more
>> recent trend, which is why I am not making it an official patch, but
>
> Could you explain why this goes against the "more recent trend" for my
> understanding?

The illustration uses a global state.

The recent trend is to reduce reliance on global states and use the
repository object and others that hold such state through the
callchain.

But a new global variable that holds the fallback tree-ish object name
was a so convenient way to illustrate the core of the idea, without
having to change many callchains.

>> with this you can do things like:
>> 
>>   $ git --attr-source=e83c5163 check-attr whitespace cache.h
>>   cache.h: whitespace: unspecified
>>   $ git --attr-source=e2f6331a142^ check-attr whitespace cache.h
>>   cache.h: whitespace: set
>>   $ git --attr-source=HEAD check-attr whitespace cache.h
>>   cache.h: whitespace: indent,trail,space
>
> I like the idea of an option that is global. For git-check-attr however, we

I guess I shouldn't have used check-attr to avoid confusion.

The point is that the internal mechanisms introduced by 47cfc9bd
(attr: add flag `--source` to work with tree-ish, 2023-01-14), which
taught check-attr the --source option and is reused by this
illustration patch, was a good idea, but its UI was a mistake.  We
do not need per-command --source option the commit adds if we did
the global option from day one.  Yes, I think we can deprecate the
"--source" option from there, if we all prefer the global option
avenue.  I _think_ git_all_attrs() needs to be told about the
default attr-source trick (which I didn't touch in my illustration
patch) before it happens, though.

If you used the mechanism in the illustration patch I gave you, and
adjusted the test part of your patch to match (i.e. "diff" does not
learn "--attr-source" option, but "git --attr-source=... diff" is
how you make it read attributes from a tree-ish), would the result
work well, or do we need more work to make it usable?  How well do
other commands (e.g. "git show") work in the same test repository
you created in your version?

Thanks.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm
  2023-03-14 19:18   ` John Cai
@ 2023-03-14 20:44     ` Junio C Hamano
  2023-03-16 14:29       ` Jeff King
  0 siblings, 1 reply; 15+ messages in thread
From: Junio C Hamano @ 2023-03-14 20:44 UTC (permalink / raw)
  To: John Cai; +Cc: Philippe Blain, John Cai via GitGitGadget, git

John Cai <johncai86@gmail.com> writes:

> I wasn't aware of those config options. Indeed that's a good option! My only
> concern with that is that there is already some precedence for passing a
> <tree-ish> as a source for attributes in [1], so I thought adding a command line
> option would be somewhat consistent.
>
> But I can also see the benefit of using a config value since there is also
> precedence because of mailmap.file and mailmap.blob. Not sure what others think,
> but this may be the less invasive way to go.

I actually hate these one-off variables, and I wish we didn't do
mailmap.file or mailmap.blob at all.  Instead we could have taught
the machinery to read from $GIT_DIR/info/mailmap just like the
exclude and the attribute machinery already knows to read excludea
and attributes files in the directory.

As your "per filetype (determined by the attribute) diff algorithm
in a bare repository" feature already depends on being able to use
config (as the look-up is "attributes determines the diff filetype
name, and then diff.<filetype>.algo is looked up in the config") to
configure, it does not sound too bad to add the attribute info in
the $GIT_DIR/info/attributes file in your bare repository (and you'd
set the algorithm in $GIT_DIR/config file there), and it would just
work without any new "read from HEAD" feature, I would presume?

Thanks.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm
  2023-03-14 20:44     ` Junio C Hamano
@ 2023-03-16 14:29       ` Jeff King
  2023-03-16 16:44         ` Junio C Hamano
  0 siblings, 1 reply; 15+ messages in thread
From: Jeff King @ 2023-03-16 14:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: John Cai, Philippe Blain, John Cai via GitGitGadget, git

On Tue, Mar 14, 2023 at 01:44:11PM -0700, Junio C Hamano wrote:

> John Cai <johncai86@gmail.com> writes:
> 
> > I wasn't aware of those config options. Indeed that's a good option! My only
> > concern with that is that there is already some precedence for passing a
> > <tree-ish> as a source for attributes in [1], so I thought adding a command line
> > option would be somewhat consistent.
> >
> > But I can also see the benefit of using a config value since there is also
> > precedence because of mailmap.file and mailmap.blob. Not sure what others think,
> > but this may be the less invasive way to go.
> 
> I actually hate these one-off variables, and I wish we didn't do
> mailmap.file or mailmap.blob at all.  Instead we could have taught
> the machinery to read from $GIT_DIR/info/mailmap just like the
> exclude and the attribute machinery already knows to read excludea
> and attributes files in the directory.

I don't think $GIT_DIR/info/mailmap is a good substitute for
mailmap.blob. The point there is to pull it from the repository, and
you'd have to do:

  git cat-file HEAD:.mailmap >.git/info/mailmap

before each command to get the equivalent. It is even worse for
attributes, because the in-tree representation is not even a single file
that is compatible with $GIT_DIR/ (there can be many attributes spread
throughout the tree).

It's true that mailmap.file, when specified in the local config, is
redundant with a repo-level info/mailmap file. I always assumed that
people put it in their ~/.gitconfig, though, to cover multiple projects
(just like we have core.attributesFile in addition to the in-repo
meta-file).

> As your "per filetype (determined by the attribute) diff algorithm
> in a bare repository" feature already depends on being able to use
> config (as the look-up is "attributes determines the diff filetype
> name, and then diff.<filetype>.algo is looked up in the config") to
> configure, it does not sound too bad to add the attribute info in
> the $GIT_DIR/info/attributes file in your bare repository (and you'd
> set the algorithm in $GIT_DIR/config file there), and it would just
> work without any new "read from HEAD" feature, I would presume?

I don't think that's quite a substitute.

If you have a static list of attributes, that is OK (though you are
presumably better off with /etc/gitattributes or similar that covers all
repos). But if you want to respect in-repo ones, you need to look at the
whole tree (and can't even just dump them out).

It does seem at first glance that you are limited to a static list of
attributes, because any diff "type" you mention has to have a
corresponding entry in the config to do anything useful. But these lists
are only loosely coupled. For example, you could have static entries
like this:

  # in config
  [diff "json"]
  algorithm = patience

  # in attributes
  *.json diff=json

but still tell users that "json" is a well-known name, and if they want
to get the same treatment for an arbitrary file "foo" that they would
get for "foo.json", they have to add attributes mapping it, like:

  foo diff=json

inside their repo.

-Peff

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm
  2023-03-16 14:29       ` Jeff King
@ 2023-03-16 16:44         ` Junio C Hamano
  0 siblings, 0 replies; 15+ messages in thread
From: Junio C Hamano @ 2023-03-16 16:44 UTC (permalink / raw)
  To: Jeff King; +Cc: John Cai, Philippe Blain, John Cai via GitGitGadget, git

Jeff King <peff@peff.net> writes:

> If you have a static list of attributes, that is OK (though you are
> presumably better off with /etc/gitattributes or similar that covers all
> repos).
> ...
> but still tell users that "json" is a well-known name, and if they want
> to get the same treatment for an arbitrary file "foo" that they would
> get for "foo.json", they have to add attributes mapping it, like:
>
>   foo diff=json
>
> inside their repo.

Yes, you described the reason very well why we didn't add "global"
or "system-wide" attribute files.

Thanks for commenting on my somewhat tongue-in-cheek "devil's
advocate" remark.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-14 20:37         ` Junio C Hamano
@ 2023-03-16 16:46           ` John Cai
  2023-03-16 22:56             ` Junio C Hamano
  0 siblings, 1 reply; 15+ messages in thread
From: John Cai @ 2023-03-16 16:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: John Cai via GitGitGadget, git

Hi Junio,

On 14 Mar 2023, at 16:37, Junio C Hamano wrote:

> John Cai <johncai86@gmail.com> writes:
>
>>> Just for illustration, here is one way to do so.
>>>
>>> The implementation goes in the opposite direction from the more
>>> recent trend, which is why I am not making it an official patch, but
>>
>> Could you explain why this goes against the "more recent trend" for my
>> understanding?
>
> The illustration uses a global state.
>
> The recent trend is to reduce reliance on global states and use the
> repository object and others that hold such state through the
> callchain.

Ah I see--in that case, what would be a good object to put this state into? Mabe
repo_settings?

>
> But a new global variable that holds the fallback tree-ish object name
> was a so convenient way to illustrate the core of the idea, without
> having to change many callchains.
>
>>> with this you can do things like:
>>>
>>>   $ git --attr-source=e83c5163 check-attr whitespace cache.h
>>>   cache.h: whitespace: unspecified
>>>   $ git --attr-source=e2f6331a142^ check-attr whitespace cache.h
>>>   cache.h: whitespace: set
>>>   $ git --attr-source=HEAD check-attr whitespace cache.h
>>>   cache.h: whitespace: indent,trail,space
>>
>> I like the idea of an option that is global. For git-check-attr however, we
>
> I guess I shouldn't have used check-attr to avoid confusion.
>
> The point is that the internal mechanisms introduced by 47cfc9bd
> (attr: add flag `--source` to work with tree-ish, 2023-01-14), which
> taught check-attr the --source option and is reused by this
> illustration patch, was a good idea, but its UI was a mistake.  We
> do not need per-command --source option the commit adds if we did
> the global option from day one.  Yes, I think we can deprecate the
> "--source" option from there, if we all prefer the global option
> avenue.  I _think_ git_all_attrs() needs to be told about the
> default attr-source trick (which I didn't touch in my illustration
> patch) before it happens, though.

Good point. I think to have a global flag would be a better user experience.

>
> If you used the mechanism in the illustration patch I gave you, and
> adjusted the test part of your patch to match (i.e. "diff" does not
> learn "--attr-source" option, but "git --attr-source=... diff" is
> how you make it read attributes from a tree-ish), would the result
> work well, or do we need more work to make it usable?  How well do
> other commands (e.g. "git show") work in the same test repository
> you created in your version?

I think it works pretty smoothly. I adjusted my tests to work with this git
flag, and I also tested git show and it works well.

In fact, your proposal would serve the needs of my patch independently of any of
the diff code, which is nice.

>
> Thanks.

Thanks,
John

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-16 16:46           ` John Cai
@ 2023-03-16 22:56             ` Junio C Hamano
  2023-03-17 14:11               ` John Cai
  0 siblings, 1 reply; 15+ messages in thread
From: Junio C Hamano @ 2023-03-16 22:56 UTC (permalink / raw)
  To: John Cai; +Cc: John Cai via GitGitGadget, git

John Cai <johncai86@gmail.com> writes:

> Ah I see--in that case, what would be a good object to put this state into? Mabe
> repo_settings?

I personally think a global is good enough for a case like this.  It
is not like the code in the attribute subsystem is structured to
work in multiple repositories at the same time in the first place, so
in repo_settings, the_repository, or elsewhere is just as good as
having it in a plain old file-scope static, and even after moving it
into the_repository or something, the access will initially be done
by referring to the_repository global (which may not even exist by
the time "git --attr-source=<tree>" gets parsed), and not by plumbing
a "struct repository *" parameter through the callchain.

Another thing to note is that there needs a mechanism to convey the
tree object used to read the attributes from down to subprocesses,
e.g. exporting the GIT_ATTR_SOURCE environment variable and make Git
behave as if it saw the "git --attr-source=<tree>" option when such
an environment variable exists, or something.  Otherwise, the custom
attribute source would only take effect inside built-in commands.

In any case, here is what I have now, stealing some tests from your
patch.  I do not think I'll be working on it further at least for
now, so feel free to run with it.  I am not adding the "in a bare
repository, always read from HEAD unless told otherwise", as I do
not see a good way to countermand it from a command line.

----- >8 ----- cut here ----- >8 ----- cut here ----- >8 -----
Subject: [PATCH] attr: teach "--attr-source=<tree>" global option to "git"

Earlier, 47cfc9bd (attr: add flag `--source` to work with tree-ish,
2023-01-14) taught "git check-attr" the "--source=<tree>" option to
allow it to read attribute files from a tree-ish, but did so only
for the command.  Just like "check-attr" users wanted a way to use
attributes from a tree-ish and not from the working tree files,
users of other commands (like "git diff") would benefit from the
same.

Undo most of the UI change the commit made, while keeping the
internal logic to read attributes from a given tree-ish.  Expose the
internal logic via a new "--attr-source=<tree>" command line option
given to "git", so that it can be used with any git command that
runs internally.

The tests were stolen and adjusted from John Cai's effort where only
"git diff" learned the "--attr-source" option to read from a
tree-ish.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 archive.c                 |  2 +-
 attr.c                    | 34 ++++++++++++++++++++++++++++++++--
 attr.h                    | 13 +++++++++----
 builtin/check-attr.c      | 17 ++++++++---------
 builtin/pack-objects.c    |  2 +-
 convert.c                 |  2 +-
 git.c                     |  3 +++
 ll-merge.c                |  4 ++--
 pathspec.c                |  2 +-
 t/lib-diff-alternative.sh | 31 ++++++++++++++++++++++++++-----
 t/t0003-attributes.sh     |  7 ++++++-
 t/t4018-diff-funcname.sh  | 19 +++++++++++++++++++
 userdiff.c                |  2 +-
 ws.c                      |  2 +-
 14 files changed, 111 insertions(+), 29 deletions(-)

diff --git c/archive.c w/archive.c
index 9aeaf2bd87..385aa5f248 100644
--- c/archive.c
+++ w/archive.c
@@ -120,7 +120,7 @@ static const struct attr_check *get_archive_attrs(struct index_state *istate,
 	static struct attr_check *check;
 	if (!check)
 		check = attr_check_initl("export-ignore", "export-subst", NULL);
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	return check;
 }
 
diff --git c/attr.c w/attr.c
index 1053dfcd4b..d34c7e9d54 100644
--- c/attr.c
+++ w/attr.c
@@ -1165,11 +1165,40 @@ static void collect_some_attrs(struct index_state *istate,
 	fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
 }
 
+static const char *default_attr_source_tree_object_name;
+
+void set_git_attr_source(const char *tree_object_name)
+{
+	default_attr_source_tree_object_name = xstrdup(tree_object_name);
+}
+
+
+static void compute_default_attr_source(struct object_id *attr_source)
+{
+	if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
+		return;
+
+	if (get_oid_treeish(default_attr_source_tree_object_name, attr_source))
+		die(_("bad --attr-source object"));
+}
+
+static struct object_id *default_attr_source(void)
+{
+	static struct object_id attr_source;
+
+	if (is_null_oid(&attr_source))
+		compute_default_attr_source(&attr_source);
+	if (is_null_oid(&attr_source))
+		return NULL;
+	return &attr_source;
+}
+
 void git_check_attr(struct index_state *istate,
-		    const struct object_id *tree_oid, const char *path,
+		    const char *path,
 		    struct attr_check *check)
 {
 	int i;
+	const struct object_id *tree_oid = default_attr_source();
 
 	collect_some_attrs(istate, tree_oid, path, check);
 
@@ -1182,10 +1211,11 @@ void git_check_attr(struct index_state *istate,
 	}
 }
 
-void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
+void git_all_attrs(struct index_state *istate,
 		   const char *path, struct attr_check *check)
 {
 	int i;
+	const struct object_id *tree_oid = default_attr_source();
 
 	attr_check_reset(check);
 	collect_some_attrs(istate, tree_oid, path, check);
diff --git c/attr.h w/attr.h
index 9884ea2bc6..676bd17ce2 100644
--- c/attr.h
+++ w/attr.h
@@ -45,7 +45,7 @@
  * const char *path;
  *
  * setup_check();
- * git_check_attr(&the_index, tree_oid, path, check);
+ * git_check_attr(&the_index, path, check);
  * ------------
  *
  * - Act on `.value` member of the result, left in `check->items[]`:
@@ -120,7 +120,6 @@
 #define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
 
 struct index_state;
-struct object_id;
 
 /**
  * An attribute is an opaque object that is identified by its name. Pass the
@@ -135,6 +134,12 @@ struct git_attr;
 struct all_attrs_item;
 struct attr_stack;
 
+/*
+ * The textual object name for the tree-ish used by git_check_attr()
+ * to read attributes from (instead of from the working tree).
+ */
+void set_git_attr_source(const char *);
+
 /*
  * Given a string, return the gitattribute object that
  * corresponds to it.
@@ -203,14 +208,14 @@ void attr_check_free(struct attr_check *check);
 const char *git_attr_name(const struct git_attr *);
 
 void git_check_attr(struct index_state *istate,
-		    const struct object_id *tree_oid, const char *path,
+		    const char *path,
 		    struct attr_check *check);
 
 /*
  * Retrieve all attributes that apply to the specified path.
  * check holds the attributes and their values.
  */
-void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
+void git_all_attrs(struct index_state *istate,
 		   const char *path, struct attr_check *check);
 
 enum git_attr_direction {
diff --git c/builtin/check-attr.c w/builtin/check-attr.c
index d7a40e674c..1a7929c980 100644
--- c/builtin/check-attr.c
+++ w/builtin/check-attr.c
@@ -58,7 +58,7 @@ static void output_attr(struct attr_check *check, const char *file)
 }
 
 static void check_attr(const char *prefix, struct attr_check *check,
-		       const struct object_id *tree_oid, int collect_all,
+		       int collect_all,
 		       const char *file)
 
 {
@@ -66,9 +66,9 @@ static void check_attr(const char *prefix, struct attr_check *check,
 		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
 
 	if (collect_all) {
-		git_all_attrs(&the_index, tree_oid, full_path, check);
+		git_all_attrs(&the_index, full_path, check);
 	} else {
-		git_check_attr(&the_index, tree_oid, full_path, check);
+		git_check_attr(&the_index, full_path, check);
 	}
 	output_attr(check, file);
 
@@ -76,7 +76,7 @@ static void check_attr(const char *prefix, struct attr_check *check,
 }
 
 static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
-				   const struct object_id *tree_oid, int collect_all)
+				   int collect_all)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct strbuf unquoted = STRBUF_INIT;
@@ -90,7 +90,7 @@ static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
 				die("line is badly quoted");
 			strbuf_swap(&buf, &unquoted);
 		}
-		check_attr(prefix, check, tree_oid, collect_all, buf.buf);
+		check_attr(prefix, check, collect_all, buf.buf);
 		maybe_flush_or_die(stdout, "attribute to stdout");
 	}
 	strbuf_release(&buf);
@@ -106,7 +106,6 @@ static NORETURN void error_with_usage(const char *msg)
 int cmd_check_attr(int argc, const char **argv, const char *prefix)
 {
 	struct attr_check *check;
-	struct object_id *tree_oid = NULL;
 	struct object_id initialized_oid;
 	int cnt, i, doubledash, filei;
 
@@ -182,14 +181,14 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
 	if (source) {
 		if (repo_get_oid_tree(the_repository, source, &initialized_oid))
 			die("%s: not a valid tree-ish source", source);
-		tree_oid = &initialized_oid;
+		set_git_attr_source(source);
 	}
 
 	if (stdin_paths)
-		check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
+		check_attr_stdin_paths(prefix, check, all_attrs);
 	else {
 		for (i = filei; i < argc; i++)
-			check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
+			check_attr(prefix, check, all_attrs, argv[i]);
 		maybe_flush_or_die(stdout, "attribute to stdout");
 	}
 
diff --git c/builtin/pack-objects.c w/builtin/pack-objects.c
index 74a167a180..d561541b8c 100644
--- c/builtin/pack-objects.c
+++ w/builtin/pack-objects.c
@@ -1320,7 +1320,7 @@ static int no_try_delta(const char *path)
 
 	if (!check)
 		check = attr_check_initl("delta", NULL);
-	git_check_attr(the_repository->index, NULL, path, check);
+	git_check_attr(the_repository->index, path, check);
 	if (ATTR_FALSE(check->items[0].value))
 		return 1;
 	return 0;
diff --git c/convert.c w/convert.c
index a54d1690c0..9b67649032 100644
--- c/convert.c
+++ w/convert.c
@@ -1308,7 +1308,7 @@ void convert_attrs(struct index_state *istate,
 		git_config(read_convert_config, NULL);
 	}
 
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	ccheck = check->items;
 	ca->crlf_action = git_path_check_crlf(ccheck + 4);
 	if (ca->crlf_action == CRLF_UNDEFINED)
diff --git c/git.c w/git.c
index 6171fd6769..21bddc5718 100644
--- c/git.c
+++ w/git.c
@@ -4,6 +4,7 @@
 #include "help.h"
 #include "run-command.h"
 #include "alias.h"
+#include "attr.h"
 #include "shallow.h"
 
 #define RUN_SETUP		(1<<0)
@@ -307,6 +308,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 			} else {
 				exit(list_cmds(cmd));
 			}
+		} else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
+			set_git_attr_source(cmd);
 		} else {
 			fprintf(stderr, _("unknown option: %s\n"), cmd);
 			usage(git_usage_string);
diff --git c/ll-merge.c w/ll-merge.c
index 130d26501c..22a603e8af 100644
--- c/ll-merge.c
+++ w/ll-merge.c
@@ -391,7 +391,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
 		normalize_file(theirs, path, istate);
 	}
 
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	ll_driver_name = check->items[0].value;
 	if (check->items[1].value) {
 		marker_size = atoi(check->items[1].value);
@@ -419,7 +419,7 @@ int ll_merge_marker_size(struct index_state *istate, const char *path)
 
 	if (!check)
 		check = attr_check_initl("conflict-marker-size", NULL);
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	if (check->items[0].value) {
 		marker_size = atoi(check->items[0].value);
 		if (marker_size <= 0)
diff --git c/pathspec.c w/pathspec.c
index ab70fcbe61..74e02c75fc 100644
--- c/pathspec.c
+++ w/pathspec.c
@@ -730,7 +730,7 @@ int match_pathspec_attrs(struct index_state *istate,
 	if (name[namelen])
 		name = to_free = xmemdupz(name, namelen);
 
-	git_check_attr(istate, NULL, name, item->attr_check);
+	git_check_attr(istate, name, item->attr_check);
 
 	free(to_free);
 
diff --git c/t/lib-diff-alternative.sh w/t/lib-diff-alternative.sh
index a8f5d3274a..02381eb7f1 100644
--- c/t/lib-diff-alternative.sh
+++ w/t/lib-diff-alternative.sh
@@ -112,15 +112,36 @@ 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 "diff from attributes with bare repo when branch different than HEAD" '
+		git -C bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
+			-c diff.driverA.algorithm=$STRATEGY \
+			diff 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 --attr-source=invalid-branch diff \
+			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 c/t/t0003-attributes.sh w/t/t0003-attributes.sh
index 89b306cb11..73db37a7f3 100755
--- c/t/t0003-attributes.sh
+++ w/t/t0003-attributes.sh
@@ -30,8 +30,13 @@ attr_check_quote () {
 attr_check_source () {
 	path="$1" expect="$2" source="$3" git_opts="$4" &&
 
-	git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
 	echo "$path: test: $expect" >expect &&
+
+	git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
+	test_cmp expect actual &&
+	test_must_be_empty err &&
+
+	git $git_opts --attr-source="$source" check-attr test -- "$path" >actual 2>err &&
 	test_cmp expect actual &&
 	test_must_be_empty err
 }
diff --git c/t/t4018-diff-funcname.sh w/t/t4018-diff-funcname.sh
index 42a2b9a13b..c8d555771d 100755
--- c/t/t4018-diff-funcname.sh
+++ w/t/t4018-diff-funcname.sh
@@ -63,6 +63,25 @@ 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 &&
+		git add . &&
+		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 --attr-source=branchA \
+			diff --exit-code 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' '
diff --git c/userdiff.c w/userdiff.c
index 58a3d59ef8..156660eaca 100644
--- c/userdiff.c
+++ w/userdiff.c
@@ -415,7 +415,7 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 		check = attr_check_initl("diff", NULL);
 	if (!path)
 		return NULL;
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 
 	if (ATTR_TRUE(check->items[0].value))
 		return &driver_true;
diff --git c/ws.c w/ws.c
index da3d0e28cb..903bfcd53e 100644
--- c/ws.c
+++ w/ws.c
@@ -79,7 +79,7 @@ unsigned whitespace_rule(struct index_state *istate, const char *pathname)
 	if (!attr_whitespace_rule)
 		attr_whitespace_rule = attr_check_initl("whitespace", NULL);
 
-	git_check_attr(istate, NULL, pathname, attr_whitespace_rule);
+	git_check_attr(istate, pathname, attr_whitespace_rule);
 	value = attr_whitespace_rule->items[0].value;
 	if (ATTR_TRUE(value)) {
 		/* true (whitespace) */

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] diff: use HEAD for attributes when using bare repository
  2023-03-16 22:56             ` Junio C Hamano
@ 2023-03-17 14:11               ` John Cai
  0 siblings, 0 replies; 15+ messages in thread
From: John Cai @ 2023-03-17 14:11 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: John Cai via GitGitGadget, git

Hi Junio,

On 16 Mar 2023, at 18:56, Junio C Hamano wrote:

> John Cai <johncai86@gmail.com> writes:
>
>> Ah I see--in that case, what would be a good object to put this state into? Mabe
>> repo_settings?
>
> I personally think a global is good enough for a case like this.  It
> is not like the code in the attribute subsystem is structured to
> work in multiple repositories at the same time in the first place, so
> in repo_settings, the_repository, or elsewhere is just as good as
> having it in a plain old file-scope static, and even after moving it
> into the_repository or something, the access will initially be done
> by referring to the_repository global (which may not even exist by
> the time "git --attr-source=<tree>" gets parsed), and not by plumbing
> a "struct repository *" parameter through the callchain.

Makes sense.

>
> Another thing to note is that there needs a mechanism to convey the
> tree object used to read the attributes from down to subprocesses,
> e.g. exporting the GIT_ATTR_SOURCE environment variable and make Git
> behave as if it saw the "git --attr-source=<tree>" option when such
> an environment variable exists, or something.  Otherwise, the custom
> attribute source would only take effect inside built-in commands.
>
> In any case, here is what I have now, stealing some tests from your
> patch.  I do not think I'll be working on it further at least for
> now, so feel free to run with it.  I am not adding the "in a bare
> repository, always read from HEAD unless told otherwise", as I do
> not see a good way to countermand it from a command line.

Thanks for the patch! Looks like you took it most of the way. I'll take a look
and make some additions if necessary.

thanks
John

>
> ----- >8 ----- cut here ----- >8 ----- cut here ----- >8 -----
> Subject: [PATCH] attr: teach "--attr-source=<tree>" global option to "git"
>
> Earlier, 47cfc9bd (attr: add flag `--source` to work with tree-ish,
> 2023-01-14) taught "git check-attr" the "--source=<tree>" option to
> allow it to read attribute files from a tree-ish, but did so only
> for the command.  Just like "check-attr" users wanted a way to use
> attributes from a tree-ish and not from the working tree files,
> users of other commands (like "git diff") would benefit from the
> same.
>
> Undo most of the UI change the commit made, while keeping the
> internal logic to read attributes from a given tree-ish.  Expose the
> internal logic via a new "--attr-source=<tree>" command line option
> given to "git", so that it can be used with any git command that
> runs internally.
>
> The tests were stolen and adjusted from John Cai's effort where only
> "git diff" learned the "--attr-source" option to read from a
> tree-ish.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  archive.c                 |  2 +-
>  attr.c                    | 34 ++++++++++++++++++++++++++++++++--
>  attr.h                    | 13 +++++++++----
>  builtin/check-attr.c      | 17 ++++++++---------
>  builtin/pack-objects.c    |  2 +-
>  convert.c                 |  2 +-
>  git.c                     |  3 +++
>  ll-merge.c                |  4 ++--
>  pathspec.c                |  2 +-
>  t/lib-diff-alternative.sh | 31 ++++++++++++++++++++++++++-----
>  t/t0003-attributes.sh     |  7 ++++++-
>  t/t4018-diff-funcname.sh  | 19 +++++++++++++++++++
>  userdiff.c                |  2 +-
>  ws.c                      |  2 +-
>  14 files changed, 111 insertions(+), 29 deletions(-)
>
> diff --git c/archive.c w/archive.c
> index 9aeaf2bd87..385aa5f248 100644
> --- c/archive.c
> +++ w/archive.c
> @@ -120,7 +120,7 @@ static const struct attr_check *get_archive_attrs(struct index_state *istate,
>  	static struct attr_check *check;
>  	if (!check)
>  		check = attr_check_initl("export-ignore", "export-subst", NULL);
> -	git_check_attr(istate, NULL, path, check);
> +	git_check_attr(istate, path, check);
>  	return check;
>  }
>
> diff --git c/attr.c w/attr.c
> index 1053dfcd4b..d34c7e9d54 100644
> --- c/attr.c
> +++ w/attr.c
> @@ -1165,11 +1165,40 @@ static void collect_some_attrs(struct index_state *istate,
>  	fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
>  }
>
> +static const char *default_attr_source_tree_object_name;
> +
> +void set_git_attr_source(const char *tree_object_name)
> +{
> +	default_attr_source_tree_object_name = xstrdup(tree_object_name);
> +}
> +
> +
> +static void compute_default_attr_source(struct object_id *attr_source)
> +{
> +	if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
> +		return;
> +
> +	if (get_oid_treeish(default_attr_source_tree_object_name, attr_source))
> +		die(_("bad --attr-source object"));
> +}
> +
> +static struct object_id *default_attr_source(void)
> +{
> +	static struct object_id attr_source;
> +
> +	if (is_null_oid(&attr_source))
> +		compute_default_attr_source(&attr_source);
> +	if (is_null_oid(&attr_source))
> +		return NULL;
> +	return &attr_source;
> +}
> +
>  void git_check_attr(struct index_state *istate,
> -		    const struct object_id *tree_oid, const char *path,
> +		    const char *path,
>  		    struct attr_check *check)
>  {
>  	int i;
> +	const struct object_id *tree_oid = default_attr_source();
>
>  	collect_some_attrs(istate, tree_oid, path, check);
>
> @@ -1182,10 +1211,11 @@ void git_check_attr(struct index_state *istate,
>  	}
>  }
>
> -void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
> +void git_all_attrs(struct index_state *istate,
>  		   const char *path, struct attr_check *check)
>  {
>  	int i;
> +	const struct object_id *tree_oid = default_attr_source();
>
>  	attr_check_reset(check);
>  	collect_some_attrs(istate, tree_oid, path, check);
> diff --git c/attr.h w/attr.h
> index 9884ea2bc6..676bd17ce2 100644
> --- c/attr.h
> +++ w/attr.h
> @@ -45,7 +45,7 @@
>   * const char *path;
>   *
>   * setup_check();
> - * git_check_attr(&the_index, tree_oid, path, check);
> + * git_check_attr(&the_index, path, check);
>   * ------------
>   *
>   * - Act on `.value` member of the result, left in `check->items[]`:
> @@ -120,7 +120,6 @@
>  #define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
>
>  struct index_state;
> -struct object_id;
>
>  /**
>   * An attribute is an opaque object that is identified by its name. Pass the
> @@ -135,6 +134,12 @@ struct git_attr;
>  struct all_attrs_item;
>  struct attr_stack;
>
> +/*
> + * The textual object name for the tree-ish used by git_check_attr()
> + * to read attributes from (instead of from the working tree).
> + */
> +void set_git_attr_source(const char *);
> +
>  /*
>   * Given a string, return the gitattribute object that
>   * corresponds to it.
> @@ -203,14 +208,14 @@ void attr_check_free(struct attr_check *check);
>  const char *git_attr_name(const struct git_attr *);
>
>  void git_check_attr(struct index_state *istate,
> -		    const struct object_id *tree_oid, const char *path,
> +		    const char *path,
>  		    struct attr_check *check);
>
>  /*
>   * Retrieve all attributes that apply to the specified path.
>   * check holds the attributes and their values.
>   */
> -void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
> +void git_all_attrs(struct index_state *istate,
>  		   const char *path, struct attr_check *check);
>
>  enum git_attr_direction {
> diff --git c/builtin/check-attr.c w/builtin/check-attr.c
> index d7a40e674c..1a7929c980 100644
> --- c/builtin/check-attr.c
> +++ w/builtin/check-attr.c
> @@ -58,7 +58,7 @@ static void output_attr(struct attr_check *check, const char *file)
>  }
>
>  static void check_attr(const char *prefix, struct attr_check *check,
> -		       const struct object_id *tree_oid, int collect_all,
> +		       int collect_all,
>  		       const char *file)
>
>  {
> @@ -66,9 +66,9 @@ static void check_attr(const char *prefix, struct attr_check *check,
>  		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
>
>  	if (collect_all) {
> -		git_all_attrs(&the_index, tree_oid, full_path, check);
> +		git_all_attrs(&the_index, full_path, check);
>  	} else {
> -		git_check_attr(&the_index, tree_oid, full_path, check);
> +		git_check_attr(&the_index, full_path, check);
>  	}
>  	output_attr(check, file);
>
> @@ -76,7 +76,7 @@ static void check_attr(const char *prefix, struct attr_check *check,
>  }
>
>  static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
> -				   const struct object_id *tree_oid, int collect_all)
> +				   int collect_all)
>  {
>  	struct strbuf buf = STRBUF_INIT;
>  	struct strbuf unquoted = STRBUF_INIT;
> @@ -90,7 +90,7 @@ static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
>  				die("line is badly quoted");
>  			strbuf_swap(&buf, &unquoted);
>  		}
> -		check_attr(prefix, check, tree_oid, collect_all, buf.buf);
> +		check_attr(prefix, check, collect_all, buf.buf);
>  		maybe_flush_or_die(stdout, "attribute to stdout");
>  	}
>  	strbuf_release(&buf);
> @@ -106,7 +106,6 @@ static NORETURN void error_with_usage(const char *msg)
>  int cmd_check_attr(int argc, const char **argv, const char *prefix)
>  {
>  	struct attr_check *check;
> -	struct object_id *tree_oid = NULL;
>  	struct object_id initialized_oid;
>  	int cnt, i, doubledash, filei;
>
> @@ -182,14 +181,14 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
>  	if (source) {
>  		if (repo_get_oid_tree(the_repository, source, &initialized_oid))
>  			die("%s: not a valid tree-ish source", source);
> -		tree_oid = &initialized_oid;
> +		set_git_attr_source(source);
>  	}
>
>  	if (stdin_paths)
> -		check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
> +		check_attr_stdin_paths(prefix, check, all_attrs);
>  	else {
>  		for (i = filei; i < argc; i++)
> -			check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
> +			check_attr(prefix, check, all_attrs, argv[i]);
>  		maybe_flush_or_die(stdout, "attribute to stdout");
>  	}
>
> diff --git c/builtin/pack-objects.c w/builtin/pack-objects.c
> index 74a167a180..d561541b8c 100644
> --- c/builtin/pack-objects.c
> +++ w/builtin/pack-objects.c
> @@ -1320,7 +1320,7 @@ static int no_try_delta(const char *path)
>
>  	if (!check)
>  		check = attr_check_initl("delta", NULL);
> -	git_check_attr(the_repository->index, NULL, path, check);
> +	git_check_attr(the_repository->index, path, check);
>  	if (ATTR_FALSE(check->items[0].value))
>  		return 1;
>  	return 0;
> diff --git c/convert.c w/convert.c
> index a54d1690c0..9b67649032 100644
> --- c/convert.c
> +++ w/convert.c
> @@ -1308,7 +1308,7 @@ void convert_attrs(struct index_state *istate,
>  		git_config(read_convert_config, NULL);
>  	}
>
> -	git_check_attr(istate, NULL, path, check);
> +	git_check_attr(istate, path, check);
>  	ccheck = check->items;
>  	ca->crlf_action = git_path_check_crlf(ccheck + 4);
>  	if (ca->crlf_action == CRLF_UNDEFINED)
> diff --git c/git.c w/git.c
> index 6171fd6769..21bddc5718 100644
> --- c/git.c
> +++ w/git.c
> @@ -4,6 +4,7 @@
>  #include "help.h"
>  #include "run-command.h"
>  #include "alias.h"
> +#include "attr.h"
>  #include "shallow.h"
>
>  #define RUN_SETUP		(1<<0)
> @@ -307,6 +308,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
>  			} else {
>  				exit(list_cmds(cmd));
>  			}
> +		} else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
> +			set_git_attr_source(cmd);
>  		} else {
>  			fprintf(stderr, _("unknown option: %s\n"), cmd);
>  			usage(git_usage_string);
> diff --git c/ll-merge.c w/ll-merge.c
> index 130d26501c..22a603e8af 100644
> --- c/ll-merge.c
> +++ w/ll-merge.c
> @@ -391,7 +391,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
>  		normalize_file(theirs, path, istate);
>  	}
>
> -	git_check_attr(istate, NULL, path, check);
> +	git_check_attr(istate, path, check);
>  	ll_driver_name = check->items[0].value;
>  	if (check->items[1].value) {
>  		marker_size = atoi(check->items[1].value);
> @@ -419,7 +419,7 @@ int ll_merge_marker_size(struct index_state *istate, const char *path)
>
>  	if (!check)
>  		check = attr_check_initl("conflict-marker-size", NULL);
> -	git_check_attr(istate, NULL, path, check);
> +	git_check_attr(istate, path, check);
>  	if (check->items[0].value) {
>  		marker_size = atoi(check->items[0].value);
>  		if (marker_size <= 0)
> diff --git c/pathspec.c w/pathspec.c
> index ab70fcbe61..74e02c75fc 100644
> --- c/pathspec.c
> +++ w/pathspec.c
> @@ -730,7 +730,7 @@ int match_pathspec_attrs(struct index_state *istate,
>  	if (name[namelen])
>  		name = to_free = xmemdupz(name, namelen);
>
> -	git_check_attr(istate, NULL, name, item->attr_check);
> +	git_check_attr(istate, name, item->attr_check);
>
>  	free(to_free);
>
> diff --git c/t/lib-diff-alternative.sh w/t/lib-diff-alternative.sh
> index a8f5d3274a..02381eb7f1 100644
> --- c/t/lib-diff-alternative.sh
> +++ w/t/lib-diff-alternative.sh
> @@ -112,15 +112,36 @@ 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 "diff from attributes with bare repo when branch different than HEAD" '
> +		git -C bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
> +			-c diff.driverA.algorithm=$STRATEGY \
> +			diff 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 --attr-source=invalid-branch diff \
> +			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 c/t/t0003-attributes.sh w/t/t0003-attributes.sh
> index 89b306cb11..73db37a7f3 100755
> --- c/t/t0003-attributes.sh
> +++ w/t/t0003-attributes.sh
> @@ -30,8 +30,13 @@ attr_check_quote () {
>  attr_check_source () {
>  	path="$1" expect="$2" source="$3" git_opts="$4" &&
>
> -	git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
>  	echo "$path: test: $expect" >expect &&
> +
> +	git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
> +	test_cmp expect actual &&
> +	test_must_be_empty err &&
> +
> +	git $git_opts --attr-source="$source" check-attr test -- "$path" >actual 2>err &&
>  	test_cmp expect actual &&
>  	test_must_be_empty err
>  }
> diff --git c/t/t4018-diff-funcname.sh w/t/t4018-diff-funcname.sh
> index 42a2b9a13b..c8d555771d 100755
> --- c/t/t4018-diff-funcname.sh
> +++ w/t/t4018-diff-funcname.sh
> @@ -63,6 +63,25 @@ 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 &&
> +		git add . &&
> +		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 --attr-source=branchA \
> +			diff --exit-code 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' '
> diff --git c/userdiff.c w/userdiff.c
> index 58a3d59ef8..156660eaca 100644
> --- c/userdiff.c
> +++ w/userdiff.c
> @@ -415,7 +415,7 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
>  		check = attr_check_initl("diff", NULL);
>  	if (!path)
>  		return NULL;
> -	git_check_attr(istate, NULL, path, check);
> +	git_check_attr(istate, path, check);
>
>  	if (ATTR_TRUE(check->items[0].value))
>  		return &driver_true;
> diff --git c/ws.c w/ws.c
> index da3d0e28cb..903bfcd53e 100644
> --- c/ws.c
> +++ w/ws.c
> @@ -79,7 +79,7 @@ unsigned whitespace_rule(struct index_state *istate, const char *pathname)
>  	if (!attr_whitespace_rule)
>  		attr_whitespace_rule = attr_check_initl("whitespace", NULL);
>
> -	git_check_attr(istate, NULL, pathname, attr_whitespace_rule);
> +	git_check_attr(istate, pathname, attr_whitespace_rule);
>  	value = attr_whitespace_rule->items[0].value;
>  	if (ATTR_TRUE(value)) {
>  		/* true (whitespace) */

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-03-17 14:11 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-14  1:53 [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm John Cai via GitGitGadget
2023-03-14  1:53 ` [PATCH 1/2] diff: use HEAD for attributes when using bare repository John Cai via GitGitGadget
2023-03-14 16:54   ` Junio C Hamano
2023-03-14 17:43     ` Junio C Hamano
2023-03-14 19:38       ` John Cai
2023-03-14 20:37         ` Junio C Hamano
2023-03-16 16:46           ` John Cai
2023-03-16 22:56             ` Junio C Hamano
2023-03-17 14:11               ` John Cai
2023-03-14  1:53 ` [PATCH 2/2] diff: add --attr-source to read gitattributes from a commit John Cai via GitGitGadget
2023-03-14 17:21 ` [PATCH 0/2] diff: support bare repositories when reading gitattributes for diff algorithm Philippe Blain
2023-03-14 19:18   ` John Cai
2023-03-14 20:44     ` Junio C Hamano
2023-03-16 14:29       ` Jeff King
2023-03-16 16:44         ` Junio C Hamano

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).