Subject: [PATCH v4 0/2] check-attr: add support to work with revisions

From: Karthik Nayak <[email-masked]>
Date: Wed, 21 Dec 2022 14:46:52 +0100

v1: https://lore.kernel.org/git/[email-masked]/
v2: https://lore.kernel.org/git/CAOLa=ZSsFGBw3ta1jWN8cmUch2ca=[email-masked]/T/#t
v3: https://lore.kernel.org/git/[email-masked]/

Given a pathname, git-check-attr(1) will list the attributes which apply to that
pathname by reading all relevant gitattributes files. Currently there is no way
to specify a revision to read the gitattributes from.

This is specifically useful in bare repositories wherein the gitattributes are
only present in the git working tree but not available directly on the
filesystem.

This series aims to add a new flag `--revision` to git-check-attr(1) which
allows us to read gitattributes from the specified revision.

Changes since version 3:
- Dropped the `-r` shorcut for the flag
- Fixed tests failing by initializing the tree_oid to NULL
- Incorporated some changes around the commit message

Range-diff against v3:

1:  6224754179 = 1:  6224754179 t0003: move setup for `--all` into new block
2:  d8f8b3c36c ! 2:  a161dbdf8b attr: add flag `-r|--revisions` to work with revisions
    @@ Metadata
     Author: Karthik Nayak <[email-masked]>
     
      ## Commit message ##
    -    attr: add flag `-r|--revisions` to work with revisions
    +    attr: add flag `--revision` to work with revisions
     
         The contents of the .gitattributes files may evolve over time, but "git
         check-attr" always checks attributes against them in the working tree
         and/or in the index. It may be beneficial to optionally allow the users
    -    to check attributes against paths from older commits.
    +    to check attributes taken from a commit other than HEAD against paths.
     
    -    Add a new flag `--revision`/`-r` which will allow users to check the
    -    attributes against a tree-ish revision. When the user uses this flag, we
    -    go through the stack of .gitattributes files but instead of checking the
    -    current working tree and/or in the index, we check the blobs from the
    -    provided tree-ish object. This allows the command to also be used in
    -    bare repositories.
    +    Add a new flag `--revision` which will allow users to check the
    +    attributes against a commit (actually any tree-ish would do). When the
    +    user uses this flag, we go through the stack of .gitattributes files but
    +    instead of checking the current working tree and/or in the index, we
    +    check the blobs from the provided tree-ish object. This allows the
    +    command to also be used in bare repositories.
     
    -    Since we use a tree-ish object, the user can pass "-r HEAD:subdirectory"
    -    and all the attributes will be looked up as if subdirectory was the root
    -    directory of the repository.
    +    Since we use a tree-ish object, the user can pass "--revision
    +    HEAD:subdirectory" and all the attributes will be looked up as if
    +    subdirectory was the root directory of the repository.
     
    -    We cannot use the `<rev>:<path>` syntax like the one used in `git show`
    -    because any non-flag parameter before `--` is treated as an attribute
    -    and any parameter after `--` is treated as a pathname.
    +    We cannot simply use the `<rev>:<path>` syntax without the `--revision`
    +    flag, similar to how it is used in `git show` because any non-flag
    +    parameter before `--` is treated as an attribute and any parameter after
    +    `--` is treated as a pathname.
     
         The change involves creating a new function `read_attr_from_blob`, which
         given the path reads the blob for the path against the provided revision and
    @@ Commit message
         files.
     
         Signed-off-by: Karthik Nayak <[email-masked]>
    +    Signed-off-by: Toon Claes <[email-masked]>
         Co-authored-by: [email-masked]
     
      ## Documentation/git-check-attr.txt ##
    @@ Documentation/git-check-attr.txt: git-check-attr - Display gitattributes informa
      [verse]
     -'git check-attr' [-a | --all | <attr>...] [--] <pathname>...
     -'git check-attr' --stdin [-z] [-a | --all | <attr>...]
    -+'git check-attr' [-r <revision>] [-a | --all | <attr>...] [--] <pathname>...
    -+'git check-attr' --stdin [-z] [-r <revision>] [-a | --all | <attr>...]
    ++'git check-attr' [--revision <revision>] [-a | --all | <attr>...] [--] <pathname>...
    ++'git check-attr' --stdin [-z] [--revision <revision>] [-a | --all | <attr>...]
      
      DESCRIPTION
      -----------
    @@ Documentation/git-check-attr.txt: OPTIONS
      	If `--stdin` is also given, input paths are separated
      	with a NUL character instead of a linefeed character.
      
    -+--r <revision>::
     +--revision=<revision>::
    -+	Check attributes against the specified tree-ish revision. All the
    -+	attributes will be checked against the provided revision. Paths provided
    -+	as part of the revision will be treated as the root directory.
    ++	Check attributes against the specified commit. All the attributes will
    ++	be checked against the provided revision. Paths provided as part of the
    ++	revision will be treated as the root directory.
     +
      \--::
      	Interpret all preceding arguments as attributes and all following
    @@ attr.c
      #include "exec-cmd.h"
      #include "attr.h"
      #include "dir.h"
    -+#include "git-compat-util.h"
     +#include "strbuf.h"
     +#include "tree-walk.h"
      #include "utf8.h"
    @@ builtin/check-attr.c
      static const char * const check_attr_usage[] = {
     -N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
     -N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
    -+N_("git check-attr [-r <revision>] [-a | --all | <attr>...] [--] <pathname>..."),
    -+N_("git check-attr --stdin [-z] [-r <revision>] [-a | --all | <attr>...]"),
    ++N_("git check-attr [--revision <revision>] [-a | --all | <attr>...] [--] <pathname>..."),
    ++N_("git check-attr --stdin [-z] [--revision <revision>] [-a | --all | <attr>...]"),
      NULL
      };
      
    @@ builtin/check-attr.c: static const struct option check_attr_options[] = {
      	OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
      	OPT_BOOL('z', NULL, &nul_term_line,
      		 N_("terminate input and output records by a NUL character")),
    -+	OPT_STRING('r', "revision", &revision, N_("revision"), N_("check attributes at this revision")),
    ++	OPT_STRING(0, "revision", &revision, N_("revision"), N_("check attributes at this revision")),
      	OPT_END()
      };
      
    @@ builtin/check-attr.c: 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;
    ++	struct object_id *tree_oid = NULL;
      	int cnt, i, doubledash, filei;
      
      	if (!is_bare_repository())
    @@ builtin/check-attr.c: int cmd_check_attr(int argc, const char **argv, const char
      		}
      	}
      
    -+	if (revision)
    -+		if (repo_get_oid_tree(the_repository, revision, &tree_oid))
    ++	if (revision) {
    ++		tree_oid = xmalloc(sizeof(struct object_id));
    ++
    ++		if (repo_get_oid_tree(the_repository, revision, tree_oid))
     +			error("%s: not a valid revision", revision);
    ++	}
     +
      	if (stdin_paths)
     -		check_attr_stdin_paths(prefix, check, all_attrs);
    -+		check_attr_stdin_paths(prefix, check, &tree_oid, all_attrs);
    ++		check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
      	else {
      		for (i = filei; i < argc; i++)
     -			check_attr(prefix, check, all_attrs, argv[i]);
    -+			check_attr(prefix, check, &tree_oid, all_attrs, argv[i]);
    ++			check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
      		maybe_flush_or_die(stdout, "attribute to stdout");
      	}
      
    @@ t/t0003-attributes.sh: attr_check_quote () {
     +attr_check_revision () {
     +	path="$1" expect="$2" revision="$3" git_opts="$4" &&
      
    -+	git $git_opts check-attr -r $revision test -- "$path" >actual 2>err &&
    ++	git $git_opts check-attr --revision $revision test -- "$path" >actual 2>err &&
     +	echo "$path: test: $expect" >expect &&
     +	test_cmp expect actual
      }
    @@ t/t0003-attributes.sh: test_expect_success 'setup' '
      	test_must_fail git check-attr test &&
      	test_must_fail git check-attr test -- &&
      	test_must_fail git check-attr -- f &&
    -+	test_must_fail git check-attr -r &&
    -+	test_must_fail git check-attr -r not-a-valid-ref &&
    ++	test_must_fail git check-attr --revision &&
    ++	test_must_fail git check-attr --revision not-a-valid-ref &&
      	echo "f" | test_must_fail git check-attr --stdin &&
      	echo "f" | test_must_fail git check-attr --stdin -- f &&
      	echo "f" | test_must_fail git check-attr --stdin test -- f &&

Karthik Nayak (2):
  t0003: move setup for `--all` into new block
  attr: add flag `--revision` to work with revisions

 Documentation/git-check-attr.txt |  9 ++-
 archive.c                        |  2 +-
 attr.c                           | 99 +++++++++++++++++++++++---------
 attr.h                           |  7 ++-
 builtin/check-attr.c             | 36 +++++++-----
 builtin/pack-objects.c           |  2 +-
 convert.c                        |  2 +-
 ll-merge.c                       |  4 +-
 pathspec.c                       |  2 +-
 t/t0003-attributes.sh            | 71 ++++++++++++++++++++++-
 userdiff.c                       |  2 +-
 ws.c                             |  2 +-
 12 files changed, 183 insertions(+), 55 deletions(-)

-- 
2.39.0