]> git.ipfire.org Git - thirdparty/git.git/commitdiff
path-walk: support wildcard pathspecs for blob filtering
authorDerrick Stolee <stolee@gmail.com>
Thu, 26 Mar 2026 15:14:53 +0000 (15:14 +0000)
committerJunio C Hamano <gitster@pobox.com>
Thu, 26 Mar 2026 16:38:07 +0000 (09:38 -0700)
Previously, walk_objects_by_path() silently ignored pathspecs containing
wildcards or magic by clearing them. This caused all blobs to be
downloaded regardless of the given pathspec. Wildcard pathspecs like
"d/file.*.txt" are useful for narrowing which blobs to process (e.g.,
during 'git backfill').

Support wildcard pathspecs by making two changes:

 1. Add an 'exact_pathspecs' flag to path_walk_context. When the
    pathspec has no wildcards or magic, set this flag and use the
    existing fast-path prefix matching in add_tree_entries(). When
    wildcards are present, skip that block since prefix matching
    cannot handle glob patterns.

 2. Add a match_pathspec() check in walk_path() to filter out blobs
    whose full path does not match the pathspec. This provides the
    actual blob-level filtering for wildcard pathspecs.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
path-walk.c
t/t5620-backfill.sh

index 3750552978655e5b8ec17063c8f79287405a9e18..2aa3e7d8a4133d5c7bdd9f46b374f49fa35e0654 100644 (file)
@@ -63,6 +63,8 @@ struct path_walk_context {
         */
        struct prio_queue path_stack;
        struct strset path_stack_pushed;
+
+       unsigned exact_pathspecs:1;
 };
 
 static int compare_by_type(const void *one, const void *two, void *cb_data)
@@ -207,7 +209,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
                                 match != MATCHED)
                                continue;
                }
-               if (ctx->revs->prune_data.nr) {
+               if (ctx->revs->prune_data.nr && ctx->exact_pathspecs) {
                        struct pathspec *pd = &ctx->revs->prune_data;
                        bool found = false;
                        int did_strip_suffix = strbuf_strip_suffix(&path, "/");
@@ -302,6 +304,13 @@ static int walk_path(struct path_walk_context *ctx,
                        return 0;
        }
 
+       if (list->type == OBJ_BLOB &&
+           ctx->revs->prune_data.nr &&
+           !match_pathspec(ctx->repo->index, &ctx->revs->prune_data,
+                          path, strlen(path), 0,
+                          NULL, 0))
+               return 0;
+
        /* Evaluate function pointer on this data, if requested. */
        if ((list->type == OBJ_TREE && ctx->info->trees) ||
            (list->type == OBJ_BLOB && ctx->info->blobs) ||
@@ -510,14 +519,9 @@ int walk_objects_by_path(struct path_walk_info *info)
                info->revs->tag_objects = 1;
 
        if (ctx.revs->prune_data.nr) {
-               /*
-                * Only exact prefix pathspecs are currently supported.
-                * Clear any wildcard or magic pathspecs to avoid
-                * incorrect prefix matching.
-                */
-               if (ctx.revs->prune_data.has_wildcard ||
-                   ctx.revs->prune_data.magic)
-                       clear_pathspec(&ctx.revs->prune_data);
+               if (!ctx.revs->prune_data.has_wildcard &&
+                   !ctx.revs->prune_data.magic)
+                       ctx.exact_pathspecs = 1;
        }
 
        /* Insert a single list for the root tree into the paths. */
index 52f6484ca18e553a6c380deea2cfeef40bde66f8..c6f54ee91ccc6a1fad5c5370df9ab02746d04120 100755 (executable)
@@ -307,12 +307,11 @@ test_expect_success 'backfill with wildcard pathspec' '
        git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing &&
        test_line_count = 48 missing &&
 
-       # TODO: The wildcard pathspec should limit downloaded blobs,
-       # but currently all blobs are downloaded.
-       git -C backfill-path backfill HEAD -- "d/file.*.txt" &&
+       git -C backfill-path backfill HEAD -- "d/file.*.txt" 2>err &&
+       test_must_be_empty err &&
 
        git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing &&
-       test_line_count = 0 missing
+       test_line_count = 40 missing
 '
 
 test_expect_success 'backfill with --all' '