Skip to content

Commit 94bc671

Browse files
Jean-Noël AVILAgitster
Jean-Noël AVILA
authored andcommitted
Add directory pattern matching to attributes
The manpage of gitattributes says: "The rules how the pattern matches paths are the same as in .gitignore files" and the gitignore pattern matching has a pattern ending with / for directory matching. This rule is specifically relevant for the 'export-ignore' rule used for git archive. Signed-off-by: Jean-Noel Avila <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 24a62db commit 94bc671

File tree

3 files changed

+76
-9
lines changed

3 files changed

+76
-9
lines changed

archive.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
120120
strbuf_add(&path, args->base, args->baselen);
121121
strbuf_add(&path, base, baselen);
122122
strbuf_addstr(&path, filename);
123+
if (S_ISDIR(mode) || S_ISGITLINK(mode))
124+
strbuf_addch(&path, '/');
123125
path_without_prefix = path.buf + args->baselen;
124126

125127
setup_archive_check(check);
@@ -130,7 +132,6 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
130132
}
131133

132134
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
133-
strbuf_addch(&path, '/');
134135
if (args->verbose)
135136
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
136137
err = write_entry(args, sha1, path.buf, path.len, mode);

attr.c

+17-8
Original file line numberDiff line numberDiff line change
@@ -564,17 +564,24 @@ static void bootstrap_attr_stack(void)
564564
attr_stack = elem;
565565
}
566566

567+
static const char *find_basename(const char *path)
568+
{
569+
const char *cp, *last_slash = NULL;
570+
571+
for (cp = path; *cp; cp++) {
572+
if (*cp == '/' && cp[1])
573+
last_slash = cp;
574+
}
575+
return last_slash ? last_slash + 1 : path;
576+
}
577+
567578
static void prepare_attr_stack(const char *path)
568579
{
569580
struct attr_stack *elem, *info;
570581
int dirlen, len;
571582
const char *cp;
572583

573-
cp = strrchr(path, '/');
574-
if (!cp)
575-
dirlen = 0;
576-
else
577-
dirlen = cp - path;
584+
dirlen = find_basename(path) - path;
578585

579586
/*
580587
* At the bottom of the attribute stack is the built-in
@@ -668,6 +675,10 @@ static int path_matches(const char *pathname, int pathlen,
668675
const char *pattern = pat->pattern;
669676
int prefix = pat->nowildcardlen;
670677

678+
if ((pat->flags & EXC_FLAG_MUSTBEDIR) &&
679+
((!pathlen) || (pathname[pathlen-1] != '/')))
680+
return 0;
681+
671682
if (pat->flags & EXC_FLAG_NODIR) {
672683
return match_basename(basename,
673684
pathlen - (basename - pathname),
@@ -758,9 +769,7 @@ static void collect_all_attrs(const char *path)
758769
for (i = 0; i < attr_nr; i++)
759770
check_all_attr[i].value = ATTR__UNKNOWN;
760771

761-
basename = strrchr(path, '/');
762-
basename = basename ? basename + 1 : path;
763-
772+
basename = find_basename(path);
764773
pathlen = strlen(path);
765774
rem = attr_nr;
766775
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)

t/t5002-archive-attr-pattern.sh

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/bin/sh
2+
3+
test_description='git archive attribute pattern tests'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_exists() {
8+
test_expect_success " $1 exists" "test -e $1"
9+
}
10+
11+
test_expect_missing() {
12+
test_expect_success " $1 does not exist" "test ! -e $1"
13+
}
14+
15+
test_expect_success 'setup' '
16+
echo ignored >ignored &&
17+
echo ignored export-ignore >>.git/info/attributes &&
18+
git add ignored &&
19+
20+
mkdir not-ignored-dir &&
21+
echo ignored-in-tree >not-ignored-dir/ignored &&
22+
echo not-ignored-in-tree >not-ignored-dir/ignored-only-if-dir &&
23+
git add not-ignored-dir &&
24+
25+
mkdir ignored-only-if-dir &&
26+
echo ignored by ignored dir >ignored-only-if-dir/ignored-by-ignored-dir &&
27+
echo ignored-only-if-dir/ export-ignore >>.git/info/attributes &&
28+
git add ignored-only-if-dir &&
29+
30+
31+
mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir &&
32+
echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir &&
33+
git add one-level-lower &&
34+
35+
git commit -m. &&
36+
37+
git clone --bare . bare &&
38+
cp .git/info/attributes bare/info/attributes
39+
'
40+
41+
test_expect_success 'git archive' '
42+
git archive HEAD >archive.tar &&
43+
(mkdir archive && cd archive && "$TAR" xf -) <archive.tar
44+
'
45+
46+
test_expect_missing archive/ignored
47+
test_expect_missing archive/not-ignored-dir/ignored
48+
test_expect_exists archive/not-ignored-dir/ignored-only-if-dir
49+
test_expect_exists archive/not-ignored-dir/
50+
test_expect_missing archive/ignored-only-if-dir/
51+
test_expect_missing archive/ignored-ony-if-dir/ignored-by-ignored-dir
52+
test_expect_exists archive/one-level-lower/
53+
test_expect_missing archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
54+
test_expect_missing archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
55+
56+
57+
test_done

0 commit comments

Comments
 (0)