From: "Torsten Bögershausen" <tboegi@web.de>
To: Joanna Wang <jojwang@google.com>
Cc: git@vger.kernel.org
Subject: Re: [PATCH 1/1] attr: add native file mode values support
Date: Mon, 13 Nov 2023 17:50:31 +0100 [thread overview]
Message-ID: <20231113165031.GA28778@tb-raspi4> (raw)
In-Reply-To: <20231111040309.2848560-1-jojwang@google.com>
On Sat, Nov 11, 2023 at 04:03:08AM +0000, Joanna Wang wrote:
Some thoughts and comments inline...
> Gives all paths inherent 'mode' attribute values based on the paths'
> modes (one of 100644, 100755, 120000, 040000, 160000). Users may use
> this feature to filter by file types. For example a pathspec such as
> ':(attr:mode=160000)' could filter for submodules without needing
My spontanous feeling is that filetype may be another choice:
> ':(attr:filetype=160000)' could filter for submodules without needing
And having written this, we can think using something borrowed from
`find . -type f`
:(attr:filetype=f)' or :(attr:filetype=x)' (for executable)
> `mode=160000` to actually be specified for each subdmoule path in
> .gitattributes. The native mode values are also reflected in
> `git check-attr` results.
But then I missed the point why we need an attribute here?
The mode is already defined by the the file system (and Git),
is there a special reason that the user can define or re-define the
value here ?
May be there is, when working with pathspec.
But then "pathspec=" could be a better construction.
Since "mode" could make a reader think that Git does somewhat with the file
when checking out.
My personal hope reading "mode=100755" in .gitattributes would
be that Git makes it executable when checking out, if if it is
recorded in Git as 100644, probably coming from a file-system that
doesn't support the executable bit in a Unix way.
> If there is any existing mode attribute for a path (e.g. there is
> !mode, -mode, mode, mode=<value> in .gitattributes) that setting will
> take precedence over the native mode value.
>
> ---
>
> I went with 'mode' because that is the word used in documentation
> (e.g. https://git-scm.com/book/sv/v2/Git-Internals-Git-Objects)
> and in the code (e.g. `ce_mode`). Please let me know what you think
> of this UX.
>
> The implementation happens within git_check_attr() method which is
> the common mathod called for getting a pathspec attribute value.
>
> The previous discussed idea did not work with `git check-attr`.
> (https://lore.kernel.org/all/CAMmZTi8swsSMcLUcW+YwUDg8GcrY_ks2+i35-nsHE3o9MNpsUQ@mail.gmail.com/).
>
> There are no tests for excluding based on pathspec attrs in subdirectories
> due to an existing bug. Since it is not specific to native mode, I thought
> it would be better to fix separately.
> https://lore.kernel.org/all/CAMmZTi89Un+bsLXdEdYs44oT8eLNp8y=Pm8ywaurcQ7ccRKGdQ@mail.gmail.com/
Reading "pathspec attrs" above it feels better to call the new attribute pathspec.
But why should a user be allowed to overwrite what we have in the index ?
That is somewhat missing in the motivation for this change,
it would be good to have this explained in the commit message.
>
> Documentation/gitattributes.txt | 10 +++++++
> attr.c | 42 ++++++++++++++++++++++++--
> t/t0003-attributes.sh | 40 +++++++++++++++++++++++++
> t/t6135-pathspec-with-attrs.sh | 53 +++++++++++++++++++++++++++++++++
> 4 files changed, 143 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
> index 8c1793c148..bb3c11f151 100644
> --- a/Documentation/gitattributes.txt
> +++ b/Documentation/gitattributes.txt
> @@ -156,6 +156,16 @@ Unspecified::
> Any other value causes Git to act as if `text` has been left
> unspecified.
>
> +`mode`
> +^^^^^
> +
> +This attribute is for filtering files by their file bit modes
> +(40000, 120000, 160000, 100755, 100644) and has native support in git,
> +meaning values for this attribute are natively set (e.g. mode=100644) by
> +git without having to specify them in .gitattributes. However, if
> +'mode' is set in .gitattributest for some path, that value takes precendence,
> +whether it is 'set', 'unset', 'unspecified', or some other value.
> +
> `eol`
> ^^^^^
>
> diff --git a/attr.c b/attr.c
> index e62876dfd3..95dc1cf695 100644
> --- a/attr.c
> +++ b/attr.c
> @@ -1240,20 +1240,58 @@ static struct object_id *default_attr_source(void)
> return &attr_source;
> }
>
> +
> +/*
> + * This implements native file mode attr support.
> + * We always return the current mode of the path, not the mode stored
> + * in the index, unless the path is a directory where we check the index
> + * to see if it is a GITLINK. It is ok to rely on the index for GITLINK
> + * modes because a path cannot become a GITLINK (which is a git concept only)
> + * without having it indexed with a GITLINK mode in git.
> + */
> +static unsigned int native_mode_attr(struct index_state *istate, const char *path)
> +{
> + struct stat st;
> + unsigned int mode;
> + if (lstat(path, &st))
> + die_errno(_("unable to stat '%s'"), path);
> + mode = canon_mode(st.st_mode);
> + if (S_ISDIR(mode)) {
> + int pos = index_name_pos(istate, path, strlen(path));
> + if (pos >= 0)
> + if (S_ISGITLINK(istate->cache[pos]->ce_mode))
> + return istate->cache[pos]->ce_mode;
> + }
> + return mode;
> +}
> +
> +
> void git_check_attr(struct index_state *istate,
> const char *path,
> struct attr_check *check)
> {
> int i;
> const struct object_id *tree_oid = default_attr_source();
> + struct strbuf sb = STRBUF_INIT;
>
> collect_some_attrs(istate, tree_oid, path, check);
>
> for (i = 0; i < check->nr; i++) {
> unsigned int n = check->items[i].attr->attr_nr;
> const char *value = check->all_attrs[n].value;
> - if (value == ATTR__UNKNOWN)
> - value = ATTR__UNSET;
> + if (value == ATTR__UNKNOWN){
> + if (strcmp(check->all_attrs[n].attr->name, "mode") == 0) {
> + /*
> + * If value is ATTR_UNKNOWN then it has not been set
> + * anywhere with -mode (ATTR_FALSE), !mode (ATTR_UNSET),
> + * mode (ATTR_TRUE), or an explicit value. We fill
> + * value with the native mode value.
> + */
> + strbuf_addf(&sb, "%06o", native_mode_attr(istate, path));
> + value = sb.buf;
> + } else
> + value = ATTR__UNSET;
> + }
> check->items[i].value = value;
> }
> }
> diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
> index aee2298f01..9c2603d8e2 100755
> --- a/t/t0003-attributes.sh
> +++ b/t/t0003-attributes.sh
> @@ -19,6 +19,15 @@ attr_check () {
> test_must_be_empty err
> }
>
> +attr_check_mode () {
> + path="$1" expect="$2" git_opts="$3" &&
It would be easier to read, if each assignment is on it's on line
> +
> + git $git_opts check-attr mode -- "$path" >actual 2>err &&
> + echo "$path: mode: $expect" >expect &&
> + test_cmp expect actual &&
> + test_must_be_empty err
> +}
> +
> attr_check_quote () {
> path="$1" quoted_path="$2" expect="$3" &&
>
> @@ -558,4 +567,35 @@ test_expect_success EXPENSIVE 'large attributes file ignored in index' '
> test_cmp expect err
> '
>
> +test_expect_success 'submodule with .git file' '
> + mkdir sub &&
> + (cd sub &&
> + git init &&
> + mv .git .real &&
> + echo "gitdir: .real" >.git &&
> + test_commit first) &&
> + git update-index --add -- sub
Style and indentation (Please use TAB for indenting)
Using sub-shells is good. Somewhat easier to read would be this:
mkdir sub &&
(
cd sub &&
git init &&
mv .git .real &&
echo "gitdir: .real" >.git &&
test_commit first
) &&
> +'
> +
> +test_expect_success 'native mode attributes work' '
> + >exec && chmod +x exec && attr_check_mode exec 100755 &&
> + >normal && attr_check_mode normal 100644 &&
> + mkdir dir && attr_check_mode dir 040000 &&
> + ln -s normal normal_sym && attr_check_mode normal_sym 120000 &&
> + attr_check_mode sub 160000
> +'
We need a precondition here:
test_expect_success SYMLINKS
> +
> +test_expect_success '.gitattributes mode values take precedence' '
> + (
> + echo "mode_value* mode=myownmode" &&
> + echo "mode_set* mode" &&
> + echo "mode_unset* -mode" &&
> + echo "mode_unspecified* !mode"
> + ) >.gitattributes &&
Can this be written using a
cat <<-\EOF >expect &&
EOF
expression ?
> + >mode_value && attr_check_mode mode_value myownmode &&
> + >mode_unset && attr_check_mode mode_unset unset &&
> + >mode_unspecified && attr_check_mode mode_unspecified unspecified &&
> + >mode_set && attr_check_mode mode_set set
> +'
> +
> test_done
> diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
> index a9c1e4e0ec..fd9569d39b 100755
> --- a/t/t6135-pathspec-with-attrs.sh
> +++ b/t/t6135-pathspec-with-attrs.sh
> @@ -64,6 +64,9 @@ test_expect_success 'setup .gitattributes' '
> fileSetLabel label
> fileValue label=foo
> fileWrongLabel label☺
> + mode_set* mode=1234
> + mode_unset* -mode
> + mode_unspecified* !mode
> EOF
> echo fileSetLabel label1 >sub/.gitattributes &&
> git add .gitattributes sub/.gitattributes &&
> @@ -295,4 +298,54 @@ test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
> test_cmp expect actual
> '
>
> +test_expect_success 'mode attr is handled correctly for overriden values' '
> + >mode_set_1 &&
> + >mode_unset_1 &&
> + >mode_unspecified_1 &&
> + >mode_regular_file_1 &&
> +
> + git status -s ":(attr:mode=1234)mode*" >actual &&
> + cat <<-\EOF >expect &&
> + ?? mode_set_1
> + EOF
> + test_cmp expect actual &&
> +
> + git status -s ":(attr:-mode)mode*" >actual &&
> + echo ?? mode_unset_1 >expect &&
> + test_cmp expect actual &&
> +
> + git status -s ":(attr:!mode)mode*" >actual &&
> + echo ?? mode_unspecified_1 >expect &&
> + test_cmp expect actual &&
> +
> + git status -s ":(attr:mode=100644)mode*" >actual &&
> + echo ?? mode_regular_file_1 >expect &&
> + test_cmp expect actual
> +'
> +
> +test_expect_success 'mode attr values are current file modes, not indexed modes' '
> + >mode_exec_file_1 &&
> +
> + git status -s ":(attr:mode=100644)mode_exec_*" >actual &&
> + echo ?? mode_exec_file_1 >expect &&
> + test_cmp expect actual &&
> +
> + git add mode_exec_file_1 && chmod +x mode_exec_file_1 &&
> + git status -s ":(attr:mode=100755)mode_exec_*" >actual &&
> + echo AM mode_exec_file_1 >expect &&
> + test_cmp expect actual
> +'
> +
> +test_expect_success 'mode attr can be excluded' '
> + >mode_1_regular &&
> + >mode_1_exec && chmod +x mode_1_exec &&
> + git status -s ":(exclude,attr:mode=100644)" "mode_1_*" >actual &&
> + echo ?? mode_1_exec >expect &&
> + test_cmp expect actual &&
> +
> + git status -s ":(exclude,attr:mode=100755)" "mode_1_*" >actual &&
> + echo ?? mode_1_regular >expect &&
> + test_cmp expect actual
> +'
> +
> test_done
> --
> 2.42.0.869.gea05f2083d-goog
>
>
next prev parent reply other threads:[~2023-11-13 16:50 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-11 4:03 [PATCH 1/1] attr: add native file mode values support Joanna Wang
2023-11-11 4:48 ` Junio C Hamano
2023-11-13 16:50 ` Torsten Bögershausen [this message]
2023-11-13 22:22 ` Junio C Hamano
-- strict thread matches above, loose matches on Subject: below --
2023-11-14 2:28 Joanna Wang
2023-11-14 2:52 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231113165031.GA28778@tb-raspi4 \
--to=tboegi@web.de \
--cc=git@vger.kernel.org \
--cc=jojwang@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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 NNTP newsgroup(s).