Skip to content

Commit 5252076

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 dfd00b2 commit 5252076

File tree

5 files changed

+155
-38
lines changed

5 files changed

+155
-38
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

@@ -164,6 +169,43 @@ static int walk_path(struct path_walk_context *ctx,
164169
if (!list)
165170
BUG("provided path '%s' that had no associated list", path);
166171

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

243+
static struct repository *edge_repo;
244+
static struct type_and_oid_list *edge_tree_list;
245+
246+
static void show_edge(struct commit *commit)
247+
{
248+
struct tree *t = repo_get_commit_tree(edge_repo, commit);
249+
250+
if (!t)
251+
return;
252+
253+
if (commit->object.flags & UNINTERESTING)
254+
t->object.flags |= UNINTERESTING;
255+
256+
if (t->object.flags & SEEN)
257+
return;
258+
t->object.flags |= SEEN;
259+
260+
oid_array_append(&edge_tree_list->oids, &t->object.oid);
261+
}
262+
201263
static void setup_pending_objects(struct path_walk_info *info,
202264
struct path_walk_context *ctx)
203265
{
@@ -306,6 +368,7 @@ static void setup_pending_objects(struct path_walk_info *info,
306368
if (tagged_blobs->oids.nr) {
307369
const char *tagged_blob_path = "/tagged-blobs";
308370
tagged_blobs->type = OBJ_BLOB;
371+
tagged_blobs->maybe_interesting = 1;
309372
push_to_stack(ctx, tagged_blob_path);
310373
strmap_put(&ctx->paths_to_lists, tagged_blob_path, tagged_blobs);
311374
} else {
@@ -317,6 +380,7 @@ static void setup_pending_objects(struct path_walk_info *info,
317380
if (tags->oids.nr) {
318381
const char *tag_path = "/tags";
319382
tags->type = OBJ_TAG;
383+
tags->maybe_interesting = 1;
320384
push_to_stack(ctx, tag_path);
321385
strmap_put(&ctx->paths_to_lists, tag_path, tags);
322386
} else {
@@ -359,6 +423,7 @@ int walk_objects_by_path(struct path_walk_info *info)
359423
/* Insert a single list for the root tree into the paths. */
360424
CALLOC_ARRAY(root_tree_list, 1);
361425
root_tree_list->type = OBJ_TREE;
426+
root_tree_list->maybe_interesting = 1;
362427
strmap_put(&ctx.paths_to_lists, root_path, root_tree_list);
363428
push_to_stack(&ctx, root_path);
364429

@@ -372,6 +437,14 @@ int walk_objects_by_path(struct path_walk_info *info)
372437
if (prepare_revision_walk(info->revs))
373438
die(_("failed to setup revision walk"));
374439

440+
/* Walk trees to mark them as UNINTERESTING. */
441+
edge_repo = info->revs->repo;
442+
edge_tree_list = root_tree_list;
443+
mark_edges_uninteresting(info->revs, show_edge,
444+
info->prune_all_uninteresting);
445+
edge_repo = NULL;
446+
edge_tree_list = NULL;
447+
375448
info->revs->blob_objects = info->revs->tree_objects = 0;
376449

377450
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

+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

+58-36
Original file line numberDiff line numberDiff line change
@@ -209,31 +209,54 @@ test_expect_success 'topic, not base' '
209209
COMMIT::$(git rev-parse topic)
210210
commits:1
211211
TREE::$(git rev-parse topic^{tree})
212-
TREE:left/:$(git rev-parse topic:left)
212+
TREE:left/:$(git rev-parse base~1:left):UNINTERESTING
213213
TREE:right/:$(git rev-parse topic:right)
214214
trees:3
215-
BLOB:a:$(git rev-parse topic:a)
216-
BLOB:left/b:$(git rev-parse topic:left/b)
215+
BLOB:a:$(git rev-parse base~1:a):UNINTERESTING
216+
BLOB:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
217217
BLOB:right/c:$(git rev-parse topic:right/c)
218-
BLOB:right/d:$(git rev-parse topic:right/d)
218+
BLOB:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
219219
blobs:4
220220
tags:0
221221
EOF
222222
223223
test_cmp_sorted expect out
224224
'
225225

226+
test_expect_success 'fourth, blob-tag2, not base' '
227+
test-tool path-walk -- fourth blob-tag2 --not base >out &&
228+
229+
cat >expect <<-EOF &&
230+
COMMIT::$(git rev-parse topic)
231+
commits:1
232+
TREE::$(git rev-parse topic^{tree})
233+
TREE:left/:$(git rev-parse base~1:left):UNINTERESTING
234+
TREE:right/:$(git rev-parse topic:right)
235+
trees:3
236+
BLOB:a:$(git rev-parse base~1:a):UNINTERESTING
237+
BLOB:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
238+
BLOB:right/c:$(git rev-parse topic:right/c)
239+
BLOB:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
240+
BLOB:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
241+
blobs:5
242+
TAG:/tags:$(git rev-parse fourth)
243+
tags:1
244+
EOF
245+
246+
test_cmp_sorted expect out
247+
'
248+
226249
test_expect_success 'topic, not base, only blobs' '
227250
test-tool path-walk --no-trees --no-commits \
228251
-- topic --not base >out &&
229252
230253
cat >expect <<-EOF &&
231254
commits:0
232255
trees:0
233-
BLOB:a:$(git rev-parse topic:a)
234-
BLOB:left/b:$(git rev-parse topic:left/b)
256+
BLOB:a:$(git rev-parse base~1:a):UNINTERESTING
257+
BLOB:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
235258
BLOB:right/c:$(git rev-parse topic:right/c)
236-
BLOB:right/d:$(git rev-parse topic:right/d)
259+
BLOB:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
237260
blobs:4
238261
tags:0
239262
EOF
@@ -265,7 +288,7 @@ test_expect_success 'topic, not base, only trees' '
265288
cat >expect <<-EOF &&
266289
commits:0
267290
TREE::$(git rev-parse topic^{tree})
268-
TREE:left/:$(git rev-parse topic:left)
291+
TREE:left/:$(git rev-parse base~1:left):UNINTERESTING
269292
TREE:right/:$(git rev-parse topic:right)
270293
trees:3
271294
blobs:0
@@ -280,46 +303,45 @@ test_expect_success 'topic, not base, boundary' '
280303
281304
cat >expect <<-EOF &&
282305
COMMIT::$(git rev-parse topic)
283-
COMMIT::$(git rev-parse base~1)
306+
COMMIT::$(git rev-parse base~1):UNINTERESTING
284307
commits:2
285308
TREE::$(git rev-parse topic^{tree})
286-
TREE::$(git rev-parse base~1^{tree})
287-
TREE:left/:$(git rev-parse base~1:left)
309+
TREE::$(git rev-parse base~1^{tree}):UNINTERESTING
310+
TREE:left/:$(git rev-parse base~1:left):UNINTERESTING
288311
TREE:right/:$(git rev-parse topic:right)
289-
TREE:right/:$(git rev-parse base~1:right)
312+
TREE:right/:$(git rev-parse base~1:right):UNINTERESTING
290313
trees:5
291-
BLOB:a:$(git rev-parse base~1:a)
292-
BLOB:left/b:$(git rev-parse base~1:left/b)
293-
BLOB:right/c:$(git rev-parse base~1:right/c)
314+
BLOB:a:$(git rev-parse base~1:a):UNINTERESTING
315+
BLOB:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
316+
BLOB:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
294317
BLOB:right/c:$(git rev-parse topic:right/c)
295-
BLOB:right/d:$(git rev-parse base~1:right/d)
318+
BLOB:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
296319
blobs:5
297320
tags:0
298321
EOF
299322
300323
test_cmp_sorted expect out
301324
'
302325

303-
test_expect_success 'trees are reported exactly once' '
304-
test_when_finished "rm -rf unique-trees" &&
305-
test_create_repo unique-trees &&
306-
(
307-
cd unique-trees &&
308-
mkdir initial &&
309-
test_commit initial/file &&
310-
311-
git switch -c move-to-top &&
312-
git mv initial/file.t ./ &&
313-
test_tick &&
314-
git commit -m moved &&
315-
316-
git update-ref refs/heads/other HEAD
317-
) &&
318-
319-
test-tool -C unique-trees path-walk -- --all >out &&
320-
tree=$(git -C unique-trees rev-parse HEAD:) &&
321-
grep "$tree" out >out-filtered &&
322-
test_line_count = 1 out-filtered
326+
test_expect_success 'topic, not base, boundary with pruning' '
327+
test-tool path-walk --prune -- --boundary topic --not base >out &&
328+
329+
cat >expect <<-EOF &&
330+
COMMIT::$(git rev-parse topic)
331+
COMMIT::$(git rev-parse base~1):UNINTERESTING
332+
commits:2
333+
TREE::$(git rev-parse topic^{tree})
334+
TREE::$(git rev-parse base~1^{tree}):UNINTERESTING
335+
TREE:right/:$(git rev-parse topic:right)
336+
TREE:right/:$(git rev-parse base~1:right):UNINTERESTING
337+
trees:4
338+
BLOB:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
339+
BLOB:right/c:$(git rev-parse topic:right/c)
340+
blobs:2
341+
tags:0
342+
EOF
343+
344+
test_cmp_sorted expect out
323345
'
324346

325347
test_done

0 commit comments

Comments
 (0)