Support "**" wildcard in .gitignore and .gitattributes

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Oct 15, 2012 at 13:26 UTC 237ec6e40d4fd1a0190c4ffde6d18278abc5853a
4 files changed +77 -1
Documentation/gitignore.txt
+19
@@ -96,6 +96,25 @@ PATTERN FORMAT
96 For example, "/{asterisk}.c" matches "cat-file.c" but not
97 "mozilla-sha1/sha1.c".
98
99 +Two consecutive asterisks ("`**`") in patterns matched against
100 +full pathname may have special meaning:
101 +
102 + - A leading "`**`" followed by a slash means match in all
103 + directories. For example, "`**/foo`" matches file or directory
104 + "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar"
105 + matches file or directory "`bar`" anywhere that is directly
106 + under directory "`foo`".
107 +
108 + - A trailing "/**" matches everything inside. For example,
109 + "abc/**" matches all files inside directory "abc", relative
110 + to the location of the `.gitignore` file, with infinite depth.
111 +
112 + - A slash followed by two consecutive asterisks then a slash
113 + matches zero or more directories. For example, "`a/**/b`"
114 + matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on.
115 +
116 + - Other consecutive asterisks are considered invalid.
117 +
118 NOTES
119 -----
120
dir.c
+3 -1
@@ -8,6 +8,7 @@
8 #include "cache.h"
9 #include "dir.h"
10 #include "refs.h"
11 +#include "wildmatch.h"
12
13 struct path_simplify {
14 int len;
@@ -593,7 +594,8 @@ int match_pathname(const char *pathname, int pathlen,
594 namelen -= prefix;
595 }
596
596 - return fnmatch_icase(pattern, name, FNM_PATHNAME) == 0;
597 + return wildmatch(pattern, name,
598 + ignore_case ? FNM_CASEFOLD : 0) == 0;
599 }
600
601 /* Scan the list and let the last match determine the fate.
t/t0003-attributes.sh
+37
@@ -216,6 +216,43 @@ test_expect_success 'patterns starting with exclamation' '
216 attr_check "!f" foo
217 '
218
219 +test_expect_success '"**" test' '
220 + echo "**/f foo=bar" >.gitattributes &&
221 + cat <<\EOF >expect &&
222 +f: foo: bar
223 +a/f: foo: bar
224 +a/b/f: foo: bar
225 +a/b/c/f: foo: bar
226 +EOF
227 + git check-attr foo -- "f" >actual 2>err &&
228 + git check-attr foo -- "a/f" >>actual 2>>err &&
229 + git check-attr foo -- "a/b/f" >>actual 2>>err &&
230 + git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
231 + test_cmp expect actual &&
232 + test_line_count = 0 err
233 +'
234 +
235 +test_expect_success '"**" with no slashes test' '
236 + echo "a**f foo=bar" >.gitattributes &&
237 + git check-attr foo -- "f" >actual &&
238 + cat <<\EOF >expect &&
239 +f: foo: unspecified
240 +af: foo: bar
241 +axf: foo: bar
242 +a/f: foo: unspecified
243 +a/b/f: foo: unspecified
244 +a/b/c/f: foo: unspecified
245 +EOF
246 + git check-attr foo -- "f" >actual 2>err &&
247 + git check-attr foo -- "af" >>actual 2>err &&
248 + git check-attr foo -- "axf" >>actual 2>err &&
249 + git check-attr foo -- "a/f" >>actual 2>>err &&
250 + git check-attr foo -- "a/b/f" >>actual 2>>err &&
251 + git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
252 + test_cmp expect actual &&
253 + test_line_count = 0 err
254 +'
255 +
256 test_expect_success 'setup bare' '
257 git clone --bare . bare.git &&
258 cd bare.git
t/t3001-ls-files-others-exclude.sh
+18
@@ -220,4 +220,22 @@ test_expect_success 'pattern matches prefix completely' '
220 test_cmp expect actual
221 '
222
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 +
241 test_done