]> git.ipfire.org Git - thirdparty/git.git/commitdiff
dir.c: accept a directory as part of cone-mode patterns
authorDerrick Stolee <dstolee@microsoft.com>
Wed, 14 Jul 2021 13:12:34 +0000 (13:12 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Jul 2021 20:42:49 +0000 (13:42 -0700)
When we have sparse directory entries in the index, we want to compare
that directory against sparse-checkout patterns. Those pattern matching
algorithms are built expecting a file path, not a directory path. This
is especially important in the "cone mode" patterns which will match
files that exist within the "parent directories" as well as the
recursive directory matches.

If path_matches_pattern_list() is given a directory, we can add a fake
filename ("-") to the directory and get the same results as before,
assuming we are in cone mode. Since sparse index requires cone mode
patterns, this is an acceptable assumption.

Reviewed-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dir.c

diff --git a/dir.c b/dir.c
index ebe5ec046e050683443507495b3dcfa903c420e4..0c5264b3b20a2ff00bc064fd2682c26babd465d0 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1376,7 +1376,7 @@ enum pattern_match_result path_matches_pattern_list(
        struct path_pattern *pattern;
        struct strbuf parent_pathname = STRBUF_INIT;
        int result = NOT_MATCHED;
-       const char *slash_pos;
+       size_t slash_pos;
 
        if (!pl->use_cone_patterns) {
                pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
@@ -1397,21 +1397,35 @@ enum pattern_match_result path_matches_pattern_list(
        strbuf_addch(&parent_pathname, '/');
        strbuf_add(&parent_pathname, pathname, pathlen);
 
+       /*
+        * Directory entries are matched if and only if a file
+        * contained immediately within them is matched. For the
+        * case of a directory entry, modify the path to create
+        * a fake filename within this directory, allowing us to
+        * use the file-base matching logic in an equivalent way.
+        */
+       if (parent_pathname.len > 0 &&
+           parent_pathname.buf[parent_pathname.len - 1] == '/') {
+               slash_pos = parent_pathname.len - 1;
+               strbuf_add(&parent_pathname, "-", 1);
+       } else {
+               const char *slash_ptr = strrchr(parent_pathname.buf, '/');
+               slash_pos = slash_ptr ? slash_ptr - parent_pathname.buf : 0;
+       }
+
        if (hashmap_contains_path(&pl->recursive_hashmap,
                                  &parent_pathname)) {
                result = MATCHED_RECURSIVE;
                goto done;
        }
 
-       slash_pos = strrchr(parent_pathname.buf, '/');
-
-       if (slash_pos == parent_pathname.buf) {
+       if (!slash_pos) {
                /* include every file in root */
                result = MATCHED;
                goto done;
        }
 
-       strbuf_setlen(&parent_pathname, slash_pos - parent_pathname.buf);
+       strbuf_setlen(&parent_pathname, slash_pos);
 
        if (hashmap_contains_path(&pl->parent_hashmap, &parent_pathname)) {
                result = MATCHED;