diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc index e38853391b..c86219be91 100644 --- a/Documentation/git-pack-objects.adoc +++ b/Documentation/git-pack-objects.adoc @@ -404,8 +404,8 @@ will be automatically changed to version `1`. + Incompatible with `--delta-islands`. The `--use-bitmap-index` option is ignored in the presence of `--path-walk`. The `--path-walk` option -supports the `--filter=` forms `blob:none`, `blob:limit=`, and -`sparse:`. +supports the `--filter=` forms `blob:none`, `blob:limit=`, +`tree:0`, and `sparse:`. DELTA ISLANDS diff --git a/path-walk.c b/path-walk.c index ce38dcf1e9..cb67b8ce86 100644 --- a/path-walk.c +++ b/path-walk.c @@ -390,11 +390,18 @@ static int walk_path(struct path_walk_context *ctx, ctx->info->path_fn_data); } - /* Expand data for children. */ - if (list->type == OBJ_TREE) { + /* + * Expand tree children, except when the set is directly requested + * _and_ we are otherwise filtering out trees. + */ + if (list->type == OBJ_TREE && + (!path_is_for_direct_objects(path) || ctx->info->trees)) { + /* Use root path if expanding from tagged/direct trees. */ + const char *expand_path = !strcmp(path, "/tagged-trees") + ? root_path : path; for (size_t i = 0; i < list->oids.nr; i++) { ret |= add_tree_entries(ctx, - path, + expand_path, &list->oids.oid[i]); } } @@ -442,12 +449,12 @@ static int setup_pending_objects(struct path_walk_info *info, { struct type_and_oid_list *tags = NULL; struct type_and_oid_list *tagged_blobs = NULL; - struct type_and_oid_list *root_tree_list = NULL; + struct type_and_oid_list *tagged_trees = NULL; if (info->tags) CALLOC_ARRAY(tags, 1); CALLOC_ARRAY(tagged_blobs, 1); - root_tree_list = strmap_get(&ctx->paths_to_lists, root_path); + CALLOC_ARRAY(tagged_trees, 1); /* * Pending objects include: @@ -491,14 +498,15 @@ static int setup_pending_objects(struct path_walk_info *info, switch (obj->type) { case OBJ_TREE: - if (pending->path) { - char *path = *pending->path ? xstrfmt("%s/", pending->path) - : xstrdup(""); + if (pending->path && *pending->path) { + char *path = xstrfmt("%s/", pending->path); add_path_to_list(ctx, path, OBJ_TREE, &obj->oid, 1); free(path); + } else if (!pending->path || !info->trees) { + oid_array_append(&tagged_trees->oids, &obj->oid); } else { - /* assume a root tree, such as a lightweight tag. */ - oid_array_append(&root_tree_list->oids, &obj->oid); + add_path_to_list(ctx, root_path, OBJ_TREE, + &obj->oid, 1); } break; @@ -535,6 +543,18 @@ static int setup_pending_objects(struct path_walk_info *info, free(tagged_blobs); } } + if (tagged_trees) { + if (tagged_trees->oids.nr) { + const char *tagged_tree_path = "/tagged-trees"; + tagged_trees->type = OBJ_TREE; + tagged_trees->maybe_interesting = 1; + strmap_put(&ctx->paths_to_lists, tagged_tree_path, tagged_trees); + push_to_stack(ctx, tagged_tree_path); + } else { + oid_array_clear(&tagged_trees->oids); + free(tagged_trees); + } + } if (tags) { if (tags->oids.nr) { const char *tag_path = "/tags"; @@ -575,6 +595,19 @@ static int prepare_filters(struct path_walk_info *info, } return 1; + case LOFC_TREE_DEPTH: + if (options->tree_exclude_depth) { + error(_("tree:%lu filter not supported by the path-walk API"), + options->tree_exclude_depth); + return 0; + } + if (info) { + info->trees = 0; + info->blobs = 0; + list_objects_filter_release(options); + } + return 1; + case LOFC_SPARSE_OID: if (info) { struct object_id sparse_oid; diff --git a/t/t6601-path-walk.sh b/t/t6601-path-walk.sh index 92c524d145..566db7c7e3 100755 --- a/t/t6601-path-walk.sh +++ b/t/t6601-path-walk.sh @@ -77,23 +77,23 @@ test_expect_success 'all' ' 3:tree::$(git rev-parse base^{tree}) 3:tree::$(git rev-parse base~1^{tree}) 3:tree::$(git rev-parse base~2^{tree}) - 3:tree::$(git rev-parse refs/tags/tree-tag^{}) - 3:tree::$(git rev-parse refs/tags/tree-tag2^{}) 4:blob:a:$(git rev-parse base~2:a) - 5:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2) - 6:tree:a/:$(git rev-parse base:a) - 7:tree:child/:$(git rev-parse refs/tags/tree-tag:child) - 8:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file) - 9:tree:left/:$(git rev-parse base:left) - 9:tree:left/:$(git rev-parse base~2:left) - 10:blob:left/b:$(git rev-parse base~2:left/b) - 10:blob:left/b:$(git rev-parse base:left/b) - 11:tree:right/:$(git rev-parse topic:right) - 11:tree:right/:$(git rev-parse base~1:right) - 11:tree:right/:$(git rev-parse base~2:right) - 12:blob:right/c:$(git rev-parse base~2:right/c) - 12:blob:right/c:$(git rev-parse topic:right/c) - 13:blob:right/d:$(git rev-parse base~1:right/d) + 5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{}) + 5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{}) + 6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2) + 7:tree:a/:$(git rev-parse base:a) + 8:tree:child/:$(git rev-parse refs/tags/tree-tag:child) + 9:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file) + 10:tree:left/:$(git rev-parse base:left) + 10:tree:left/:$(git rev-parse base~2:left) + 11:blob:left/b:$(git rev-parse base~2:left/b) + 11:blob:left/b:$(git rev-parse base:left/b) + 12:tree:right/:$(git rev-parse topic:right) + 12:tree:right/:$(git rev-parse base~1:right) + 12:tree:right/:$(git rev-parse base~2:right) + 13:blob:right/c:$(git rev-parse base~2:right/c) + 13:blob:right/c:$(git rev-parse topic:right/c) + 14:blob:right/d:$(git rev-parse base~1:right/d) blobs:10 commits:4 tags:7 @@ -471,15 +471,15 @@ test_expect_success 'all, blob:none filter' ' 3:tree::$(git rev-parse base^{tree}) 3:tree::$(git rev-parse base~1^{tree}) 3:tree::$(git rev-parse base~2^{tree}) - 3:tree::$(git rev-parse refs/tags/tree-tag^{}) - 3:tree::$(git rev-parse refs/tags/tree-tag2^{}) - 4:tree:a/:$(git rev-parse base:a) - 5:tree:child/:$(git rev-parse refs/tags/tree-tag:child) - 6:tree:left/:$(git rev-parse base:left) - 6:tree:left/:$(git rev-parse base~2:left) - 7:tree:right/:$(git rev-parse topic:right) - 7:tree:right/:$(git rev-parse base~1:right) - 7:tree:right/:$(git rev-parse base~2:right) + 4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{}) + 4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{}) + 5:tree:a/:$(git rev-parse base:a) + 6:tree:child/:$(git rev-parse refs/tags/tree-tag:child) + 7:tree:left/:$(git rev-parse base:left) + 7:tree:left/:$(git rev-parse base~2:left) + 8:tree:right/:$(git rev-parse topic:right) + 8:tree:right/:$(git rev-parse base~1:right) + 8:tree:right/:$(git rev-parse base~2:right) blobs:2 commits:4 tags:7 @@ -533,15 +533,15 @@ test_expect_success 'all, blob:limit=0 filter' ' 3:tree::$(git rev-parse base^{tree}) 3:tree::$(git rev-parse base~1^{tree}) 3:tree::$(git rev-parse base~2^{tree}) - 3:tree::$(git rev-parse refs/tags/tree-tag^{}) - 3:tree::$(git rev-parse refs/tags/tree-tag2^{}) - 4:tree:a/:$(git rev-parse base:a) - 5:tree:child/:$(git rev-parse refs/tags/tree-tag:child) - 6:tree:left/:$(git rev-parse base:left) - 6:tree:left/:$(git rev-parse base~2:left) - 7:tree:right/:$(git rev-parse topic:right) - 7:tree:right/:$(git rev-parse base~1:right) - 7:tree:right/:$(git rev-parse base~2:right) + 4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{}) + 4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{}) + 5:tree:a/:$(git rev-parse base:a) + 6:tree:child/:$(git rev-parse refs/tags/tree-tag:child) + 7:tree:left/:$(git rev-parse base:left) + 7:tree:left/:$(git rev-parse base~2:left) + 8:tree:right/:$(git rev-parse topic:right) + 8:tree:right/:$(git rev-parse base~1:right) + 8:tree:right/:$(git rev-parse base~2:right) blobs:2 commits:4 tags:7 @@ -572,19 +572,19 @@ test_expect_success 'all, blob:limit=3 filter' ' 3:tree::$(git rev-parse base^{tree}) 3:tree::$(git rev-parse base~1^{tree}) 3:tree::$(git rev-parse base~2^{tree}) - 3:tree::$(git rev-parse refs/tags/tree-tag^{}) - 3:tree::$(git rev-parse refs/tags/tree-tag2^{}) 4:blob:a:$(git rev-parse base~2:a) - 5:tree:a/:$(git rev-parse base:a) - 6:tree:child/:$(git rev-parse refs/tags/tree-tag:child) - 7:tree:left/:$(git rev-parse base:left) - 7:tree:left/:$(git rev-parse base~2:left) - 8:blob:left/b:$(git rev-parse base~2:left/b) - 9:tree:right/:$(git rev-parse topic:right) - 9:tree:right/:$(git rev-parse base~1:right) - 9:tree:right/:$(git rev-parse base~2:right) - 10:blob:right/c:$(git rev-parse base~2:right/c) - 11:blob:right/d:$(git rev-parse base~1:right/d) + 5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{}) + 5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{}) + 6:tree:a/:$(git rev-parse base:a) + 7:tree:child/:$(git rev-parse refs/tags/tree-tag:child) + 8:tree:left/:$(git rev-parse base:left) + 8:tree:left/:$(git rev-parse base~2:left) + 9:blob:left/b:$(git rev-parse base~2:left/b) + 10:tree:right/:$(git rev-parse topic:right) + 10:tree:right/:$(git rev-parse base~1:right) + 10:tree:right/:$(git rev-parse base~2:right) + 11:blob:right/c:$(git rev-parse base~2:right/c) + 12:blob:right/d:$(git rev-parse base~1:right/d) blobs:6 commits:4 tags:7 @@ -594,6 +594,55 @@ test_expect_success 'all, blob:limit=3 filter' ' test_cmp_sorted expect out ' +test_expect_success 'all, tree:0 filter' ' + test-tool path-walk --filter=tree:0 -- --all >out && + + cat >expect <<-EOF && + 0:commit::$(git rev-parse topic) + 0:commit::$(git rev-parse base) + 0:commit::$(git rev-parse base~1) + 0:commit::$(git rev-parse base~2) + 1:tag:/tags:$(git rev-parse refs/tags/first) + 1:tag:/tags:$(git rev-parse refs/tags/second.1) + 1:tag:/tags:$(git rev-parse refs/tags/second.2) + 1:tag:/tags:$(git rev-parse refs/tags/third) + 1:tag:/tags:$(git rev-parse refs/tags/fourth) + 1:tag:/tags:$(git rev-parse refs/tags/tree-tag) + 1:tag:/tags:$(git rev-parse refs/tags/blob-tag) + 2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{}) + 2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{}) + 3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{tree}) + 3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2) + blobs:2 + commits:4 + tags:7 + trees:2 + EOF + + test_cmp_sorted expect out +' + +test_expect_success 'topic only, tree:0 filter' ' + test-tool path-walk --filter=tree:0 -- topic >out && + + cat >expect <<-EOF && + 0:commit::$(git rev-parse topic) + 0:commit::$(git rev-parse base~1) + 0:commit::$(git rev-parse base~2) + blobs:0 + commits:3 + tags:0 + trees:0 + EOF + + test_cmp_sorted expect out +' + +test_expect_success 'tree:1 filter is rejected' ' + test_must_fail test-tool path-walk --filter=tree:1 -- --all 2>err && + test_grep "tree:1 filter not supported by the path-walk API" err +' + test_expect_success 'setup sparse filter blob' ' # Cone-mode patterns: include root, exclude all dirs, include left/ cat >patterns <<-\EOF && @@ -625,19 +674,19 @@ test_expect_success 'all, sparse:oid filter' ' 3:tree::$(git rev-parse base^{tree}) 3:tree::$(git rev-parse base~1^{tree}) 3:tree::$(git rev-parse base~2^{tree}) - 3:tree::$(git rev-parse refs/tags/tree-tag^{}) - 3:tree::$(git rev-parse refs/tags/tree-tag2^{}) 4:blob:a:$(git rev-parse base~2:a) - 5:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2) - 6:tree:a/:$(git rev-parse base:a) - 7:tree:child/:$(git rev-parse refs/tags/tree-tag:child) - 8:tree:left/:$(git rev-parse base:left) - 8:tree:left/:$(git rev-parse base~2:left) - 9:blob:left/b:$(git rev-parse base~2:left/b) - 9:blob:left/b:$(git rev-parse base:left/b) - 10:tree:right/:$(git rev-parse topic:right) - 10:tree:right/:$(git rev-parse base~1:right) - 10:tree:right/:$(git rev-parse base~2:right) + 5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{}) + 5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{}) + 6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2) + 7:tree:a/:$(git rev-parse base:a) + 8:tree:child/:$(git rev-parse refs/tags/tree-tag:child) + 9:tree:left/:$(git rev-parse base:left) + 9:tree:left/:$(git rev-parse base~2:left) + 10:blob:left/b:$(git rev-parse base~2:left/b) + 10:blob:left/b:$(git rev-parse base:left/b) + 11:tree:right/:$(git rev-parse topic:right) + 11:tree:right/:$(git rev-parse base~1:right) + 11:tree:right/:$(git rev-parse base~2:right) blobs:6 commits:4 tags:7