dir.c: accept a directory as part of cone-mode patterns

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>

Derrick Stolee committed Jul 14, 2021 at 13:12 UTC 69bdbdb0ee25bd64bd3ad2fe241d434442e96f75
1 file changed +19 -5
dir.c
+19 -5
@@ -1376,7 +1376,7 @@ enum pattern_match_result path_matches_pattern_list(
1376 struct path_pattern *pattern;
1377 struct strbuf parent_pathname = STRBUF_INIT;
1378 int result = NOT_MATCHED;
1379 - const char *slash_pos;
1379 + size_t slash_pos;
1380
1381 if (!pl->use_cone_patterns) {
1382 pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
@@ -1397,21 +1397,35 @@ enum pattern_match_result path_matches_pattern_list(
1397 strbuf_addch(&parent_pathname, '/');
1398 strbuf_add(&parent_pathname, pathname, pathlen);
1399
1400 + /*
1401 + * Directory entries are matched if and only if a file
1402 + * contained immediately within them is matched. For the
1403 + * case of a directory entry, modify the path to create
1404 + * a fake filename within this directory, allowing us to
1405 + * use the file-base matching logic in an equivalent way.
1406 + */
1407 + if (parent_pathname.len > 0 &&
1408 + parent_pathname.buf[parent_pathname.len - 1] == '/') {
1409 + slash_pos = parent_pathname.len - 1;
1410 + strbuf_add(&parent_pathname, "-", 1);
1411 + } else {
1412 + const char *slash_ptr = strrchr(parent_pathname.buf, '/');
1413 + slash_pos = slash_ptr ? slash_ptr - parent_pathname.buf : 0;
1414 + }
1415 +
1416 if (hashmap_contains_path(&pl->recursive_hashmap,
1417 &parent_pathname)) {
1418 result = MATCHED_RECURSIVE;
1419 goto done;
1420 }
1421
1406 - slash_pos = strrchr(parent_pathname.buf, '/');
1407 -
1408 - if (slash_pos == parent_pathname.buf) {
1422 + if (!slash_pos) {
1423 /* include every file in root */
1424 result = MATCHED;
1425 goto done;
1426 }
1427
1414 - strbuf_setlen(&parent_pathname, slash_pos - parent_pathname.buf);
1428 + strbuf_setlen(&parent_pathname, slash_pos);
1429
1430 if (hashmap_contains_path(&pl->parent_hashmap, &parent_pathname)) {
1431 result = MATCHED;