Skip to content

Commit f2ffc32

Browse files
committed
path-walk: mark trees and blobs as UNINTERESTING
When the input rev_info has UNINTERESTING starting points, we want to be sure that the UNINTERESTING flag is passed appropriately through the objects. To match how this is done in places such as 'git pack-objects', we use the mark_edges_uninteresting() method. This method has an option for using the "sparse" walk, which is similar in spirit to the path-walk API's walk. To be sure to keep it independent, add a new 'prune_all_uninteresting' option to the path_walk_info struct. To check how the UNINTERSTING flag is spread through our objects, extend the 'test-tool path-walk' command to output whether or not an object has that flag. This changes our tests significantly, including the removal of some objects that were previously visited due to the incomplete implementation. Signed-off-by: Derrick Stolee <[email protected]>
1 parent 6df56f4 commit f2ffc32

File tree

5 files changed

+159
-22
lines changed

5 files changed

+159
-22
lines changed

Documentation/technical/api-path-walk.txt

+8
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ commits.
4848
While it is possible to walk only commits in this way, consumers would be
4949
better off using the revision walk API instead.
5050

51+
`prune_all_uninteresting`::
52+
By default, all reachable paths are emitted by the path-walk API.
53+
This option allows consumers to declare that they are not
54+
interested in paths where all included objects are marked with the
55+
`UNINTERESTING` flag. This requires using the `boundary` option in
56+
the revision walk so that the walk emits commits marked with the
57+
`UNINTERESTING` flag.
58+
5159
Examples
5260
--------
5361

path-walk.c

+74
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "dir.h"
99
#include "hashmap.h"
1010
#include "hex.h"
11+
#include "list-objects.h"
1112
#include "object.h"
1213
#include "oid-array.h"
1314
#include "revision.h"
@@ -23,6 +24,7 @@ static const char *root_path = "";
2324
struct type_and_oid_list {
2425
enum object_type type;
2526
struct oid_array oids;
27+
int maybe_interesting;
2628
};
2729

2830
#define TYPE_AND_OID_LIST_INIT { \
@@ -142,6 +144,10 @@ static int add_tree_entries(struct path_walk_context *ctx,
142144
strmap_put(&ctx->paths_to_lists, path.buf, list);
143145
}
144146
push_to_stack(ctx, path.buf);
147+
148+
if (!(o->flags & UNINTERESTING))
149+
list->maybe_interesting = 1;
150+
145151
oid_array_append(&list->oids, &entry.oid);
146152
}
147153

@@ -169,6 +175,43 @@ static int walk_path(struct path_walk_context *ctx,
169175
if (!list->oids.nr)
170176
return 0;
171177

178+
if (ctx->info->prune_all_uninteresting) {
179+
/*
180+
* This is true if all objects were UNINTERESTING
181+
* when added to the list.
182+
*/
183+
if (!list->maybe_interesting)
184+
return 0;
185+
186+
/*
187+
* But it's still possible that the objects were set
188+
* as UNINTERESTING after being added. Do a quick check.
189+
*/
190+
list->maybe_interesting = 0;
191+
for (size_t i = 0;
192+
!list->maybe_interesting && i < list->oids.nr;
193+
i++) {
194+
if (list->type == OBJ_TREE) {
195+
struct tree *t = lookup_tree(ctx->repo,
196+
&list->oids.oid[i]);
197+
if (t && !(t->object.flags & UNINTERESTING))
198+
list->maybe_interesting = 1;
199+
} else if (list->type == OBJ_BLOB) {
200+
struct blob *b = lookup_blob(ctx->repo,
201+
&list->oids.oid[i]);
202+
if (b && !(b->object.flags & UNINTERESTING))
203+
list->maybe_interesting = 1;
204+
} else {
205+
/* Tags are always interesting if visited. */
206+
list->maybe_interesting = 1;
207+
}
208+
}
209+
210+
/* We have confirmed that all objects are UNINTERESTING. */
211+
if (!list->maybe_interesting)
212+
return 0;
213+
}
214+
172215
/* Evaluate function pointer on this data, if requested. */
173216
if ((list->type == OBJ_TREE && ctx->info->trees) ||
174217
(list->type == OBJ_BLOB && ctx->info->blobs) ||
@@ -203,6 +246,26 @@ static void clear_paths_to_lists(struct strmap *map)
203246
strmap_init(map);
204247
}
205248

249+
static struct repository *edge_repo;
250+
static struct type_and_oid_list *edge_tree_list;
251+
252+
static void show_edge(struct commit *commit)
253+
{
254+
struct tree *t = repo_get_commit_tree(edge_repo, commit);
255+
256+
if (!t)
257+
return;
258+
259+
if (commit->object.flags & UNINTERESTING)
260+
t->object.flags |= UNINTERESTING;
261+
262+
if (t->object.flags & SEEN)
263+
return;
264+
t->object.flags |= SEEN;
265+
266+
oid_array_append(&edge_tree_list->oids, &t->object.oid);
267+
}
268+
206269
static int setup_pending_objects(struct path_walk_info *info,
207270
struct path_walk_context *ctx)
208271
{
@@ -314,6 +377,7 @@ static int setup_pending_objects(struct path_walk_info *info,
314377
if (tagged_blobs->oids.nr) {
315378
const char *tagged_blob_path = "/tagged-blobs";
316379
tagged_blobs->type = OBJ_BLOB;
380+
tagged_blobs->maybe_interesting = 1;
317381
push_to_stack(ctx, tagged_blob_path);
318382
strmap_put(&ctx->paths_to_lists, tagged_blob_path, tagged_blobs);
319383
} else {
@@ -325,6 +389,7 @@ static int setup_pending_objects(struct path_walk_info *info,
325389
if (tags->oids.nr) {
326390
const char *tag_path = "/tags";
327391
tags->type = OBJ_TAG;
392+
tags->maybe_interesting = 1;
328393
push_to_stack(ctx, tag_path);
329394
strmap_put(&ctx->paths_to_lists, tag_path, tags);
330395
} else {
@@ -369,6 +434,7 @@ int walk_objects_by_path(struct path_walk_info *info)
369434
/* Insert a single list for the root tree into the paths. */
370435
CALLOC_ARRAY(root_tree_list, 1);
371436
root_tree_list->type = OBJ_TREE;
437+
root_tree_list->maybe_interesting = 1;
372438
strmap_put(&ctx.paths_to_lists, root_path, root_tree_list);
373439
push_to_stack(&ctx, root_path);
374440

@@ -382,6 +448,14 @@ int walk_objects_by_path(struct path_walk_info *info)
382448
if (prepare_revision_walk(info->revs))
383449
die(_("failed to setup revision walk"));
384450

451+
/* Walk trees to mark them as UNINTERESTING. */
452+
edge_repo = info->revs->repo;
453+
edge_tree_list = root_tree_list;
454+
mark_edges_uninteresting(info->revs, show_edge,
455+
info->prune_all_uninteresting);
456+
edge_repo = NULL;
457+
edge_tree_list = NULL;
458+
385459
info->revs->blob_objects = info->revs->tree_objects = 0;
386460

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

path-walk.h

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

4553
#define PATH_WALK_INFO_INIT { \

t/helper/test-path-walk.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,14 @@ static int emit_block(const char *path, struct oid_array *oids,
5050
printf("%"PRIuMAX":%s:%s:EMPTY\n",
5151
tdata->batch_nr, typestr, path);
5252

53-
for (size_t i = 0; i < oids->nr; i++)
54-
printf("%"PRIuMAX":%s:%s:%s\n",
53+
for (size_t i = 0; i < oids->nr; i++) {
54+
struct object *o = lookup_unknown_object(the_repository,
55+
&oids->oid[i]);
56+
printf("%"PRIuMAX":%s:%s:%s%s\n",
5557
tdata->batch_nr, typestr, path,
56-
oid_to_hex(&oids->oid[i]));
58+
oid_to_hex(&oids->oid[i]),
59+
o->flags & UNINTERESTING ? ":UNINTERESTING" : "");
60+
}
5761

5862
tdata->batch_nr++;
5963
return 0;
@@ -74,6 +78,8 @@ int cmd__path_walk(int argc, const char **argv)
7478
N_("toggle inclusion of tag objects")),
7579
OPT_BOOL(0, "trees", &info.trees,
7680
N_("toggle inclusion of tree objects")),
81+
OPT_BOOL(0, "prune", &info.prune_all_uninteresting,
82+
N_("toggle pruning of uninteresting paths")),
7783
OPT_END(),
7884
};
7985

t/t6601-path-walk.sh

+60-19
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ test_expect_success 'topic, not base' '
211211
0:commit::$(git rev-parse topic)
212212
1:tree::$(git rev-parse topic^{tree})
213213
2:tree:right/:$(git rev-parse topic:right)
214-
3:blob:right/d:$(git rev-parse topic:right/d)
214+
3:blob:right/d:$(git rev-parse topic:right/d):UNINTERESTING
215215
4:blob:right/c:$(git rev-parse topic:right/c)
216-
5:tree:left/:$(git rev-parse topic:left)
217-
6:blob:left/b:$(git rev-parse topic:left/b)
218-
7:blob:a:$(git rev-parse topic:a)
216+
5:tree:left/:$(git rev-parse topic:left):UNINTERESTING
217+
6:blob:left/b:$(git rev-parse topic:left/b):UNINTERESTING
218+
7:blob:a:$(git rev-parse topic:a):UNINTERESTING
219219
blobs:4
220220
commits:1
221221
tags:0
@@ -225,15 +225,38 @@ test_expect_success 'topic, not base' '
225225
test_cmp_sorted expect out
226226
'
227227

228+
test_expect_success 'fourth, blob-tag2, not base' '
229+
test-tool path-walk -- fourth blob-tag2 --not base >out &&
230+
231+
cat >expect <<-EOF &&
232+
0:commit::$(git rev-parse topic)
233+
1:tag:/tags:$(git rev-parse fourth)
234+
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
235+
3:tree::$(git rev-parse topic^{tree})
236+
4:tree:right/:$(git rev-parse topic:right)
237+
5:blob:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
238+
6:blob:right/c:$(git rev-parse topic:right/c)
239+
7:tree:left/:$(git rev-parse base~1:left):UNINTERESTING
240+
8:blob:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
241+
9:blob:a:$(git rev-parse base~1:a):UNINTERESTING
242+
blobs:5
243+
commits:1
244+
tags:1
245+
trees:3
246+
EOF
247+
248+
test_cmp_sorted expect out
249+
'
250+
228251
test_expect_success 'topic, not base, only blobs' '
229252
test-tool path-walk --no-trees --no-commits \
230253
-- topic --not base >out &&
231254
232255
cat >expect <<-EOF &&
233-
0:blob:right/d:$(git rev-parse topic:right/d)
256+
0:blob:right/d:$(git rev-parse topic:right/d):UNINTERESTING
234257
1:blob:right/c:$(git rev-parse topic:right/c)
235-
2:blob:left/b:$(git rev-parse topic:left/b)
236-
3:blob:a:$(git rev-parse topic:a)
258+
2:blob:left/b:$(git rev-parse topic:left/b):UNINTERESTING
259+
3:blob:a:$(git rev-parse topic:a):UNINTERESTING
237260
blobs:4
238261
commits:0
239262
tags:0
@@ -267,7 +290,7 @@ test_expect_success 'topic, not base, only trees' '
267290
cat >expect <<-EOF &&
268291
0:tree::$(git rev-parse topic^{tree})
269292
1:tree:right/:$(git rev-parse topic:right)
270-
2:tree:left/:$(git rev-parse topic:left)
293+
2:tree:left/:$(git rev-parse topic:left):UNINTERESTING
271294
commits:0
272295
blobs:0
273296
tags:0
@@ -282,17 +305,17 @@ test_expect_success 'topic, not base, boundary' '
282305
283306
cat >expect <<-EOF &&
284307
0:commit::$(git rev-parse topic)
285-
0:commit::$(git rev-parse base~1)
308+
0:commit::$(git rev-parse base~1):UNINTERESTING
286309
1:tree::$(git rev-parse topic^{tree})
287-
1:tree::$(git rev-parse base~1^{tree})
310+
1:tree::$(git rev-parse base~1^{tree}):UNINTERESTING
288311
2:tree:right/:$(git rev-parse topic:right)
289-
2:tree:right/:$(git rev-parse base~1:right)
290-
3:blob:right/d:$(git rev-parse base~1:right/d)
291-
4:blob:right/c:$(git rev-parse base~1:right/c)
312+
2:tree:right/:$(git rev-parse base~1:right):UNINTERESTING
313+
3:blob:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
314+
4:blob:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
292315
4:blob:right/c:$(git rev-parse topic:right/c)
293-
5:tree:left/:$(git rev-parse base~1:left)
294-
6:blob:left/b:$(git rev-parse base~1:left/b)
295-
7:blob:a:$(git rev-parse base~1:a)
316+
5:tree:left/:$(git rev-parse base~1:left):UNINTERESTING
317+
6:blob:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
318+
7:blob:a:$(git rev-parse base~1:a):UNINTERESTING
296319
blobs:5
297320
commits:2
298321
tags:0
@@ -302,22 +325,40 @@ test_expect_success 'topic, not base, boundary' '
302325
test_cmp_sorted expect out
303326
'
304327

328+
test_expect_success 'topic, not base, boundary with pruning' '
329+
test-tool path-walk --prune -- --boundary topic --not base >out &&
330+
331+
cat >expect <<-EOF &&
332+
0:commit::$(git rev-parse topic)
333+
0:commit::$(git rev-parse base~1):UNINTERESTING
334+
1:tree::$(git rev-parse topic^{tree})
335+
1:tree::$(git rev-parse base~1^{tree}):UNINTERESTING
336+
2:tree:right/:$(git rev-parse topic:right)
337+
2:tree:right/:$(git rev-parse base~1:right):UNINTERESTING
338+
3:blob:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
339+
3:blob:right/c:$(git rev-parse topic:right/c)
340+
blobs:2
341+
commits:2
342+
tags:0
343+
trees:4
344+
EOF
345+
346+
test_cmp_sorted expect out
347+
'
348+
305349
test_expect_success 'trees are reported exactly once' '
306350
test_when_finished "rm -rf unique-trees" &&
307351
test_create_repo unique-trees &&
308352
(
309353
cd unique-trees &&
310354
mkdir initial &&
311355
test_commit initial/file &&
312-
313356
git switch -c move-to-top &&
314357
git mv initial/file.t ./ &&
315358
test_tick &&
316359
git commit -m moved &&
317-
318360
git update-ref refs/heads/other HEAD
319361
) &&
320-
321362
test-tool -C unique-trees path-walk -- --all >out &&
322363
tree=$(git -C unique-trees rev-parse HEAD:) &&
323364
grep "$tree" out >out-filtered &&

0 commit comments

Comments
 (0)