]> git.ipfire.org Git - thirdparty/git.git/commitdiff
sparse-checkout: properly match escaped characters
authorDerrick Stolee <dstolee@microsoft.com>
Fri, 31 Jan 2020 20:16:09 +0000 (20:16 +0000)
committerJunio C Hamano <gitster@pobox.com>
Fri, 31 Jan 2020 21:05:29 +0000 (13:05 -0800)
In cone mode, the sparse-checkout feature uses hashset containment
queries to match paths. Make this algorithm respect escaped asterisk
(*) and backslash (\) characters.

Create dup_and_filter_pattern() method to convert a pattern by
removing escape characters and dropping an optional "/*" at the end.
This method is available in dir.h as we will use it in
builtin/sparse-checkout.c in a later change.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dir.c
t/t1091-sparse-checkout-builtin.sh

diff --git a/dir.c b/dir.c
index 71d28331f35c1a2f27e42f7123a86fc2bd5ebdef..7ac0920b7135171a50c39a0912db35cf7c3fe68b 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -630,6 +630,36 @@ int pl_hashmap_cmp(const void *unused_cmp_data,
        return strncmp(ee1->pattern, ee2->pattern, min_len);
 }
 
+static char *dup_and_filter_pattern(const char *pattern)
+{
+       char *set, *read;
+       size_t count  = 0;
+       char *result = xstrdup(pattern);
+
+       set = result;
+       read = result;
+
+       while (*read) {
+               /* skip escape characters (once) */
+               if (*read == '\\')
+                       read++;
+
+               *set = *read;
+
+               set++;
+               read++;
+               count++;
+       }
+       *set = 0;
+
+       if (count > 2 &&
+           *(set - 1) == '*' &&
+           *(set - 2) == '/')
+               *(set - 2) = 0;
+
+       return result;
+}
+
 static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
 {
        struct pattern_entry *translated;
@@ -702,8 +732,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
                        goto clear_hashmaps;
                }
 
-               truncated = xstrdup(given->pattern);
-               truncated[given->patternlen - 2] = 0;
+               truncated = dup_and_filter_pattern(given->pattern);
 
                translated = xmalloc(sizeof(struct pattern_entry));
                translated->pattern = truncated;
@@ -737,7 +766,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
 
        translated = xmalloc(sizeof(struct pattern_entry));
 
-       translated->pattern = xstrdup(given->pattern);
+       translated->pattern = dup_and_filter_pattern(given->pattern);
        translated->patternlen = given->patternlen;
        hashmap_entry_init(&translated->ent,
                           ignore_case ?
index c732abeacde4d0a0f2fc5e918305773754c93bfa..9ea700896d831fc4ef537e747089bd84d04debb1 100755 (executable)
@@ -378,13 +378,28 @@ test_expect_success 'pattern-checks: contained glob characters' '
        done
 '
 
-test_expect_success 'pattern-checks: escaped "*"' '
-       cat >repo/.git/info/sparse-checkout <<-\EOF &&
+test_expect_success BSLASHPSPEC 'pattern-checks: escaped "*"' '
+       git clone repo escaped &&
+       TREEOID=$(git -C escaped rev-parse HEAD:folder1) &&
+       NEWTREE=$(git -C escaped mktree <<-EOF
+       $(git -C escaped ls-tree HEAD)
+       040000 tree $TREEOID    zbad\\dir
+       040000 tree $TREEOID    zdoes*exist
+       EOF
+       ) &&
+       COMMIT=$(git -C escaped commit-tree $NEWTREE -p HEAD) &&
+       git -C escaped reset --hard $COMMIT &&
+       check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" &&
+       git -C escaped sparse-checkout init --cone &&
+       cat >escaped/.git/info/sparse-checkout <<-\EOF &&
        /*
        !/*/
-       /does\*not\*exist/
+       /zbad\\dir/
+       !/zbad\\dir/*/
+       /zdoes\*not\*exist/
+       /zdoes\*exist/
        EOF
-       check_read_tree_errors repo "a" ""
+       check_read_tree_errors escaped "a zbad\\dir zdoes*exist"
 '
 
 test_done