Skip to content

Commit 5111520

Browse files
ttaylorrgitster
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. However, this requires first fixing some issues with how the path-walk API handles directly-requested trees _and_ trees requested through lightweight tags. These changes create substantial updates to t6601-path-walk.sh, which the previous change highlighted as a problem by tagging otherwise-unreachable trees and having them not appear in the output. 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> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 5c21575 commit 5111520

3 files changed

Lines changed: 152 additions & 70 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`. The `--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: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,18 @@ static int walk_path(struct path_walk_context *ctx,
390390
ctx->info->path_fn_data);
391391
}
392392

393-
/* Expand data for children. */
394-
if (list->type == OBJ_TREE) {
393+
/*
394+
* Expand tree children, except when the set is directly requested
395+
* _and_ we are otherwise filtering out trees.
396+
*/
397+
if (list->type == OBJ_TREE &&
398+
(!path_is_for_direct_objects(path) || ctx->info->trees)) {
399+
/* Use root path if expanding from tagged/direct trees. */
400+
const char *expand_path = !strcmp(path, "/tagged-trees")
401+
? root_path : path;
395402
for (size_t i = 0; i < list->oids.nr; i++) {
396403
ret |= add_tree_entries(ctx,
397-
path,
404+
expand_path,
398405
&list->oids.oid[i]);
399406
}
400407
}
@@ -442,12 +449,12 @@ static int setup_pending_objects(struct path_walk_info *info,
442449
{
443450
struct type_and_oid_list *tags = NULL;
444451
struct type_and_oid_list *tagged_blobs = NULL;
445-
struct type_and_oid_list *root_tree_list = NULL;
452+
struct type_and_oid_list *tagged_trees = NULL;
446453

447454
if (info->tags)
448455
CALLOC_ARRAY(tags, 1);
449456
CALLOC_ARRAY(tagged_blobs, 1);
450-
root_tree_list = strmap_get(&ctx->paths_to_lists, root_path);
457+
CALLOC_ARRAY(tagged_trees, 1);
451458

452459
/*
453460
* Pending objects include:
@@ -491,14 +498,15 @@ static int setup_pending_objects(struct path_walk_info *info,
491498

492499
switch (obj->type) {
493500
case OBJ_TREE:
494-
if (pending->path) {
495-
char *path = *pending->path ? xstrfmt("%s/", pending->path)
496-
: xstrdup("");
501+
if (pending->path && *pending->path) {
502+
char *path = xstrfmt("%s/", pending->path);
497503
add_path_to_list(ctx, path, OBJ_TREE, &obj->oid, 1);
498504
free(path);
505+
} else if (!pending->path || !info->trees) {
506+
oid_array_append(&tagged_trees->oids, &obj->oid);
499507
} else {
500-
/* assume a root tree, such as a lightweight tag. */
501-
oid_array_append(&root_tree_list->oids, &obj->oid);
508+
add_path_to_list(ctx, root_path, OBJ_TREE,
509+
&obj->oid, 1);
502510
}
503511
break;
504512

@@ -535,6 +543,18 @@ static int setup_pending_objects(struct path_walk_info *info,
535543
free(tagged_blobs);
536544
}
537545
}
546+
if (tagged_trees) {
547+
if (tagged_trees->oids.nr) {
548+
const char *tagged_tree_path = "/tagged-trees";
549+
tagged_trees->type = OBJ_TREE;
550+
tagged_trees->maybe_interesting = 1;
551+
strmap_put(&ctx->paths_to_lists, tagged_tree_path, tagged_trees);
552+
push_to_stack(ctx, tagged_tree_path);
553+
} else {
554+
oid_array_clear(&tagged_trees->oids);
555+
free(tagged_trees);
556+
}
557+
}
538558
if (tags) {
539559
if (tags->oids.nr) {
540560
const char *tag_path = "/tags";
@@ -575,6 +595,19 @@ static int prepare_filters(struct path_walk_info *info,
575595
}
576596
return 1;
577597

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

t/t6601-path-walk.sh

Lines changed: 107 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,23 @@ test_expect_success 'all' '
7777
3:tree::$(git rev-parse base^{tree})
7878
3:tree::$(git rev-parse base~1^{tree})
7979
3:tree::$(git rev-parse base~2^{tree})
80-
3:tree::$(git rev-parse refs/tags/tree-tag^{})
81-
3:tree::$(git rev-parse refs/tags/tree-tag2^{})
8280
4:blob:a:$(git rev-parse base~2:a)
83-
5:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
84-
6:tree:a/:$(git rev-parse base:a)
85-
7:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
86-
8:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file)
87-
9:tree:left/:$(git rev-parse base:left)
88-
9:tree:left/:$(git rev-parse base~2:left)
89-
10:blob:left/b:$(git rev-parse base~2:left/b)
90-
10:blob:left/b:$(git rev-parse base:left/b)
91-
11:tree:right/:$(git rev-parse topic:right)
92-
11:tree:right/:$(git rev-parse base~1:right)
93-
11:tree:right/:$(git rev-parse base~2:right)
94-
12:blob:right/c:$(git rev-parse base~2:right/c)
95-
12:blob:right/c:$(git rev-parse topic:right/c)
96-
13:blob:right/d:$(git rev-parse base~1:right/d)
81+
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
82+
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
83+
6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
84+
7:tree:a/:$(git rev-parse base:a)
85+
8:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
86+
9:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file)
87+
10:tree:left/:$(git rev-parse base:left)
88+
10:tree:left/:$(git rev-parse base~2:left)
89+
11:blob:left/b:$(git rev-parse base~2:left/b)
90+
11:blob:left/b:$(git rev-parse base:left/b)
91+
12:tree:right/:$(git rev-parse topic:right)
92+
12:tree:right/:$(git rev-parse base~1:right)
93+
12:tree:right/:$(git rev-parse base~2:right)
94+
13:blob:right/c:$(git rev-parse base~2:right/c)
95+
13:blob:right/c:$(git rev-parse topic:right/c)
96+
14:blob:right/d:$(git rev-parse base~1:right/d)
9797
blobs:10
9898
commits:4
9999
tags:7
@@ -471,15 +471,15 @@ test_expect_success 'all, blob:none filter' '
471471
3:tree::$(git rev-parse base^{tree})
472472
3:tree::$(git rev-parse base~1^{tree})
473473
3:tree::$(git rev-parse base~2^{tree})
474-
3:tree::$(git rev-parse refs/tags/tree-tag^{})
475-
3:tree::$(git rev-parse refs/tags/tree-tag2^{})
476-
4:tree:a/:$(git rev-parse base:a)
477-
5:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
478-
6:tree:left/:$(git rev-parse base:left)
479-
6:tree:left/:$(git rev-parse base~2:left)
480-
7:tree:right/:$(git rev-parse topic:right)
481-
7:tree:right/:$(git rev-parse base~1:right)
482-
7:tree:right/:$(git rev-parse base~2:right)
474+
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
475+
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
476+
5:tree:a/:$(git rev-parse base:a)
477+
6:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
478+
7:tree:left/:$(git rev-parse base:left)
479+
7:tree:left/:$(git rev-parse base~2:left)
480+
8:tree:right/:$(git rev-parse topic:right)
481+
8:tree:right/:$(git rev-parse base~1:right)
482+
8:tree:right/:$(git rev-parse base~2:right)
483483
blobs:2
484484
commits:4
485485
tags:7
@@ -533,15 +533,15 @@ test_expect_success 'all, blob:limit=0 filter' '
533533
3:tree::$(git rev-parse base^{tree})
534534
3:tree::$(git rev-parse base~1^{tree})
535535
3:tree::$(git rev-parse base~2^{tree})
536-
3:tree::$(git rev-parse refs/tags/tree-tag^{})
537-
3:tree::$(git rev-parse refs/tags/tree-tag2^{})
538-
4:tree:a/:$(git rev-parse base:a)
539-
5:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
540-
6:tree:left/:$(git rev-parse base:left)
541-
6:tree:left/:$(git rev-parse base~2:left)
542-
7:tree:right/:$(git rev-parse topic:right)
543-
7:tree:right/:$(git rev-parse base~1:right)
544-
7:tree:right/:$(git rev-parse base~2:right)
536+
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
537+
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
538+
5:tree:a/:$(git rev-parse base:a)
539+
6:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
540+
7:tree:left/:$(git rev-parse base:left)
541+
7:tree:left/:$(git rev-parse base~2:left)
542+
8:tree:right/:$(git rev-parse topic:right)
543+
8:tree:right/:$(git rev-parse base~1:right)
544+
8:tree:right/:$(git rev-parse base~2:right)
545545
blobs:2
546546
commits:4
547547
tags:7
@@ -572,19 +572,19 @@ test_expect_success 'all, blob:limit=3 filter' '
572572
3:tree::$(git rev-parse base^{tree})
573573
3:tree::$(git rev-parse base~1^{tree})
574574
3:tree::$(git rev-parse base~2^{tree})
575-
3:tree::$(git rev-parse refs/tags/tree-tag^{})
576-
3:tree::$(git rev-parse refs/tags/tree-tag2^{})
577575
4:blob:a:$(git rev-parse base~2:a)
578-
5:tree:a/:$(git rev-parse base:a)
579-
6:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
580-
7:tree:left/:$(git rev-parse base:left)
581-
7:tree:left/:$(git rev-parse base~2:left)
582-
8:blob:left/b:$(git rev-parse base~2:left/b)
583-
9:tree:right/:$(git rev-parse topic:right)
584-
9:tree:right/:$(git rev-parse base~1:right)
585-
9:tree:right/:$(git rev-parse base~2:right)
586-
10:blob:right/c:$(git rev-parse base~2:right/c)
587-
11:blob:right/d:$(git rev-parse base~1:right/d)
576+
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
577+
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
578+
6:tree:a/:$(git rev-parse base:a)
579+
7:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
580+
8:tree:left/:$(git rev-parse base:left)
581+
8:tree:left/:$(git rev-parse base~2:left)
582+
9:blob:left/b:$(git rev-parse base~2:left/b)
583+
10:tree:right/:$(git rev-parse topic:right)
584+
10:tree:right/:$(git rev-parse base~1:right)
585+
10:tree:right/:$(git rev-parse base~2:right)
586+
11:blob:right/c:$(git rev-parse base~2:right/c)
587+
12:blob:right/d:$(git rev-parse base~1:right/d)
588588
blobs:6
589589
commits:4
590590
tags:7
@@ -594,6 +594,55 @@ test_expect_success 'all, blob:limit=3 filter' '
594594
test_cmp_sorted expect out
595595
'
596596

597+
test_expect_success 'all, tree:0 filter' '
598+
test-tool path-walk --filter=tree:0 -- --all >out &&
599+
600+
cat >expect <<-EOF &&
601+
0:commit::$(git rev-parse topic)
602+
0:commit::$(git rev-parse base)
603+
0:commit::$(git rev-parse base~1)
604+
0:commit::$(git rev-parse base~2)
605+
1:tag:/tags:$(git rev-parse refs/tags/first)
606+
1:tag:/tags:$(git rev-parse refs/tags/second.1)
607+
1:tag:/tags:$(git rev-parse refs/tags/second.2)
608+
1:tag:/tags:$(git rev-parse refs/tags/third)
609+
1:tag:/tags:$(git rev-parse refs/tags/fourth)
610+
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
611+
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
612+
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
613+
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
614+
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{tree})
615+
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2)
616+
blobs:2
617+
commits:4
618+
tags:7
619+
trees:2
620+
EOF
621+
622+
test_cmp_sorted expect out
623+
'
624+
625+
test_expect_success 'topic only, tree:0 filter' '
626+
test-tool path-walk --filter=tree:0 -- topic >out &&
627+
628+
cat >expect <<-EOF &&
629+
0:commit::$(git rev-parse topic)
630+
0:commit::$(git rev-parse base~1)
631+
0:commit::$(git rev-parse base~2)
632+
blobs:0
633+
commits:3
634+
tags:0
635+
trees:0
636+
EOF
637+
638+
test_cmp_sorted expect out
639+
'
640+
641+
test_expect_success 'tree:1 filter is rejected' '
642+
test_must_fail test-tool path-walk --filter=tree:1 -- --all 2>err &&
643+
test_grep "tree:1 filter not supported by the path-walk API" err
644+
'
645+
597646
test_expect_success 'setup sparse filter blob' '
598647
# Cone-mode patterns: include root, exclude all dirs, include left/
599648
cat >patterns <<-\EOF &&
@@ -625,19 +674,19 @@ test_expect_success 'all, sparse:oid filter' '
625674
3:tree::$(git rev-parse base^{tree})
626675
3:tree::$(git rev-parse base~1^{tree})
627676
3:tree::$(git rev-parse base~2^{tree})
628-
3:tree::$(git rev-parse refs/tags/tree-tag^{})
629-
3:tree::$(git rev-parse refs/tags/tree-tag2^{})
630677
4:blob:a:$(git rev-parse base~2:a)
631-
5:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
632-
6:tree:a/:$(git rev-parse base:a)
633-
7:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
634-
8:tree:left/:$(git rev-parse base:left)
635-
8:tree:left/:$(git rev-parse base~2:left)
636-
9:blob:left/b:$(git rev-parse base~2:left/b)
637-
9:blob:left/b:$(git rev-parse base:left/b)
638-
10:tree:right/:$(git rev-parse topic:right)
639-
10:tree:right/:$(git rev-parse base~1:right)
640-
10:tree:right/:$(git rev-parse base~2:right)
678+
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
679+
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
680+
6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
681+
7:tree:a/:$(git rev-parse base:a)
682+
8:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
683+
9:tree:left/:$(git rev-parse base:left)
684+
9:tree:left/:$(git rev-parse base~2:left)
685+
10:blob:left/b:$(git rev-parse base~2:left/b)
686+
10:blob:left/b:$(git rev-parse base:left/b)
687+
11:tree:right/:$(git rev-parse topic:right)
688+
11:tree:right/:$(git rev-parse base~1:right)
689+
11:tree:right/:$(git rev-parse base~2:right)
641690
blobs:6
642691
commits:4
643692
tags:7

0 commit comments

Comments
 (0)