Skip to content

Commit

Permalink
Merge branch 'jw/git-add-attr-pathspec' into seen
Browse files Browse the repository at this point in the history
"git add" and "git stash" learned to support the ":(attr:...)"
magic pathspec.

* jw/git-add-attr-pathspec:
  attr: enable attr pathspec magic for git-add and git-stash
  • Loading branch information
gitster committed Nov 3, 2023
2 parents 2cc19ec + 9edc89c commit 3780afb
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 18 deletions.
7 changes: 4 additions & 3 deletions builtin/add.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
*/
parse_pathspec(&pathspec, PATHSPEC_ATTR,
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
Expand All @@ -433,7 +433,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (pathspec.nr)
die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");

parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, pathspec_from_file, pathspec_file_nul);
Expand Down Expand Up @@ -504,7 +504,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
PATHSPEC_ICASE |
PATHSPEC_EXCLUDE);
PATHSPEC_EXCLUDE |
PATHSPEC_ATTR);

for (i = 0; i < pathspec.nr; i++) {
const char *path = pathspec.items[i].match;
Expand Down
3 changes: 2 additions & 1 deletion dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -2179,7 +2179,8 @@ static int exclude_matches_pathspec(const char *path, int pathlen,
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
PATHSPEC_ICASE |
PATHSPEC_EXCLUDE);
PATHSPEC_EXCLUDE |
PATHSPEC_ATTR);

for (i = 0; i < pathspec->nr; i++) {
const struct pathspec_item *item = &pathspec->items[i];
Expand Down
25 changes: 16 additions & 9 deletions pathspec.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,23 @@ static struct pathspec_magic {
{ PATHSPEC_ATTR, '\0', "attr" },
};

static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic, const char *element)
{
int i;
strbuf_addstr(sb, ":(");
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
if (magic & pathspec_magic[i].bit) {
if (sb->buf[sb->len - 1] != '(')
strbuf_addch(sb, ',');
strbuf_addstr(sb, pathspec_magic[i].name);
if (element[1] != '(') {
/* Process an element in shorthand form (e.g. ":!/<match>") */
strbuf_addstr(sb, ":(");
for (int i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
if ((magic & pathspec_magic[i].bit) && (pathspec_magic[i].mnemonic != '\0')) {
if (sb->buf[sb->len - 1] != '(')
strbuf_addch(sb, ',');
strbuf_addstr(sb, pathspec_magic[i].name);
}
}
} else {
/* For an element in longhand form, we simply copy everything up to the final ')' */
int len = strchr(element, ')') - element;
strbuf_add(sb, element, len);
}
strbuf_addf(sb, ",prefix:%d)", prefixlen);
}

Expand Down Expand Up @@ -493,7 +500,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
struct strbuf sb = STRBUF_INIT;

/* Preserve the actual prefix length of each pattern */
prefix_magic(&sb, prefixlen, element_magic);
prefix_magic(&sb, prefixlen, element_magic, elt);

strbuf_addstr(&sb, match);
item->original = strbuf_detach(&sb, NULL);
Expand Down
108 changes: 103 additions & 5 deletions t/t6135-pathspec-with-attrs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,24 @@ test_expect_success 'setup .gitattributes' '
fileSetLabel label
fileValue label=foo
fileWrongLabel label☺
newFileA* labelA
newFileB* labelB
EOF
echo fileSetLabel label1 >sub/.gitattributes &&
git add .gitattributes sub/.gitattributes &&
git commit -m "add attributes"
'

test_expect_success 'setup .gitignore' '
cat <<-\EOF >.gitignore &&
actual
expect
pathspec_file
EOF
git add .gitignore &&
git commit -m "add gitignore"
'

test_expect_success 'check specific set attr' '
cat <<-\EOF >expect &&
fileSetLabel
Expand Down Expand Up @@ -150,6 +162,7 @@ test_expect_success 'check specific value attr (2)' '
test_expect_success 'check unspecified attr' '
cat <<-\EOF >expect &&
.gitattributes
.gitignore
fileA
fileAB
fileAC
Expand All @@ -175,6 +188,7 @@ test_expect_success 'check unspecified attr' '
test_expect_success 'check unspecified attr (2)' '
cat <<-\EOF >expect &&
HEAD:.gitattributes
HEAD:.gitignore
HEAD:fileA
HEAD:fileAB
HEAD:fileAC
Expand All @@ -200,6 +214,7 @@ test_expect_success 'check unspecified attr (2)' '
test_expect_success 'check multiple unspecified attr' '
cat <<-\EOF >expect &&
.gitattributes
.gitignore
fileC
fileNoLabel
fileWrongLabel
Expand Down Expand Up @@ -239,16 +254,99 @@ test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
test_grep "Only one" actual
'

test_expect_success 'fail if attr magic is used places not implemented' '
test_expect_success 'fail if attr magic is used in places not implemented' '
# The main purpose of this test is to check that we actually fail
# when you attempt to use attr magic in commands that do not implement
# attr magic. This test does not advocate git-add to stay that way,
# though, but git-add is convenient as it has its own internal pathspec
# parsing.
test_must_fail git add ":(attr:labelB)" 2>actual &&
# attr magic. This test does not advocate check-ignore to stay that way.
# When you teach the command to grok the pathspec, you need to find
# another command to replace it for the test.
test_must_fail git check-ignore ":(attr:labelB)" 2>actual &&
test_grep "magic not supported" actual
'

test_expect_success 'check that attr magic works for git stash push' '
cat <<-\EOF >expect &&
A sub/newFileA-foo
EOF
>sub/newFileA-foo &&
>sub/newFileB-foo &&
git stash push --include-untracked -- ":(exclude,attr:labelB)" &&
git stash show --include-untracked --name-status >actual &&
test_cmp expect actual
'

test_expect_success 'check that attr magic works for git add --all' '
cat <<-\EOF >expect &&
sub/newFileA-foo
EOF
>sub/newFileA-foo &&
>sub/newFileB-foo &&
git add --all ":(exclude,attr:labelB)" &&
git diff --name-only --cached >actual &&
git restore -W -S . &&
test_cmp expect actual
'

test_expect_success 'check that attr magic works for git add -u' '
cat <<-\EOF >expect &&
sub/fileA
EOF
>sub/newFileA-foo &&
>sub/newFileB-foo &&
>sub/fileA &&
>sub/fileB &&
git add -u ":(exclude,attr:labelB)" &&
git diff --name-only --cached >actual &&
git restore -S -W . && rm sub/new* &&
test_cmp expect actual
'

test_expect_success 'check that attr magic works for git add <path>' '
cat <<-\EOF >expect &&
fileA
fileB
sub/fileA
EOF
>fileA &&
>fileB &&
>sub/fileA &&
>sub/fileB &&
git add ":(exclude,attr:labelB)sub/*" &&
git diff --name-only --cached >actual &&
git restore -S -W . &&
test_cmp expect actual
'

test_expect_success 'check that attr magic works for git -add .' '
cat <<-\EOF >expect &&
sub/fileA
EOF
>fileA &&
>fileB &&
>sub/fileA &&
>sub/fileB &&
cd sub &&
git add . ":(exclude,attr:labelB)" &&
cd .. &&
git diff --name-only --cached >actual &&
git restore -S -W . &&
test_cmp expect actual
'

test_expect_success 'check that attr magic works for git add --pathspec-from-file' '
cat <<-\EOF >pathspec_file &&
:(exclude,attr:labelB)
EOF
cat <<-\EOF >expect &&
sub/newFileA-foo
EOF
>sub/newFileA-foo &&
>sub/newFileB-foo &&
git add --all --pathspec-from-file=pathspec_file &&
git diff --name-only --cached >actual &&
test_cmp expect actual
'

test_expect_success 'abort on giving invalid label on the command line' '
test_must_fail git ls-files . ":(attr:☺)"
'
Expand Down

0 comments on commit 3780afb

Please sign in to comment.