pathspec: support :(literal) syntax for noglob pathspec

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 Jul 14, 2013 at 15:36 UTC 5c6933d201fab183a9779dca0fe43bf2f1eca098
9 files changed +64 -15
Documentation/glossary-content.txt
+16 -4
@@ -322,10 +322,22 @@ and a close parentheses `)`, and the remainder is the pattern to match
322 against the path.
323 +
324 The "magic signature" consists of an ASCII symbol that is not
325 -alphanumeric. Currently only the slash `/` is recognized as a
326 -"magic signature": it makes the pattern match from the root of
327 -the working tree, even when you are running the command from
328 -inside a subdirectory.
325 +alphanumeric.
326 ++
327 +--
328 +top `/`;;
329 + The magic word `top` (mnemonic: `/`) makes the pattern match
330 + from the root of the working tree, even when you are running
331 + the command from inside a subdirectory.
332 +
333 +literal;;
334 + Wildcards in the pattern such as `*` or `?` are treated
335 + as literal characters.
336 +--
337 ++
338 +Currently only the slash `/` is recognized as the "magic signature",
339 +but it is envisioned that we will support more types of magic in later
340 +versions of Git.
341 +
342 A pathspec with only a colon means "there is no pathspec". This form
343 should not be combined with other pathspec.
builtin/add.c
+1 -1
@@ -541,7 +541,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
541 /*
542 * file_exists() assumes exact match
543 */
544 - GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP);
544 + GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
545
546 for (i = 0; i < pathspec.nr; i++) {
547 const char *path = pathspec.items[i].match;
builtin/diff.c
+1 -1
@@ -368,7 +368,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
368 }
369 if (rev.prune_data.nr) {
370 /* builtin_diff_b_f() */
371 - GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP);
371 + GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
372 if (!path)
373 path = rev.prune_data.items[0].match;
374 paths += rev.prune_data.nr;
dir.c
+12 -3
@@ -108,7 +108,10 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
108 int n;
109 size_t max = 0;
110
111 - GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH);
111 + GUARD_PATHSPEC(pathspec,
112 + PATHSPEC_FROMTOP |
113 + PATHSPEC_MAXDEPTH |
114 + PATHSPEC_LITERAL);
115
116 for (n = 0; n < pathspec->nr; n++) {
117 size_t i = 0, len = 0;
@@ -232,7 +235,10 @@ int match_pathspec_depth(const struct pathspec *ps,
235 {
236 int i, retval = 0;
237
235 - GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH);
238 + GUARD_PATHSPEC(ps,
239 + PATHSPEC_FROMTOP |
240 + PATHSPEC_MAXDEPTH |
241 + PATHSPEC_LITERAL);
242
243 if (!ps->nr) {
244 if (!ps->recursive ||
@@ -1288,7 +1294,10 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
1294 * Check out create_simplify()
1295 */
1296 if (pathspec)
1291 - GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH);
1297 + GUARD_PATHSPEC(pathspec,
1298 + PATHSPEC_FROMTOP |
1299 + PATHSPEC_MAXDEPTH |
1300 + PATHSPEC_LITERAL);
1301
1302 if (has_symlink_leading_path(path, len))
1303 return dir->nr;
pathspec.c
+8 -3
@@ -70,6 +70,7 @@ static struct pathspec_magic {
70 const char *name;
71 } pathspec_magic[] = {
72 { PATHSPEC_FROMTOP, '/', "top" },
73 + { PATHSPEC_LITERAL, 0, "literal" },
74 };
75
76 /*
@@ -92,13 +93,15 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
93 const char *elt)
94 {
95 static int literal_global = -1;
95 - unsigned magic = 0, short_magic = 0;
96 + unsigned magic = 0, short_magic = 0, global_magic = 0;
97 const char *copyfrom = elt, *long_magic_end = NULL;
98 char *match;
99 int i, pathspec_prefix = -1;
100
101 if (literal_global < 0)
102 literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
103 + if (literal_global)
104 + global_magic |= PATHSPEC_LITERAL;
105
106 if (elt[0] != ':') {
107 ; /* nothing to do */
@@ -164,6 +167,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
167
168 magic |= short_magic;
169 *p_short_magic = short_magic;
170 + magic |= global_magic;
171
172 if (pathspec_prefix >= 0 &&
173 (prefixlen || (prefix && *prefix)))
@@ -236,7 +240,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
240 elt, ce_len, ce->name);
241 }
242
239 - if (literal_global)
243 + if (magic & PATHSPEC_LITERAL)
244 item->nowildcard_len = item->len;
245 else {
246 item->nowildcard_len = simple_length(item->match);
@@ -402,7 +406,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
406 {
407 struct pathspec ps;
408 parse_pathspec(&ps,
405 - PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
409 + PATHSPEC_ALL_MAGIC &
410 + ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
411 PATHSPEC_PREFER_CWD,
412 prefix, pathspec);
413 return ps._raw;
pathspec.h
+3 -1
@@ -4,9 +4,11 @@
4 /* Pathspec magic */
5 #define PATHSPEC_FROMTOP (1<<0)
6 #define PATHSPEC_MAXDEPTH (1<<1)
7 +#define PATHSPEC_LITERAL (1<<2)
8 #define PATHSPEC_ALL_MAGIC \
9 (PATHSPEC_FROMTOP | \
9 - PATHSPEC_MAXDEPTH)
10 + PATHSPEC_MAXDEPTH | \
11 + PATHSPEC_LITERAL)
12
13 #define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */
14
t/t6130-pathspec-noglob.sh
+18
@@ -47,18 +47,36 @@ test_expect_success 'no-glob option matches literally (vanilla)' '
47 test_cmp expect actual
48 '
49
50 +test_expect_success 'no-glob option matches literally (vanilla)' '
51 + echo vanilla >expect &&
52 + git log --format=%s -- ":(literal)foo" >actual &&
53 + test_cmp expect actual
54 +'
55 +
56 test_expect_success 'no-glob option matches literally (star)' '
57 echo star >expect &&
58 git --literal-pathspecs log --format=%s -- "f*" >actual &&
59 test_cmp expect actual
60 '
61
62 +test_expect_success 'no-glob option matches literally (star)' '
63 + echo star >expect &&
64 + git log --format=%s -- ":(literal)f*" >actual &&
65 + test_cmp expect actual
66 +'
67 +
68 test_expect_success 'no-glob option matches literally (bracket)' '
69 echo bracket >expect &&
70 git --literal-pathspecs log --format=%s -- "f[o][o]" >actual &&
71 test_cmp expect actual
72 '
73
74 +test_expect_success 'no-glob option matches literally (bracket)' '
75 + echo bracket >expect &&
76 + git log --format=%s -- ":(literal)f[o][o]" >actual &&
77 + test_cmp expect actual
78 +'
79 +
80 test_expect_success 'no-glob environment variable works' '
81 echo star >expect &&
82 GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual &&
tree-diff.c
+1 -1
@@ -202,7 +202,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
202 * path. Magic that matches more than one path is not
203 * supported.
204 */
205 - GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP);
205 + GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
206 #if 0
207 /*
208 * We should reject wildcards as well. Unfortunately we
tree-walk.c
+4 -1
@@ -636,7 +636,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
636 enum interesting never_interesting = ps->has_wildcard ?
637 entry_not_interesting : all_entries_not_interesting;
638
639 - GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH);
639 + GUARD_PATHSPEC(ps,
640 + PATHSPEC_FROMTOP |
641 + PATHSPEC_MAXDEPTH |
642 + PATHSPEC_LITERAL);
643
644 if (!ps->nr) {
645 if (!ps->recursive ||