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 <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jean-Noël AVILA committed
Dec 8, 2012 at 21:04 UTC
94bc671a1f2e8610de475c2494d2763355a99f65
3 files changed
+76
-9
archive.c
+2
-1
@@ -120,6 +120,8 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
120
strbuf_add(&path, args->base, args->baselen);
121
strbuf_add(&path, base, baselen);
122
strbuf_addstr(&path, filename);
123
+ if (S_ISDIR(mode) || S_ISGITLINK(mode))
124
+ strbuf_addch(&path, '/');
125
path_without_prefix = path.buf + args->baselen;
126
127
setup_archive_check(check);
@@ -130,7 +132,6 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
132
}
133
134
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
133
- strbuf_addch(&path, '/');
135
if (args->verbose)
136
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
137
err = write_entry(args, sha1, path.buf, path.len, mode);
attr.c
+17
-8
@@ -564,17 +564,24 @@ static void bootstrap_attr_stack(void)
564
attr_stack = elem;
565
}
566
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
+
578
static void prepare_attr_stack(const char *path)
579
{
580
struct attr_stack *elem, *info;
581
int dirlen, len;
582
const char *cp;
583
573
- cp = strrchr(path, '/');
574
- if (!cp)
575
- dirlen = 0;
576
- else
577
- dirlen = cp - path;
584
+ dirlen = find_basename(path) - path;
585
586
/*
587
* At the bottom of the attribute stack is the built-in
@@ -668,6 +675,10 @@ static int path_matches(const char *pathname, int pathlen,
675
const char *pattern = pat->pattern;
676
int prefix = pat->nowildcardlen;
677
678
+ if ((pat->flags & EXC_FLAG_MUSTBEDIR) &&
679
+ ((!pathlen) || (pathname[pathlen-1] != '/')))
680
+ return 0;
681
+
682
if (pat->flags & EXC_FLAG_NODIR) {
683
return match_basename(basename,
684
pathlen - (basename - pathname),
@@ -758,9 +769,7 @@ static void collect_all_attrs(const char *path)
769
for (i = 0; i < attr_nr; i++)
770
check_all_attr[i].value = ATTR__UNKNOWN;
771
761
- basename = strrchr(path, '/');
762
- basename = basename ? basename + 1 : path;
763
-
772
+ basename = find_basename(path);
773
pathlen = strlen(path);
774
rem = attr_nr;
775
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
t/t5002-archive-attr-pattern.sh
new
+57
@@ -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