path-walk: add new 'edge_aggressive' option

In preparation for allowing both the --shallow and --path-walk options in the 'git pack-objects' builtin, create a new 'edge_aggressive' option in the path-walk API. This option will help walk the boundary more thoroughly and help avoid sending extra objects during fetches and pushes. The only use of the 'edge_hint_aggressive' option in the revision API is within mark_edges_uninteresting(), which is usually called before between prepare_revision_walk() and before visiting commits with get_revision(). In prepare_revision_walk(), the UNINTERESTING commits are walked until a boundary is found. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed May 16, 2025 at 18:12 UTC 4705889c3dfbb38f14c3569b489b4b2a00b4186a
5 files changed +42 -1
Documentation/technical/api-path-walk.adoc
+8
@@ -56,6 +56,14 @@ better off using the revision walk API instead.
56 the revision walk so that the walk emits commits marked with the
57 `UNINTERESTING` flag.
58
59 +`edge_aggressive`::
60 + For performance reasons, usually only the boundary commits are
61 + explored to find UNINTERESTING objects. However, in the case of
62 + shallow clones it can be helpful to mark all trees and blobs
63 + reachable from UNINTERESTING tip commits as UNINTERESTING. This
64 + matches the behavior of `--objects-edge-aggressive` in the
65 + revision API.
66 +
67 `pl`::
68 This pattern list pointer allows focusing the path-walk search to
69 a set of patterns, only emitting paths that match the given
path-walk.c
+5 -1
@@ -503,7 +503,11 @@ int walk_objects_by_path(struct path_walk_info *info)
503 if (prepare_revision_walk(info->revs))
504 die(_("failed to setup revision walk"));
505
506 - /* Walk trees to mark them as UNINTERESTING. */
506 + /*
507 + * Walk trees to mark them as UNINTERESTING.
508 + * This is particularly important when 'edge_aggressive' is set.
509 + */
510 + info->revs->edge_hint_aggressive = info->edge_aggressive;
511 edge_repo = info->revs->repo;
512 edge_tree_list = root_tree_list;
513 mark_edges_uninteresting(info->revs, show_edge,
path-walk.h
+7
@@ -50,6 +50,13 @@ struct path_walk_info {
50 */
51 int prune_all_uninteresting;
52
53 + /**
54 + * When 'edge_aggressive' is set, then the revision walk will use
55 + * the '--object-edge-aggressive' option to mark even more objects
56 + * as uninteresting.
57 + */
58 + int edge_aggressive;
59 +
60 /**
61 * Specify a sparse-checkout definition to match our paths to. Do not
62 * walk outside of this sparse definition. If the patterns are in
t/helper/test-path-walk.c
+2
@@ -82,6 +82,8 @@ int cmd__path_walk(int argc, const char **argv)
82 N_("toggle inclusion of tree objects")),
83 OPT_BOOL(0, "prune", &info.prune_all_uninteresting,
84 N_("toggle pruning of uninteresting paths")),
85 + OPT_BOOL(0, "edge-aggressive", &info.edge_aggressive,
86 + N_("toggle aggressive edge walk")),
87 OPT_BOOL(0, "stdin-pl", &stdin_pl,
88 N_("read a pattern list over stdin")),
89 OPT_END(),
t/t6601-path-walk.sh
+20
@@ -378,6 +378,26 @@ test_expect_success 'topic, not base, boundary with pruning' '
378 test_cmp_sorted expect out
379 '
380
381 +test_expect_success 'topic, not base, --edge-aggressive with pruning' '
382 + test-tool path-walk --prune --edge-aggressive -- topic --not base >out &&
383 +
384 + cat >expect <<-EOF &&
385 + 0:commit::$(git rev-parse topic)
386 + 1:tree::$(git rev-parse topic^{tree})
387 + 1:tree::$(git rev-parse base^{tree}):UNINTERESTING
388 + 2:tree:right/:$(git rev-parse topic:right)
389 + 2:tree:right/:$(git rev-parse base:right):UNINTERESTING
390 + 3:blob:right/c:$(git rev-parse base:right/c):UNINTERESTING
391 + 3:blob:right/c:$(git rev-parse topic:right/c)
392 + blobs:2
393 + commits:1
394 + tags:0
395 + trees:4
396 + EOF
397 +
398 + test_cmp_sorted expect out
399 +'
400 +
401 test_expect_success 'trees are reported exactly once' '
402 test_when_finished "rm -rf unique-trees" &&
403 test_create_repo unique-trees &&