Skip to content

Commit c9efff0

Browse files
ttaylorrderrickstolee
authored andcommitted
path-walk: support tree:0 filter
The `tree:0` object filter omits all trees and blobs from the result, keeping only commits and tags. Consequently, this filter type should has a fairly straightforward integration with path-walk, as the decision to include an object depends only on its type and does not depend on any path-sensitive state. Mapping it onto `path_walk_info` is direct: set `info->trees = 0` and `info->blobs = 0` in `prepare_filters()` when the `LOFC_TREE_DEPTH` choice is requested with depth zero. The existing code already plumbs those flags through the rest of the walk: - 'walk_objects_by_path()' sets `revs->blob_objects = info->blobs` and `revs->tree_objects = info->trees` before `prepare_revision_walk()`, so the revision walk doesn't try to enumerate trees or blobs itself. - The commit-walk loop short-circuits the root-tree fetch with "if (!info->trees && !info->blobs) continue;", so we never even look up the root tree, let alone descend into it. - `setup_pending_objects()` skips pending trees and blobs based on the same flags. This means the path-walk doesn't allocate or expand any tree structures at all under `tree:0`, which matches the intended behavior of the filter. Non-zero tree-depth filters are not supported. Those depend on the depth at which a tree is visited, which is a path-walk concept the filter machinery doesn't currently share with the path-walk API. Reject them in `prepare_filters()` with a helpful error and let pack-objects fall back to the regular traversal, the same way it already does for unsupported filters. Add coverage in t6601 for both `--all` and a single-branch case to confirm that no trees or blobs are emitted, and a separate test that `tree:1` is rejected with the expected error message. Place the new tests before "setup sparse filter blob" so they run on the original set of refs, before the orphan branch that the sparse-tree tests create. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Derrick Stolee <stolee@gmail.com>
1 parent d9f5a98 commit c9efff0

3 files changed

Lines changed: 90 additions & 5 deletions

File tree

Documentation/git-pack-objects.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,8 @@ will be automatically changed to version `1`.
404404
+
405405
Incompatible with `--delta-islands`. The `--use-bitmap-index` option is
406406
ignored in the presence of `--path-walk`. Whe `--path-walk` option
407-
supports the `--filter=<spec>` forms `blob:none`, `blob:limit=<n>`, and
408-
`sparse:<oid>`.
407+
supports the `--filter=<spec>` forms `blob:none`, `blob:limit=<n>`,
408+
`tree:0`, and `sparse:<oid>`.
409409
410410
411411
DELTA ISLANDS

path-walk.c

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ static int walk_path(struct path_walk_context *ctx,
389389
ctx->info->path_fn_data);
390390
}
391391

392-
/* Expand data for children. */
393-
if (list->type == OBJ_TREE) {
392+
/* Expand data for children, unless this is a direct-object path. */
393+
if (list->type == OBJ_TREE && !path_is_for_direct_objects(path)) {
394394
for (size_t i = 0; i < list->oids.nr; i++) {
395395
ret |= add_tree_entries(ctx,
396396
path,
@@ -441,11 +441,14 @@ static int setup_pending_objects(struct path_walk_info *info,
441441
{
442442
struct type_and_oid_list *tags = NULL;
443443
struct type_and_oid_list *tagged_blobs = NULL;
444+
struct type_and_oid_list *tagged_trees = NULL;
444445
struct type_and_oid_list *root_tree_list = NULL;
445446

446447
if (info->tags)
447448
CALLOC_ARRAY(tags, 1);
448449
CALLOC_ARRAY(tagged_blobs, 1);
450+
if (!info->trees)
451+
CALLOC_ARRAY(tagged_trees, 1);
449452
root_tree_list = strmap_get(&ctx->paths_to_lists, root_path);
450453

451454
/*
@@ -490,7 +493,15 @@ static int setup_pending_objects(struct path_walk_info *info,
490493

491494
switch (obj->type) {
492495
case OBJ_TREE:
493-
if (pending->path) {
496+
if (tagged_trees) {
497+
/*
498+
* Trees are disabled but pending trees
499+
* should still be emitted. Collect them
500+
* into a "/tagged-trees" list that
501+
* bypasses the object type filter.
502+
*/
503+
oid_array_append(&tagged_trees->oids, &obj->oid);
504+
} else if (pending->path) {
494505
char *path = *pending->path ? xstrfmt("%s/", pending->path)
495506
: xstrdup("");
496507
add_path_to_list(ctx, path, OBJ_TREE, &obj->oid, 1);
@@ -534,6 +545,18 @@ static int setup_pending_objects(struct path_walk_info *info,
534545
free(tagged_blobs);
535546
}
536547
}
548+
if (tagged_trees) {
549+
if (tagged_trees->oids.nr) {
550+
const char *tagged_tree_path = "/tagged-trees";
551+
tagged_trees->type = OBJ_TREE;
552+
tagged_trees->maybe_interesting = 1;
553+
strmap_put(&ctx->paths_to_lists, tagged_tree_path, tagged_trees);
554+
push_to_stack(ctx, tagged_tree_path);
555+
} else {
556+
oid_array_clear(&tagged_trees->oids);
557+
free(tagged_trees);
558+
}
559+
}
537560
if (tags) {
538561
if (tags->oids.nr) {
539562
const char *tag_path = "/tags";
@@ -574,6 +597,19 @@ static int prepare_filters(struct path_walk_info *info,
574597
}
575598
return 1;
576599

600+
case LOFC_TREE_DEPTH:
601+
if (options->tree_exclude_depth) {
602+
error(_("tree:%lu filter not supported by the path-walk API"),
603+
options->tree_exclude_depth);
604+
return 0;
605+
}
606+
if (info) {
607+
info->trees = 0;
608+
info->blobs = 0;
609+
list_objects_filter_release(options);
610+
}
611+
return 1;
612+
577613
case LOFC_SPARSE_OID:
578614
if (info) {
579615
struct object_id sparse_oid;

t/t6601-path-walk.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,55 @@ test_expect_success 'all, blob:limit=3 filter' '
596596
test_cmp_sorted expect out
597597
'
598598

599+
test_expect_success 'all, tree:0 filter' '
600+
test-tool path-walk --filter=tree:0 -- --all >out &&
601+
602+
cat >expect <<-EOF &&
603+
0:commit::$(git rev-parse topic)
604+
0:commit::$(git rev-parse base)
605+
0:commit::$(git rev-parse base~1)
606+
0:commit::$(git rev-parse base~2)
607+
1:tag:/tags:$(git rev-parse refs/tags/first)
608+
1:tag:/tags:$(git rev-parse refs/tags/second.1)
609+
1:tag:/tags:$(git rev-parse refs/tags/second.2)
610+
1:tag:/tags:$(git rev-parse refs/tags/third)
611+
1:tag:/tags:$(git rev-parse refs/tags/fourth)
612+
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
613+
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
614+
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
615+
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
616+
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{tree})
617+
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2)
618+
blobs:2
619+
commits:4
620+
tags:7
621+
trees:2
622+
EOF
623+
624+
test_cmp_sorted expect out
625+
'
626+
627+
test_expect_success 'topic only, tree:0 filter' '
628+
test-tool path-walk --filter=tree:0 -- topic >out &&
629+
630+
cat >expect <<-EOF &&
631+
0:commit::$(git rev-parse topic)
632+
0:commit::$(git rev-parse base~1)
633+
0:commit::$(git rev-parse base~2)
634+
blobs:0
635+
commits:3
636+
tags:0
637+
trees:0
638+
EOF
639+
640+
test_cmp_sorted expect out
641+
'
642+
643+
test_expect_success 'tree:1 filter is rejected' '
644+
test_must_fail test-tool path-walk --filter=tree:1 -- --all 2>err &&
645+
test_grep "tree:1 filter not supported by the path-walk API" err
646+
'
647+
599648
test_expect_success 'setup sparse filter blob' '
600649
# Cone-mode patterns: include root, exclude all dirs, include left/
601650
cat >patterns <<-\EOF &&

0 commit comments

Comments
 (0)