parse_pathspec: make sure the prefix part is wildcard-free

Prepending prefix to pathspec is a trick to workaround the fact that commands can be executed in a subdirectory, but all git commands run at worktree's root. The prefix part should always be treated as literal string. Make it so. 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 645a29c40a12a4ade1b041dce246ae241c502a64
5 files changed +54 -10
cache.h
+2
@@ -414,6 +414,7 @@ extern void setup_work_tree(void);
414 extern const char *setup_git_directory_gently(int *);
415 extern const char *setup_git_directory(void);
416 extern char *prefix_path(const char *prefix, int len, const char *path);
417 +extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
418 extern const char *prefix_filename(const char *prefix, int len, const char *path);
419 extern int check_filename(const char *prefix, const char *name);
420 extern void verify_filename(const char *prefix,
@@ -741,6 +742,7 @@ const char *real_path(const char *path);
742 const char *real_path_if_valid(const char *path);
743 const char *absolute_path(const char *path);
744 const char *relative_path(const char *abs, const char *base);
745 +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
746 int normalize_path_copy(char *dst, const char *src);
747 int longest_ancestor_length(const char *path, struct string_list *prefixes);
748 char *strip_path_suffix(const char *path, const char *suffix);
path.c
+14 -1
@@ -492,8 +492,14 @@ const char *relative_path(const char *abs, const char *base)
492 *
493 * Note that this function is purely textual. It does not follow symlinks,
494 * verify the existence of the path, or make any system calls.
495 + *
496 + * prefix_len != NULL is for a specific case of prefix_pathspec():
497 + * assume that src == dst and src[0..prefix_len-1] is already
498 + * normalized, any time "../" eats up to the prefix_len part,
499 + * prefix_len is reduced. In the end prefix_len is the remaining
500 + * prefix that has not been overridden by user pathspec.
501 */
496 -int normalize_path_copy(char *dst, const char *src)
502 +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
503 {
504 char *dst0;
505
@@ -568,11 +574,18 @@ int normalize_path_copy(char *dst, const char *src)
574 /* Windows: dst[-1] cannot be backslash anymore */
575 while (dst0 < dst && dst[-1] != '/')
576 dst--;
577 + if (prefix_len && *prefix_len > dst - dst0)
578 + *prefix_len = dst - dst0;
579 }
580 *dst = '\0';
581 return 0;
582 }
583
584 +int normalize_path_copy(char *dst, const char *src)
585 +{
586 + return normalize_path_copy_len(dst, src, NULL);
587 +}
588 +
589 /*
590 * path = Canonical absolute path
591 * prefixes = string_list containing normalized, absolute paths without
pathspec.c
+17 -4
@@ -150,10 +150,14 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
150 magic |= short_magic;
151 *p_short_magic = short_magic;
152
153 - if (magic & PATHSPEC_FROMTOP)
153 + if (magic & PATHSPEC_FROMTOP) {
154 match = xstrdup(copyfrom);
155 - else
156 - match = prefix_path(prefix, prefixlen, copyfrom);
155 + prefixlen = 0;
156 + } else {
157 + match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom);
158 + if (!match)
159 + die(_("%s: '%s' is outside repository"), elt, copyfrom);
160 + }
161 *raw = item->match = match;
162 /*
163 * Prefix the pathspec (keep all magic) and assign to
@@ -167,6 +171,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
171 } else
172 item->original = elt;
173 item->len = strlen(item->match);
174 + item->prefix = prefixlen;
175
176 if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) &&
177 (item->len >= 1 && item->match[item->len - 1] == '/') &&
@@ -198,13 +203,20 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
203
204 if (limit_pathspec_to_literal())
205 item->nowildcard_len = item->len;
201 - else
206 + else {
207 item->nowildcard_len = simple_length(item->match);
208 + if (item->nowildcard_len < prefixlen)
209 + item->nowildcard_len = prefixlen;
210 + }
211 item->flags = 0;
212 if (item->nowildcard_len < item->len &&
213 item->match[item->nowildcard_len] == '*' &&
214 no_wildcard(item->match + item->nowildcard_len + 1))
215 item->flags |= PATHSPEC_ONESTAR;
216 +
217 + /* sanity checks, pathspec matchers assume these are sane */
218 + assert(item->nowildcard_len <= item->len &&
219 + item->prefix <= item->len);
220 return magic;
221 }
222
@@ -284,6 +296,7 @@ void parse_pathspec(struct pathspec *pathspec,
296 item->match = prefix;
297 item->original = prefix;
298 item->nowildcard_len = item->len = strlen(prefix);
299 + item->prefix = item->len;
300 raw[0] = prefix;
301 raw[1] = NULL;
302 pathspec->nr = 1;
pathspec.h
+1 -1
@@ -21,7 +21,7 @@ struct pathspec {
21 const char *match;
22 const char *original;
23 unsigned magic;
24 - int len;
24 + int len, prefix;
25 int nowildcard_len;
26 int flags;
27 } *items;
setup.c
+20 -4
@@ -5,7 +5,19 @@
5 static int inside_git_dir = -1;
6 static int inside_work_tree = -1;
7
8 -static char *prefix_path_gently(const char *prefix, int len, const char *path)
8 +/*
9 + * Normalize "path", prepending the "prefix" for relative paths. If
10 + * remaining_prefix is not NULL, return the actual prefix still
11 + * remains in the path. For example, prefix = sub1/sub2/ and path is
12 + *
13 + * foo -> sub1/sub2/foo (full prefix)
14 + * ../foo -> sub1/foo (remaining prefix is sub1/)
15 + * ../../bar -> bar (no remaining prefix)
16 + * ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix)
17 + * `pwd`/../bar -> sub1/bar (no remaining prefix)
18 + */
19 +char *prefix_path_gently(const char *prefix, int len,
20 + int *remaining_prefix, const char *path)
21 {
22 const char *orig = path;
23 char *sanitized;
@@ -13,13 +25,17 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path)
25 const char *temp = real_path(path);
26 sanitized = xmalloc(len + strlen(temp) + 1);
27 strcpy(sanitized, temp);
28 + if (remaining_prefix)
29 + *remaining_prefix = 0;
30 } else {
31 sanitized = xmalloc(len + strlen(path) + 1);
32 if (len)
33 memcpy(sanitized, prefix, len);
34 strcpy(sanitized + len, path);
35 + if (remaining_prefix)
36 + *remaining_prefix = len;
37 }
22 - if (normalize_path_copy(sanitized, sanitized))
38 + if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix))
39 goto error_out;
40 if (is_absolute_path(orig)) {
41 size_t root_len, len, total;
@@ -44,7 +60,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path)
60
61 char *prefix_path(const char *prefix, int len, const char *path)
62 {
47 - char *r = prefix_path_gently(prefix, len, path);
63 + char *r = prefix_path_gently(prefix, len, NULL, path);
64 if (!r)
65 die("'%s' is outside repository", path);
66 return r;
@@ -53,7 +69,7 @@ char *prefix_path(const char *prefix, int len, const char *path)
69 int path_inside_repo(const char *prefix, const char *path)
70 {
71 int len = prefix ? strlen(prefix) : 0;
56 - char *r = prefix_path_gently(prefix, len, path);
72 + char *r = prefix_path_gently(prefix, len, NULL, path);
73 if (r) {
74 free(r);
75 return 1;