Skip to content

Commit

Permalink
Merge branch 'nd/wildmatch'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Jan 10, 2013
2 parents 4249d85 + b6a3d33 commit 2adf724
Show file tree
Hide file tree
Showing 13 changed files with 564 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -197,6 +197,7 @@
/test-string-list
/test-subprocess
/test-svn-fe
/test-wildmatch
/common-cmds.h
*.tar.gz
*.dsc
Expand Down
19 changes: 19 additions & 0 deletions Documentation/gitignore.txt
Expand Up @@ -108,6 +108,25 @@ PATTERN FORMAT
For example, "/{asterisk}.c" matches "cat-file.c" but not
"mozilla-sha1/sha1.c".

Two consecutive asterisks ("`**`") in patterns matched against
full pathname may have special meaning:

- A leading "`**`" followed by a slash means match in all
directories. For example, "`**/foo`" matches file or directory
"`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar"
matches file or directory "`bar`" anywhere that is directly
under directory "`foo`".

- A trailing "/**" matches everything inside. For example,
"abc/**" matches all files inside directory "abc", relative
to the location of the `.gitignore` file, with infinite depth.

- A slash followed by two consecutive asterisks then a slash
matches zero or more directories. For example, "`a/**/b`"
matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on.

- Other consecutive asterisks are considered invalid.

NOTES
-----

Expand Down
3 changes: 3 additions & 0 deletions Makefile
Expand Up @@ -532,6 +532,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain
TEST_PROGRAMS_NEED_X += test-string-list
TEST_PROGRAMS_NEED_X += test-subprocess
TEST_PROGRAMS_NEED_X += test-svn-fe
TEST_PROGRAMS_NEED_X += test-wildmatch

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

Expand Down Expand Up @@ -704,6 +705,7 @@ LIB_H += userdiff.h
LIB_H += utf8.h
LIB_H += varint.h
LIB_H += walker.h
LIB_H += wildmatch.h
LIB_H += wt-status.h
LIB_H += xdiff-interface.h
LIB_H += xdiff/xdiff.h
Expand Down Expand Up @@ -838,6 +840,7 @@ LIB_OBJS += utf8.o
LIB_OBJS += varint.o
LIB_OBJS += version.o
LIB_OBJS += walker.o
LIB_OBJS += wildmatch.o
LIB_OBJS += wrapper.o
LIB_OBJS += write_or_die.o
LIB_OBJS += ws.o
Expand Down
3 changes: 2 additions & 1 deletion compat/fnmatch/fnmatch.c
Expand Up @@ -55,7 +55,8 @@
program understand `configure --with-gnu-libc' and omit the object files,
it is simpler to just do this in the source for each such file. */

#if defined _LIBC || !defined __GNU_LIBRARY__
#if defined NO_FNMATCH || defined NO_FNMATCH_CASEFOLD || \
defined _LIBC || !defined __GNU_LIBRARY__


# if defined STDC_HEADERS || !defined isascii
Expand Down
15 changes: 9 additions & 6 deletions ctype.c
Expand Up @@ -11,18 +11,21 @@ enum {
D = GIT_DIGIT,
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */
P = GIT_PATHSPEC_MAGIC /* other non-alnum, except for ] and } */
P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */
X = GIT_CNTRL,
U = GIT_PUNCT,
Z = GIT_CNTRL | GIT_SPACE
};

unsigned char sane_ctype[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
const unsigned char sane_ctype[256] = {
X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */
S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P, /* 80.. 95 */
A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0, /* 112..127 */
A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */
/* Nothing in the 128.. range */
};

Expand Down
4 changes: 3 additions & 1 deletion dir.c
Expand Up @@ -8,6 +8,7 @@
#include "cache.h"
#include "dir.h"
#include "refs.h"
#include "wildmatch.h"

struct path_simplify {
int len;
Expand Down Expand Up @@ -624,7 +625,8 @@ int match_pathname(const char *pathname, int pathlen,
namelen -= prefix;
}

return fnmatch_icase(pattern, name, FNM_PATHNAME) == 0;
return wildmatch(pattern, name,
ignore_case ? FNM_CASEFOLD : 0) == 0;
}

/* Scan the list and let the last match determine the fate.
Expand Down
12 changes: 11 additions & 1 deletion git-compat-util.h
Expand Up @@ -528,13 +528,19 @@ extern const char tolower_trans_tbl[256];
#undef isupper
#undef tolower
#undef toupper
extern unsigned char sane_ctype[256];
#undef iscntrl
#undef ispunct
#undef isxdigit

extern const unsigned char sane_ctype[256];
#define GIT_SPACE 0x01
#define GIT_DIGIT 0x02
#define GIT_ALPHA 0x04
#define GIT_GLOB_SPECIAL 0x08
#define GIT_REGEX_SPECIAL 0x10
#define GIT_PATHSPEC_MAGIC 0x20
#define GIT_CNTRL 0x40
#define GIT_PUNCT 0x80
#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
#define isascii(x) (((x) & ~0x7f) == 0)
#define isspace(x) sane_istest(x,GIT_SPACE)
Expand All @@ -546,6 +552,10 @@ extern unsigned char sane_ctype[256];
#define isupper(x) sane_iscase(x, 0)
#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
#define iscntrl(x) (sane_istest(x,GIT_CNTRL))
#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
#define isxdigit(x) (hexval_table[x] != -1)
#define tolower(x) sane_case((unsigned char)(x), 0x20)
#define toupper(x) sane_case((unsigned char)(x), 0)
#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
Expand Down
37 changes: 37 additions & 0 deletions t/t0003-attributes.sh
Expand Up @@ -206,6 +206,43 @@ test_expect_success 'patterns starting with exclamation' '
attr_check "!f" foo
'

test_expect_success '"**" test' '
echo "**/f foo=bar" >.gitattributes &&
cat <<\EOF >expect &&
f: foo: bar
a/f: foo: bar
a/b/f: foo: bar
a/b/c/f: foo: bar
EOF
git check-attr foo -- "f" >actual 2>err &&
git check-attr foo -- "a/f" >>actual 2>>err &&
git check-attr foo -- "a/b/f" >>actual 2>>err &&
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
test_cmp expect actual &&
test_line_count = 0 err
'

test_expect_success '"**" with no slashes test' '
echo "a**f foo=bar" >.gitattributes &&
git check-attr foo -- "f" >actual &&
cat <<\EOF >expect &&
f: foo: unspecified
af: foo: bar
axf: foo: bar
a/f: foo: unspecified
a/b/f: foo: unspecified
a/b/c/f: foo: unspecified
EOF
git check-attr foo -- "f" >actual 2>err &&
git check-attr foo -- "af" >>actual 2>err &&
git check-attr foo -- "axf" >>actual 2>err &&
git check-attr foo -- "a/f" >>actual 2>>err &&
git check-attr foo -- "a/b/f" >>actual 2>>err &&
git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
test_cmp expect actual &&
test_line_count = 0 err
'

test_expect_success 'setup bare' '
git clone --bare . bare.git &&
cd bare.git
Expand Down
18 changes: 18 additions & 0 deletions t/t3001-ls-files-others-exclude.sh
Expand Up @@ -220,4 +220,22 @@ test_expect_success 'pattern matches prefix completely' '
test_cmp expect actual
'

test_expect_success 'ls-files with "**" patterns' '
cat <<\EOF >expect &&
a.1
one/a.1
one/two/a.1
three/a.1
EOF
git ls-files -o -i --exclude "**/a.1" >actual
test_cmp expect actual
'


test_expect_success 'ls-files with "**" patterns and no slashes' '
: >expect &&
git ls-files -o -i --exclude "one**a.1" >actual &&
test_cmp expect actual
'

test_done

0 comments on commit 2adf724

Please sign in to comment.