Skip to content

Commit 230f831

Browse files
derrickstoleedscho
authored andcommitted
path-walk: add prune_all_uninteresting option
This option causes the path-walk API to act like the sparse tree-walk algorithm implemented by mark_trees_uninteresting_sparse() in list-objects.c. Starting from the commits marked as UNINTERESTING, their root trees and all objects reachable from those trees are UNINTERSTING, at least as we walk path-by-path. When we reach a path where all objects associated with that path are marked UNINTERESTING, then do no continue walking the children of that path. We need to be careful to pass the UNINTERESTING flag in a deep way on the UNINTERESTING objects before we start the path-walk, or else the depth-first search for the path-walk API may accidentally report some objects as interesting. Signed-off-by: Derrick Stolee <stolee@gmail.com>
1 parent a472302 commit 230f831

File tree

5 files changed

+120
-13
lines changed

5 files changed

+120
-13
lines changed

Documentation/technical/api-path-walk.txt

+8
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ commits are emitted.
5757
While it is possible to walk only commits in this way, consumers would be
5858
better off using the revision walk API instead.
5959

60+
`prune_all_uninteresting`::
61+
By default, all reachable paths are emitted by the path-walk API.
62+
This option allows consumers to declare that they are not
63+
interested in paths where all included objects are marked with the
64+
`UNINTERESTING` flag. This requires using the `boundary` option in
65+
the revision walk so that the walk emits commits marked with the
66+
`UNINTERESTING` flag.
67+
6068
Examples
6169
--------
6270

path-walk.c

+64-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct type_and_oid_list
2222
{
2323
enum object_type type;
2424
struct oid_array oids;
25+
int maybe_interesting;
2526
};
2627

2728
#define TYPE_AND_OID_LIST_INIT { \
@@ -124,6 +125,8 @@ static int add_children(struct path_walk_context *ctx,
124125
strmap_put(&ctx->paths_to_lists, path.buf, list);
125126
string_list_append(&ctx->path_stack, path.buf);
126127
}
128+
if (!(o->flags & UNINTERESTING))
129+
list->maybe_interesting = 1;
127130
oid_array_append(&list->oids, &entry.oid);
128131
}
129132

@@ -145,6 +148,40 @@ static int walk_path(struct path_walk_context *ctx,
145148

146149
list = strmap_get(&ctx->paths_to_lists, path);
147150

151+
if (ctx->info->prune_all_uninteresting) {
152+
/*
153+
* This is true if all objects were UNINTERESTING
154+
* when added to the list.
155+
*/
156+
if (!list->maybe_interesting)
157+
return 0;
158+
159+
/*
160+
* But it's still possible that the objects were set
161+
* as UNINTERESTING after being added. Do a quick check.
162+
*/
163+
list->maybe_interesting = 0;
164+
for (size_t i = 0;
165+
!list->maybe_interesting && i < list->oids.nr;
166+
i++) {
167+
if (list->type == OBJ_TREE) {
168+
struct tree *t = lookup_tree(ctx->repo,
169+
&list->oids.oid[i]);
170+
if (t && !(t->object.flags & UNINTERESTING))
171+
list->maybe_interesting = 1;
172+
} else {
173+
struct blob *b = lookup_blob(ctx->repo,
174+
&list->oids.oid[i]);
175+
if (b && !(b->object.flags & UNINTERESTING))
176+
list->maybe_interesting = 1;
177+
}
178+
}
179+
180+
/* We have confirmed that all objects are UNINTERESTING. */
181+
if (!list->maybe_interesting)
182+
return 0;
183+
}
184+
148185
/* Evaluate function pointer on this data, if requested. */
149186
if ((list->type == OBJ_TREE && ctx->info->trees) ||
150187
(list->type == OBJ_BLOB && ctx->info->blobs))
@@ -187,7 +224,7 @@ static void clear_strmap(struct strmap *map)
187224
int walk_objects_by_path(struct path_walk_info *info)
188225
{
189226
const char *root_path = "";
190-
int ret = 0;
227+
int ret = 0, has_uninteresting = 0;
191228
size_t commits_nr = 0, paths_nr = 0;
192229
struct commit *c;
193230
struct type_and_oid_list *root_tree_list;
@@ -199,6 +236,7 @@ int walk_objects_by_path(struct path_walk_info *info)
199236
.path_stack = STRING_LIST_INIT_DUP,
200237
.paths_to_lists = STRMAP_INIT
201238
};
239+
struct oidset root_tree_set = OIDSET_INIT;
202240

203241
trace2_region_enter("path-walk", "commit-walk", info->revs->repo);
204242

@@ -211,6 +249,7 @@ int walk_objects_by_path(struct path_walk_info *info)
211249
/* Insert a single list for the root tree into the paths. */
212250
CALLOC_ARRAY(root_tree_list, 1);
213251
root_tree_list->type = OBJ_TREE;
252+
root_tree_list->maybe_interesting = 1;
214253
strmap_put(&ctx.paths_to_lists, root_path, root_tree_list);
215254

216255
/*
@@ -301,10 +340,17 @@ int walk_objects_by_path(struct path_walk_info *info)
301340
oid = get_commit_tree_oid(c);
302341
t = lookup_tree(info->revs->repo, oid);
303342

304-
if (t)
343+
if (t) {
344+
oidset_insert(&root_tree_set, oid);
305345
oid_array_append(&root_tree_list->oids, oid);
306-
else
346+
} else {
307347
warning("could not find tree %s", oid_to_hex(oid));
348+
}
349+
350+
if (t && (c->object.flags & UNINTERESTING)) {
351+
t->object.flags |= UNINTERESTING;
352+
has_uninteresting = 1;
353+
}
308354
}
309355

310356
trace2_data_intmax("path-walk", ctx.repo, "commits", commits_nr);
@@ -317,6 +363,21 @@ int walk_objects_by_path(struct path_walk_info *info)
317363
oid_array_clear(&commit_list->oids);
318364
free(commit_list);
319365

366+
/*
367+
* Before performing a DFS of our paths and emitting them as interesting,
368+
* do a full walk of the trees to distribute the UNINTERESTING bit. Use
369+
* the sparse algorithm if prune_all_uninteresting was set.
370+
*/
371+
if (has_uninteresting) {
372+
trace2_region_enter("path-walk", "uninteresting-walk", info->revs->repo);
373+
if (info->prune_all_uninteresting)
374+
mark_trees_uninteresting_sparse(ctx.repo, &root_tree_set);
375+
else
376+
mark_trees_uninteresting_dense(ctx.repo, &root_tree_set);
377+
trace2_region_leave("path-walk", "uninteresting-walk", info->revs->repo);
378+
}
379+
oidset_clear(&root_tree_set);
380+
320381
string_list_append(&ctx.path_stack, root_path);
321382

322383
trace2_region_enter("path-walk", "path-walk", info->revs->repo);

path-walk.h

+8
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ struct path_walk_info {
3838
int trees;
3939
int blobs;
4040
int tags;
41+
42+
/**
43+
* When 'prune_all_uninteresting' is set and a path has all objects
44+
* marked as UNINTERESTING, then the path-walk will not visit those
45+
* objects. It will not call path_fn on those objects and will not
46+
* walk the children of such trees.
47+
*/
48+
int prune_all_uninteresting;
4149
};
4250

4351
#define PATH_WALK_INFO_INIT { \

t/helper/test-path-walk.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,12 @@ static int emit_block(const char *path, struct oid_array *oids,
5555
BUG("we do not understand this type");
5656
}
5757

58-
for (size_t i = 0; i < oids->nr; i++)
59-
printf("%s:%s:%s\n", typestr, path, oid_to_hex(&oids->oid[i]));
58+
for (size_t i = 0; i < oids->nr; i++) {
59+
struct object *o = lookup_unknown_object(the_repository,
60+
&oids->oid[i]);
61+
printf("%s:%s:%s%s\n", typestr, path, oid_to_hex(&oids->oid[i]),
62+
o->flags & UNINTERESTING ? ":UNINTERESTING" : "");
63+
}
6064

6165
return 0;
6266
}
@@ -76,6 +80,8 @@ int cmd__path_walk(int argc, const char **argv)
7680
N_("toggle inclusion of tag objects")),
7781
OPT_BOOL(0, "trees", &info.trees,
7882
N_("toggle inclusion of tree objects")),
83+
OPT_BOOL(0, "prune", &info.prune_all_uninteresting,
84+
N_("toggle pruning of uninteresting paths")),
7985
OPT_END(),
8086
};
8187

t/t6601-path-walk.sh

+32-8
Original file line numberDiff line numberDiff line change
@@ -229,19 +229,19 @@ test_expect_success 'topic, not base, boundary' '
229229
230230
cat >expect <<-EOF &&
231231
COMMIT::$(git rev-parse topic)
232-
COMMIT::$(git rev-parse base~1)
232+
COMMIT::$(git rev-parse base~1):UNINTERESTING
233233
commits:2
234234
TREE::$(git rev-parse topic^{tree})
235-
TREE::$(git rev-parse base~1^{tree})
236-
TREE:left/:$(git rev-parse base~1:left)
235+
TREE::$(git rev-parse base~1^{tree}):UNINTERESTING
236+
TREE:left/:$(git rev-parse base~1:left):UNINTERESTING
237237
TREE:right/:$(git rev-parse topic:right)
238-
TREE:right/:$(git rev-parse base~1:right)
238+
TREE:right/:$(git rev-parse base~1:right):UNINTERESTING
239239
trees:5
240-
BLOB:a:$(git rev-parse base~1:a)
241-
BLOB:left/b:$(git rev-parse base~1:left/b)
242-
BLOB:right/c:$(git rev-parse base~1:right/c)
240+
BLOB:a:$(git rev-parse base~1:a):UNINTERESTING
241+
BLOB:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
242+
BLOB:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
243243
BLOB:right/c:$(git rev-parse topic:right/c)
244-
BLOB:right/d:$(git rev-parse base~1:right/d)
244+
BLOB:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
245245
blobs:5
246246
tags:0
247247
EOF
@@ -252,4 +252,28 @@ test_expect_success 'topic, not base, boundary' '
252252
test_cmp expect.sorted out.sorted
253253
'
254254

255+
test_expect_success 'topic, not base, boundary with pruning' '
256+
test-tool path-walk --prune -- --boundary topic --not base >out &&
257+
258+
cat >expect <<-EOF &&
259+
COMMIT::$(git rev-parse topic)
260+
COMMIT::$(git rev-parse base~1):UNINTERESTING
261+
commits:2
262+
TREE::$(git rev-parse topic^{tree})
263+
TREE::$(git rev-parse base~1^{tree}):UNINTERESTING
264+
TREE:right/:$(git rev-parse topic:right)
265+
TREE:right/:$(git rev-parse base~1:right):UNINTERESTING
266+
trees:4
267+
BLOB:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
268+
BLOB:right/c:$(git rev-parse topic:right/c)
269+
blobs:2
270+
tags:0
271+
EOF
272+
273+
sort expect >expect.sorted &&
274+
sort out >out.sorted &&
275+
276+
test_cmp expect.sorted out.sorted
277+
'
278+
255279
test_done

0 commit comments

Comments
 (0)