Skip to content

Commit 0f1e6c5

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 a41f53f commit 0f1e6c5

File tree

5 files changed

+158
-22
lines changed

5 files changed

+158
-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

+73
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"
@@ -24,6 +25,7 @@ struct type_and_oid_list
2425
{
2526
enum object_type type;
2627
struct oid_array oids;
28+
int maybe_interesting;
2729
};
2830

2931
#define TYPE_AND_OID_LIST_INIT { \
@@ -140,6 +142,9 @@ static int add_children(struct path_walk_context *ctx,
140142
if (o->flags & SEEN)
141143
continue;
142144
o->flags |= SEEN;
145+
146+
if (!(o->flags & UNINTERESTING))
147+
list->maybe_interesting = 1;
143148
oid_array_append(&list->oids, &entry.oid);
144149
}
145150

@@ -167,6 +172,43 @@ static int walk_path(struct path_walk_context *ctx,
167172
if (!list->oids.nr)
168173
return 0;
169174

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

246+
static struct repository *edge_repo;
247+
static struct type_and_oid_list *edge_tree_list;
248+
249+
static void show_edge(struct commit *commit)
250+
{
251+
struct tree *t = repo_get_commit_tree(edge_repo, commit);
252+
253+
if (!t)
254+
return;
255+
256+
if (commit->object.flags & UNINTERESTING)
257+
t->object.flags |= UNINTERESTING;
258+
259+
if (t->object.flags & SEEN)
260+
return;
261+
t->object.flags |= SEEN;
262+
263+
oid_array_append(&edge_tree_list->oids, &t->object.oid);
264+
}
265+
204266
static void setup_pending_objects(struct path_walk_info *info,
205267
struct path_walk_context *ctx)
206268
{
@@ -309,6 +371,7 @@ static void setup_pending_objects(struct path_walk_info *info,
309371
if (tagged_blobs->oids.nr) {
310372
const char *tagged_blob_path = "/tagged-blobs";
311373
tagged_blobs->type = OBJ_BLOB;
374+
tagged_blobs->maybe_interesting = 1;
312375
push_to_stack(ctx, tagged_blob_path);
313376
strmap_put(&ctx->paths_to_lists, tagged_blob_path, tagged_blobs);
314377
} else {
@@ -320,6 +383,7 @@ static void setup_pending_objects(struct path_walk_info *info,
320383
if (tags->oids.nr) {
321384
const char *tag_path = "/tags";
322385
tags->type = OBJ_TAG;
386+
tags->maybe_interesting = 1;
323387
push_to_stack(ctx, tag_path);
324388
strmap_put(&ctx->paths_to_lists, tag_path, tags);
325389
} else {
@@ -362,6 +426,7 @@ int walk_objects_by_path(struct path_walk_info *info)
362426
/* Insert a single list for the root tree into the paths. */
363427
CALLOC_ARRAY(root_tree_list, 1);
364428
root_tree_list->type = OBJ_TREE;
429+
root_tree_list->maybe_interesting = 1;
365430
strmap_put(&ctx.paths_to_lists, root_path, root_tree_list);
366431
push_to_stack(&ctx, root_path);
367432

@@ -375,6 +440,14 @@ int walk_objects_by_path(struct path_walk_info *info)
375440
if (prepare_revision_walk(info->revs))
376441
die(_("failed to setup revision walk"));
377442

443+
/* Walk trees to mark them as UNINTERESTING. */
444+
edge_repo = info->revs->repo;
445+
edge_tree_list = root_tree_list;
446+
mark_edges_uninteresting(info->revs, show_edge,
447+
info->prune_all_uninteresting);
448+
edge_repo = NULL;
449+
edge_tree_list = NULL;
450+
378451
info->revs->blob_objects = info->revs->tree_objects = 0;
379452

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

path-walk.h

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

4452
#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)