Skip to content

Commit 2adf724

Browse files
committed
Merge branch 'nd/wildmatch'
Allows pathname patterns in .gitignore and .gitattributes files with double-asterisks "foo/**/bar" to match any number of directory hierarchies. * nd/wildmatch: wildmatch: replace variable 'special' with better named ones compat/fnmatch: respect NO_FNMATCH* even on glibc wildmatch: fix "**" special case t3070: Disable some failing fnmatch tests test-wildmatch: avoid Windows path mangling Support "**" wildcard in .gitignore and .gitattributes wildmatch: make /**/ match zero or more directories wildmatch: adjust "**" behavior wildmatch: fix case-insensitive matching wildmatch: remove static variable force_lower_case wildmatch: make wildmatch's return value compatible with fnmatch t3070: disable unreliable fnmatch tests Integrate wildmatch to git wildmatch: follow Git's coding convention wildmatch: remove unnecessary functions Import wildmatch from rsync ctype: support iscntrl, ispunct, isxdigit and isprint ctype: make sane_ctype[] const array Conflicts: Makefile
2 parents 4249d85 + b6a3d33 commit 2adf724

File tree

13 files changed

+564
-9
lines changed

13 files changed

+564
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@
197197
/test-string-list
198198
/test-subprocess
199199
/test-svn-fe
200+
/test-wildmatch
200201
/common-cmds.h
201202
*.tar.gz
202203
*.dsc

Documentation/gitignore.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@ PATTERN FORMAT
108108
For example, "/{asterisk}.c" matches "cat-file.c" but not
109109
"mozilla-sha1/sha1.c".
110110

111+
Two consecutive asterisks ("`**`") in patterns matched against
112+
full pathname may have special meaning:
113+
114+
- A leading "`**`" followed by a slash means match in all
115+
directories. For example, "`**/foo`" matches file or directory
116+
"`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar"
117+
matches file or directory "`bar`" anywhere that is directly
118+
under directory "`foo`".
119+
120+
- A trailing "/**" matches everything inside. For example,
121+
"abc/**" matches all files inside directory "abc", relative
122+
to the location of the `.gitignore` file, with infinite depth.
123+
124+
- A slash followed by two consecutive asterisks then a slash
125+
matches zero or more directories. For example, "`a/**/b`"
126+
matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on.
127+
128+
- Other consecutive asterisks are considered invalid.
129+
111130
NOTES
112131
-----
113132

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain
532532
TEST_PROGRAMS_NEED_X += test-string-list
533533
TEST_PROGRAMS_NEED_X += test-subprocess
534534
TEST_PROGRAMS_NEED_X += test-svn-fe
535+
TEST_PROGRAMS_NEED_X += test-wildmatch
535536

536537
TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
537538

@@ -704,6 +705,7 @@ LIB_H += userdiff.h
704705
LIB_H += utf8.h
705706
LIB_H += varint.h
706707
LIB_H += walker.h
708+
LIB_H += wildmatch.h
707709
LIB_H += wt-status.h
708710
LIB_H += xdiff-interface.h
709711
LIB_H += xdiff/xdiff.h
@@ -838,6 +840,7 @@ LIB_OBJS += utf8.o
838840
LIB_OBJS += varint.o
839841
LIB_OBJS += version.o
840842
LIB_OBJS += walker.o
843+
LIB_OBJS += wildmatch.o
841844
LIB_OBJS += wrapper.o
842845
LIB_OBJS += write_or_die.o
843846
LIB_OBJS += ws.o

compat/fnmatch/fnmatch.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
program understand `configure --with-gnu-libc' and omit the object files,
5656
it is simpler to just do this in the source for each such file. */
5757

58-
#if defined _LIBC || !defined __GNU_LIBRARY__
58+
#if defined NO_FNMATCH || defined NO_FNMATCH_CASEFOLD || \
59+
defined _LIBC || !defined __GNU_LIBRARY__
5960

6061

6162
# if defined STDC_HEADERS || !defined isascii

ctype.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,21 @@ enum {
1111
D = GIT_DIGIT,
1212
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
1313
R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */
14-
P = GIT_PATHSPEC_MAGIC /* other non-alnum, except for ] and } */
14+
P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */
15+
X = GIT_CNTRL,
16+
U = GIT_PUNCT,
17+
Z = GIT_CNTRL | GIT_SPACE
1518
};
1619

17-
unsigned char sane_ctype[256] = {
18-
0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
19-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
20+
const unsigned char sane_ctype[256] = {
21+
X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */
22+
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */
2023
S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
2124
D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
2225
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
23-
A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P, /* 80.. 95 */
26+
A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */
2427
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
25-
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0, /* 112..127 */
28+
A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */
2629
/* Nothing in the 128.. range */
2730
};
2831

dir.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "cache.h"
99
#include "dir.h"
1010
#include "refs.h"
11+
#include "wildmatch.h"
1112

1213
struct path_simplify {
1314
int len;
@@ -624,7 +625,8 @@ int match_pathname(const char *pathname, int pathlen,
624625
namelen -= prefix;
625626
}
626627

627-
return fnmatch_icase(pattern, name, FNM_PATHNAME) == 0;
628+
return wildmatch(pattern, name,
629+
ignore_case ? FNM_CASEFOLD : 0) == 0;
628630
}
629631

630632
/* Scan the list and let the last match determine the fate.

git-compat-util.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,13 +528,19 @@ extern const char tolower_trans_tbl[256];
528528
#undef isupper
529529
#undef tolower
530530
#undef toupper
531-
extern unsigned char sane_ctype[256];
531+
#undef iscntrl
532+
#undef ispunct
533+
#undef isxdigit
534+
535+
extern const unsigned char sane_ctype[256];
532536
#define GIT_SPACE 0x01
533537
#define GIT_DIGIT 0x02
534538
#define GIT_ALPHA 0x04
535539
#define GIT_GLOB_SPECIAL 0x08
536540
#define GIT_REGEX_SPECIAL 0x10
537541
#define GIT_PATHSPEC_MAGIC 0x20
542+
#define GIT_CNTRL 0x40
543+
#define GIT_PUNCT 0x80
538544
#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
539545
#define isascii(x) (((x) & ~0x7f) == 0)
540546
#define isspace(x) sane_istest(x,GIT_SPACE)
@@ -546,6 +552,10 @@ extern unsigned char sane_ctype[256];
546552
#define isupper(x) sane_iscase(x, 0)
547553
#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
548554
#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
555+
#define iscntrl(x) (sane_istest(x,GIT_CNTRL))
556+
#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
557+
GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
558+
#define isxdigit(x) (hexval_table[x] != -1)
549559
#define tolower(x) sane_case((unsigned char)(x), 0x20)
550560
#define toupper(x) sane_case((unsigned char)(x), 0)
551561
#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)

t/t0003-attributes.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,43 @@ test_expect_success 'patterns starting with exclamation' '
206206
attr_check "!f" foo
207207
'
208208

209+
test_expect_success '"**" test' '
210+
echo "**/f foo=bar" >.gitattributes &&
211+
cat <<\EOF >expect &&
212+
f: foo: bar
213+
a/f: foo: bar
214+
a/b/f: foo: bar
215+
a/b/c/f: foo: bar
216+
EOF
217+
git check-attr foo -- "f" >actual 2>err &&
218+
git check-attr foo -- "a/f" >>actual 2>>err &&
219+
git check-attr foo -- "a/b/f" >>actual 2>>err &&
220+
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
221+
test_cmp expect actual &&
222+
test_line_count = 0 err
223+
'
224+
225+
test_expect_success '"**" with no slashes test' '
226+
echo "a**f foo=bar" >.gitattributes &&
227+
git check-attr foo -- "f" >actual &&
228+
cat <<\EOF >expect &&
229+
f: foo: unspecified
230+
af: foo: bar
231+
axf: foo: bar
232+
a/f: foo: unspecified
233+
a/b/f: foo: unspecified
234+
a/b/c/f: foo: unspecified
235+
EOF
236+
git check-attr foo -- "f" >actual 2>err &&
237+
git check-attr foo -- "af" >>actual 2>err &&
238+
git check-attr foo -- "axf" >>actual 2>err &&
239+
git check-attr foo -- "a/f" >>actual 2>>err &&
240+
git check-attr foo -- "a/b/f" >>actual 2>>err &&
241+
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
242+
test_cmp expect actual &&
243+
test_line_count = 0 err
244+
'
245+
209246
test_expect_success 'setup bare' '
210247
git clone --bare . bare.git &&
211248
cd bare.git

t/t3001-ls-files-others-exclude.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,22 @@ test_expect_success 'pattern matches prefix completely' '
220220
test_cmp expect actual
221221
'
222222

223+
test_expect_success 'ls-files with "**" patterns' '
224+
cat <<\EOF >expect &&
225+
a.1
226+
one/a.1
227+
one/two/a.1
228+
three/a.1
229+
EOF
230+
git ls-files -o -i --exclude "**/a.1" >actual
231+
test_cmp expect actual
232+
'
233+
234+
235+
test_expect_success 'ls-files with "**" patterns and no slashes' '
236+
: >expect &&
237+
git ls-files -o -i --exclude "one**a.1" >actual &&
238+
test_cmp expect actual
239+
'
240+
223241
test_done

0 commit comments

Comments
 (0)