Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- cache.h | 1 + path.c | 15 ++++++++++++++- setup.c | 24 +++++++++++++++-------- t/t6131-pathspec-prefix.sh | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 9 deletions(-) create mode 100755 t/t6131-pathspec-prefix.sh diff --git a/cache.h b/cache.h index c7a8d28..f3de28d 100644 --- a/cache.h +++ b/cache.h @@ -747,6 +747,7 @@ const char *real_path(const char *path); const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); const char *relative_path(const char *abs, const char *base); +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len); int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); diff --git a/path.c b/path.c index d3d3f8b..7baf334 100644 --- a/path.c +++ b/path.c @@ -487,8 +487,14 @@ const char *relative_path(const char *abs, const char *base) * * Note that this function is purely textual. It does not follow symlinks, * verify the existence of the path, or make any system calls. + * + * prefix_len != NULL is for a specific case of prefix_pathspec(): + * assume that src == dst and src[0..prefix_len-1] is already + * normalized, any time "../" eats up to the prefix_len part, + * prefix_len is reduced. In the end prefix_len is the remaining + * prefix that has not been overridden by user pathspec. */ -int normalize_path_copy(char *dst, const char *src) +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) { char *dst0; @@ -563,11 +569,18 @@ int normalize_path_copy(char *dst, const char *src) /* Windows: dst[-1] cannot be backslash anymore */ while (dst0 < dst && dst[-1] != '/') dst--; + if (prefix_len && *prefix_len > dst - dst0) + *prefix_len = dst - dst0; } *dst = '\0'; return 0; } +int normalize_path_copy(char *dst, const char *src) +{ + return normalize_path_copy_len(dst, src, NULL); +} + /* * path = Canonical absolute path * prefixes = string_list containing normalized, absolute paths without diff --git a/setup.c b/setup.c index 9db6093..c5e97c9 100644 --- a/setup.c +++ b/setup.c @@ -5,10 +5,11 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; -static char *prefix_path_gently(const char *prefix, int len, const char *path) +static char *prefix_path_gently(const char *prefix, int *p_len, const char *path) { const char *orig = path; char *sanitized; + int len = *p_len; if (is_absolute_path(orig)) { const char *temp = real_path(path); sanitized = xmalloc(len + strlen(temp) + 1); @@ -19,7 +20,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path) memcpy(sanitized, prefix, len); strcpy(sanitized + len, path); } - if (normalize_path_copy(sanitized, sanitized)) + if (normalize_path_copy_len(sanitized, sanitized, p_len)) goto error_out; if (is_absolute_path(orig)) { size_t root_len, len, total; @@ -44,7 +45,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path) char *prefix_path(const char *prefix, int len, const char *path) { - char *r = prefix_path_gently(prefix, len, path); + char *r = prefix_path_gently(prefix, &len, path); if (!r) die("'%s' is outside repository", path); return r; @@ -53,7 +54,7 @@ char *prefix_path(const char *prefix, int len, const char *path) int path_inside_repo(const char *prefix, const char *path) { int len = prefix ? strlen(prefix) : 0; - char *r = prefix_path_gently(prefix, len, path); + char *r = prefix_path_gently(prefix, &len, path); if (r) { free(r); return 1; @@ -261,10 +262,14 @@ static unsigned prefix_pathspec(struct pathspec_item *item, copyfrom++; } - if (magic & PATHSPEC_FROMTOP) + if (magic & PATHSPEC_FROMTOP) { match = xstrdup(copyfrom); - else - match = prefix_path(prefix, prefixlen, copyfrom); + prefixlen = 0; + } else { + match = prefix_path_gently(prefix, &prefixlen, copyfrom); + if (!match) + die("%s: '%s' is outside repository", elt, copyfrom); + } *raw = item->match = match; item->original = elt; item->len = strlen(item->match); @@ -300,8 +305,11 @@ static unsigned prefix_pathspec(struct pathspec_item *item, item->flags = 0; if (limit_pathspec_to_literal()) item->nowildcard_len = item->len; - else + else { item->nowildcard_len = simple_length(item->match); + if (item->nowildcard_len < prefixlen) + item->nowildcard_len = prefixlen; + } if (item->nowildcard_len < item->len && item->match[item->nowildcard_len] == '*' && no_wildcard(item->match + item->nowildcard_len + 1)) diff --git a/t/t6131-pathspec-prefix.sh b/t/t6131-pathspec-prefix.sh new file mode 100755 index 0000000..db59091 --- /dev/null +++ b/t/t6131-pathspec-prefix.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +test_description='Verify the exact matching of prefix part in pathspec' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir a\* ab && + : >a\*/foo && + : >ab/foo && + git add . +' + +test_expect_success 'prefix has wildcards but treated as literal' ' + ( + cd a\* && + git ls-files --cached foo + ) >actual && + echo foo >expected && + test_cmp expected actual +' + +test_expect_success 'prefix is reduced to empty' ' + ( + cd a\* && + git ls-files --cached ../a\*/foo + ) >actual && + cat <<\EOF >expected && +foo +../ab/foo +EOF + test_cmp expected actual +' + +test_expect_success 'prefix is reduced to empty (2)' ' + ( + cd a\* && + git ls-files --cached non-existing//../fancy/.//../../a\*/foo + ) >actual && + cat <<\EOF >expected && +foo +../ab/foo +EOF + test_cmp expected actual +' + +test_done -- 1.8.0.rc2.23.g1fb49df -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html