Raw
1 /*
2 * path-walk.c: implementation for path-based walks of the object graph.
3 */
4 #include "git-compat-util.h"
5 #include "path-walk.h"
6 #include "blob.h"
7 #include "commit.h"
8 #include "dir.h"
9 #include "hashmap.h"
10 #include "hex.h"
11 #include "list-objects.h"
12 #include "list-objects-filter-options.h"
13 #include "object-name.h"
14 #include "odb.h"
15 #include "object.h"
16 #include "oid-array.h"
17 #include "path.h"
18 #include "prio-queue.h"
19 #include "repository.h"
20 #include "revision.h"
21 #include "string-list.h"
22 #include "strmap.h"
23 #include "tag.h"
24 #include "trace2.h"
25 #include "tree.h"
26 #include "tree-walk.h"
27
28 static const char *root_path = "";
29
30 struct type_and_oid_list {
31 enum object_type type;
32 struct oid_array oids;
33 int maybe_interesting;
34 };
35
36 #define TYPE_AND_OID_LIST_INIT { \
37 .type = OBJ_NONE, \
38 .oids = OID_ARRAY_INIT \
39 }
40
41 struct path_walk_context {
42 /**
43 * Repeats of data in 'struct path_walk_info' for
44 * access with fewer characters.
45 */
46 struct repository *repo;
47 struct rev_info *revs;
48 struct path_walk_info *info;
49
50 /**
51 * Map a path to a 'struct type_and_oid_list'
52 * containing the objects discovered at that
53 * path.
54 */
55 struct strmap paths_to_lists;
56
57 /**
58 * Store the current list of paths in a priority queue,
59 * using object type as a sorting mechanism, mostly to
60 * make sure blobs are popped off the stack first. No
61 * other sort is made, so within each object type it acts
62 * like a stack and performs a DFS within the trees.
63 *
64 * Use path_stack_pushed to indicate whether a path
65 * was previously added to path_stack.
66 */
67 struct prio_queue path_stack;
68 struct strset path_stack_pushed;
69
70 unsigned exact_pathspecs:1;
71 };
72
73 static int compare_by_type(const void *one, const void *two, void *cb_data)
74 {
75 struct type_and_oid_list *list1, *list2;
76 const char *str1 = one;
77 const char *str2 = two;
78 struct path_walk_context *ctx = cb_data;
79
80 list1 = strmap_get(&ctx->paths_to_lists, str1);
81 list2 = strmap_get(&ctx->paths_to_lists, str2);
82
83 /*
84 * If object types are equal, then use path comparison.
85 */
86 if (!list1 || !list2 || list1->type == list2->type)
87 return strcmp(str1, str2);
88
89 /* Prefer tags to be popped off first. */
90 if (list1->type == OBJ_TAG)
91 return -1;
92 if (list2->type == OBJ_TAG)
93 return 1;
94
95 /* Prefer blobs to be popped off second. */
96 if (list1->type == OBJ_BLOB)
97 return -1;
98 if (list2->type == OBJ_BLOB)
99 return 1;
100
101 return 0;
102 }
103
104 static void push_to_stack(struct path_walk_context *ctx,
105 const char *path)
106 {
107 if (strset_contains(&ctx->path_stack_pushed, path))
108 return;
109
110 strset_add(&ctx->path_stack_pushed, path);
111 prio_queue_put(&ctx->path_stack, xstrdup(path));
112 }
113
114 static void add_path_to_list(struct path_walk_context *ctx,
115 const char *path,
116 enum object_type type,
117 struct object_id *oid,
118 int interesting)
119 {
120 struct type_and_oid_list *list = strmap_get(&ctx->paths_to_lists, path);
121
122 if (!list) {
123 CALLOC_ARRAY(list, 1);
124 list->type = type;
125 strmap_put(&ctx->paths_to_lists, path, list);
126 }
127
128 list->maybe_interesting |= interesting;
129 oid_array_append(&list->oids, oid);
130 }
131
132 static int add_tree_entries(struct path_walk_context *ctx,
133 const char *base_path,
134 struct object_id *oid)
135 {
136 struct tree_desc desc;
137 struct name_entry entry;
138 struct strbuf path = STRBUF_INIT;
139 size_t base_len;
140 struct tree *tree = lookup_tree(ctx->repo, oid);
141
142 if (!tree) {
143 error(_("failed to walk children of tree %s: not found"),
144 oid_to_hex(oid));
145 return -1;
146 } else if (repo_parse_tree_gently(ctx->repo, tree, 1)) {
147 error("bad tree object %s", oid_to_hex(oid));
148 return -1;
149 }
150
151 strbuf_addstr(&path, base_path);
152 base_len = path.len;
153
154 init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
155 while (tree_entry(&desc, &entry)) {
156 struct object *o;
157 /* Not actually true, but we will ignore submodules later. */
158 enum object_type type = S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB;
159
160 /* Skip submodules. */
161 if (S_ISGITLINK(entry.mode))
162 continue;
163
164 /* If the caller doesn't want blobs, then don't bother. */
165 if (!ctx->info->blobs && type == OBJ_BLOB)
166 continue;
167
168 if (type == OBJ_TREE) {
169 struct tree *child = lookup_tree(ctx->repo, &entry.oid);
170 o = child ? &child->object : NULL;
171 } else if (type == OBJ_BLOB) {
172 struct blob *child = lookup_blob(ctx->repo, &entry.oid);
173 o = child ? &child->object : NULL;
174 } else {
175 BUG("invalid type for tree entry: %d", type);
176 }
177
178 if (!o) {
179 error(_("failed to find object %s"),
180 oid_to_hex(&entry.oid));
181 return -1;
182 }
183
184 strbuf_setlen(&path, base_len);
185 strbuf_add(&path, entry.path, entry.pathlen);
186
187 /*
188 * Trees will end with "/" for concatenation and distinction
189 * from blobs at the same path.
190 */
191 if (type == OBJ_TREE)
192 strbuf_addch(&path, '/');
193
194 if (o->flags & SEEN) {
195 /*
196 * A tree with a shared OID may appear at multiple
197 * paths. Even though we already added this tree to
198 * the output at some other path, we still need to
199 * walk into it at this in-cone path to discover
200 * blobs that were not found at the earlier
201 * out-of-cone path.
202 *
203 * Only do this for paths not yet in our map, to
204 * avoid duplicate entries when the same tree OID
205 * appears at the same path across multiple commits.
206 */
207 if (type == OBJ_TREE && ctx->info->pl &&
208 ctx->info->pl->use_cone_patterns &&
209 !ctx->info->pl_sparse_trees &&
210 !strmap_contains(&ctx->paths_to_lists, path.buf)) {
211 int dtype;
212 enum pattern_match_result m;
213 m = path_matches_pattern_list(path.buf, path.len,
214 path.buf + base_len,
215 &dtype,
216 ctx->info->pl,
217 ctx->repo->index);
218 if (m != NOT_MATCHED) {
219 add_path_to_list(ctx, path.buf, type,
220 &entry.oid,
221 !(o->flags & UNINTERESTING));
222 push_to_stack(ctx, path.buf);
223 }
224 }
225 continue;
226 }
227
228 if (ctx->info->pl) {
229 int dtype;
230 enum pattern_match_result match;
231 match = path_matches_pattern_list(path.buf, path.len,
232 path.buf + base_len, &dtype,
233 ctx->info->pl,
234 ctx->repo->index);
235
236 if (ctx->info->pl->use_cone_patterns &&
237 match == NOT_MATCHED &&
238 (type == OBJ_BLOB || ctx->info->pl_sparse_trees))
239 continue;
240 else if (!ctx->info->pl->use_cone_patterns &&
241 type == OBJ_BLOB &&
242 match != MATCHED)
243 continue;
244 }
245 if (ctx->revs->prune_data.nr && ctx->exact_pathspecs) {
246 struct pathspec *pd = &ctx->revs->prune_data;
247 bool found = false;
248 int did_strip_suffix = strbuf_strip_suffix(&path, "/");
249
250
251 for (int i = 0; i < pd->nr; i++) {
252 struct pathspec_item *item = &pd->items[i];
253
254 /*
255 * Continue if either is a directory prefix
256 * of the other.
257 */
258 if (dir_prefix(path.buf, item->match) ||
259 dir_prefix(item->match, path.buf)) {
260 found = true;
261 break;
262 }
263 }
264
265 if (did_strip_suffix)
266 strbuf_addch(&path, '/');
267
268 /* Skip paths that do not match the prefix. */
269 if (!found)
270 continue;
271 }
272
273 o->flags |= SEEN;
274 add_path_to_list(ctx, path.buf, type, &entry.oid,
275 !(o->flags & UNINTERESTING));
276
277 push_to_stack(ctx, path.buf);
278 }
279
280 free_tree_buffer(tree);
281 strbuf_release(&path);
282 return 0;
283 }
284
285 /*
286 * Paths starting with '/' (e.g., "/tags", "/tagged-blobs") hold objects that
287 * were directly requested by 'pending' objects rather than discovered during
288 * tree traversal.
289 */
290 static int path_is_for_direct_objects(const char *path)
291 {
292 ASSERT(path);
293 return path[0] == '/';
294 }
295
296 /*
297 * For each path in paths_to_explore, walk the trees another level
298 * and add any found blobs to the batch (but only if they exist and
299 * haven't been added yet).
300 */
301 static int walk_path(struct path_walk_context *ctx,
302 const char *path)
303 {
304 struct type_and_oid_list *list;
305 int ret = 0;
306
307 list = strmap_get(&ctx->paths_to_lists, path);
308
309 if (!list)
310 BUG("provided path '%s' that had no associated list", path);
311
312 if (!list->oids.nr)
313 return 0;
314
315 if (ctx->info->prune_all_uninteresting) {
316 /*
317 * This is true if all objects were UNINTERESTING
318 * when added to the list.
319 */
320 if (!list->maybe_interesting)
321 return 0;
322
323 /*
324 * But it's still possible that the objects were set
325 * as UNINTERESTING after being added. Do a quick check.
326 */
327 list->maybe_interesting = 0;
328 for (size_t i = 0;
329 !list->maybe_interesting && i < list->oids.nr;
330 i++) {
331 if (list->type == OBJ_TREE) {
332 struct tree *t = lookup_tree(ctx->repo,
333 &list->oids.oid[i]);
334 if (t && !(t->object.flags & UNINTERESTING))
335 list->maybe_interesting = 1;
336 } else if (list->type == OBJ_BLOB) {
337 struct blob *b = lookup_blob(ctx->repo,
338 &list->oids.oid[i]);
339 if (b && !(b->object.flags & UNINTERESTING))
340 list->maybe_interesting = 1;
341 } else {
342 /* Tags are always interesting if visited. */
343 list->maybe_interesting = 1;
344 }
345 }
346
347 /* We have confirmed that all objects are UNINTERESTING. */
348 if (!list->maybe_interesting)
349 return 0;
350 }
351
352 if (list->type == OBJ_BLOB &&
353 ctx->revs->prune_data.nr &&
354 !path_is_for_direct_objects(path) &&
355 !match_pathspec(ctx->repo->index, &ctx->revs->prune_data,
356 path, strlen(path), 0,
357 NULL, 0))
358 return 0;
359
360 /*
361 * Evaluate function pointer on this data, if requested.
362 * Ignore object type filters for tagged objects (path starts
363 * with `/`), first for blobs and then other types.
364 */
365 if (list->type == OBJ_BLOB &&
366 ctx->info->blob_limit &&
367 !path_is_for_direct_objects(path)) {
368 struct oid_array filtered = OID_ARRAY_INIT;
369
370 for (size_t i = 0; i < list->oids.nr; i++) {
371 unsigned long size;
372
373 if (odb_read_object_info(ctx->repo->objects,
374 &list->oids.oid[i],
375 &size) != OBJ_BLOB ||
376 size < ctx->info->blob_limit)
377 oid_array_append(&filtered,
378 &list->oids.oid[i]);
379 }
380
381 if (filtered.nr)
382 ret = ctx->info->path_fn(path, &filtered, list->type,
383 ctx->info->path_fn_data);
384 oid_array_clear(&filtered);
385 } else if ((!ctx->info->strict_types && path_is_for_direct_objects(path)) ||
386 (list->type == OBJ_TREE && ctx->info->trees) ||
387 (list->type == OBJ_BLOB && ctx->info->blobs) ||
388 (list->type == OBJ_TAG && ctx->info->tags)) {
389 ret = ctx->info->path_fn(path, &list->oids, list->type,
390 ctx->info->path_fn_data);
391 }
392
393 /*
394 * Expand tree children, except when the set is directly requested
395 * _and_ we are otherwise filtering out trees.
396 */
397 if (list->type == OBJ_TREE &&
398 (!path_is_for_direct_objects(path) || ctx->info->trees)) {
399 /* Use root path if expanding from tagged/direct trees. */
400 const char *expand_path = !strcmp(path, "/tagged-trees")
401 ? root_path : path;
402 for (size_t i = 0; i < list->oids.nr; i++) {
403 ret |= add_tree_entries(ctx,
404 expand_path,
405 &list->oids.oid[i]);
406 }
407 }
408
409 oid_array_clear(&list->oids);
410 strmap_remove(&ctx->paths_to_lists, path, 1);
411 return ret;
412 }
413
414 static void clear_paths_to_lists(struct strmap *map)
415 {
416 struct hashmap_iter iter;
417 struct strmap_entry *e;
418
419 hashmap_for_each_entry(&map->map, &iter, e, ent) {
420 struct type_and_oid_list *list = e->value;
421 oid_array_clear(&list->oids);
422 }
423 strmap_clear(map, 1);
424 strmap_init(map);
425 }
426
427 static struct repository *edge_repo;
428 static struct type_and_oid_list *edge_tree_list;
429
430 static void show_edge(struct commit *commit)
431 {
432 struct tree *t = repo_get_commit_tree(edge_repo, commit);
433
434 if (!t)
435 return;
436
437 if (commit->object.flags & UNINTERESTING)
438 t->object.flags |= UNINTERESTING;
439
440 if (t->object.flags & SEEN)
441 return;
442 t->object.flags |= SEEN;
443
444 oid_array_append(&edge_tree_list->oids, &t->object.oid);
445 }
446
447 static int setup_pending_objects(struct path_walk_info *info,
448 struct path_walk_context *ctx)
449 {
450 struct type_and_oid_list *tags = NULL;
451 struct type_and_oid_list *tagged_blobs = NULL;
452 struct type_and_oid_list *tagged_trees = NULL;
453
454 if (info->tags)
455 CALLOC_ARRAY(tags, 1);
456 CALLOC_ARRAY(tagged_blobs, 1);
457 CALLOC_ARRAY(tagged_trees, 1);
458
459 /*
460 * Pending objects include:
461 * * Commits at branch tips.
462 * * Annotated tags at tag tips.
463 * * Any kind of object at lightweight tag tips.
464 * * Trees and blobs in the index (with an associated path).
465 */
466 for (size_t i = 0; i < info->revs->pending.nr; i++) {
467 struct object_array_entry *pending = info->revs->pending.objects + i;
468 struct object *obj = pending->item;
469
470 /* Commits will be picked up by revision walk. */
471 if (obj->type == OBJ_COMMIT)
472 continue;
473
474 /* Navigate annotated tag object chains. */
475 while (obj->type == OBJ_TAG) {
476 struct tag *tag = lookup_tag(info->revs->repo, &obj->oid);
477 if (!tag) {
478 error(_("failed to find tag %s"),
479 oid_to_hex(&obj->oid));
480 return -1;
481 }
482 if (tag->object.flags & SEEN)
483 break;
484 tag->object.flags |= SEEN;
485
486 if (tags)
487 oid_array_append(&tags->oids, &obj->oid);
488 obj = tag->tagged;
489 }
490
491 if (obj->type == OBJ_TAG)
492 continue;
493
494 /* We are now at a non-tag object. */
495 if (obj->flags & SEEN)
496 continue;
497 obj->flags |= SEEN;
498
499 switch (obj->type) {
500 case OBJ_TREE:
501 if (pending->path && *pending->path) {
502 char *path = xstrfmt("%s/", pending->path);
503 add_path_to_list(ctx, path, OBJ_TREE, &obj->oid, 1);
504 free(path);
505 } else if (!pending->path || !info->trees) {
506 oid_array_append(&tagged_trees->oids, &obj->oid);
507 } else {
508 add_path_to_list(ctx, root_path, OBJ_TREE,
509 &obj->oid, 1);
510 }
511 break;
512
513 case OBJ_BLOB:
514 if (pending->path)
515 add_path_to_list(ctx, pending->path, OBJ_BLOB, &obj->oid, 1);
516 else
517 oid_array_append(&tagged_blobs->oids, &obj->oid);
518 break;
519
520 case OBJ_COMMIT:
521 /* Make sure it is in the object walk */
522 if (obj != pending->item)
523 add_pending_object(info->revs, obj, "");
524 break;
525
526 default:
527 BUG("should not see any other type here");
528 }
529 }
530
531 /*
532 * Add tag objects and tagged blobs if they exist.
533 */
534 if (tagged_blobs) {
535 if (tagged_blobs->oids.nr) {
536 const char *tagged_blob_path = "/tagged-blobs";
537 tagged_blobs->type = OBJ_BLOB;
538 tagged_blobs->maybe_interesting = 1;
539 strmap_put(&ctx->paths_to_lists, tagged_blob_path, tagged_blobs);
540 push_to_stack(ctx, tagged_blob_path);
541 } else {
542 oid_array_clear(&tagged_blobs->oids);
543 free(tagged_blobs);
544 }
545 }
546 if (tagged_trees) {
547 if (tagged_trees->oids.nr) {
548 const char *tagged_tree_path = "/tagged-trees";
549 tagged_trees->type = OBJ_TREE;
550 tagged_trees->maybe_interesting = 1;
551 strmap_put(&ctx->paths_to_lists, tagged_tree_path, tagged_trees);
552 push_to_stack(ctx, tagged_tree_path);
553 } else {
554 oid_array_clear(&tagged_trees->oids);
555 free(tagged_trees);
556 }
557 }
558 if (tags) {
559 if (tags->oids.nr) {
560 const char *tag_path = "/tags";
561 tags->type = OBJ_TAG;
562 tags->maybe_interesting = 1;
563 strmap_put(&ctx->paths_to_lists, tag_path, tags);
564 push_to_stack(ctx, tag_path);
565 } else {
566 oid_array_clear(&tags->oids);
567 free(tags);
568 }
569 }
570
571 return 0;
572 }
573
574 static int prepare_filters_one(struct path_walk_info *info,
575 struct list_objects_filter_options *options)
576 {
577 switch (options->choice) {
578 case LOFC_DISABLED:
579 return 1;
580
581 case LOFC_BLOB_NONE:
582 if (info) {
583 info->blobs = 0;
584 list_objects_filter_release(options);
585 }
586 return 1;
587
588 case LOFC_BLOB_LIMIT:
589 if (info) {
590 if (!options->blob_limit_value)
591 info->blobs = 0;
592 else if (!info->blob_limit ||
593 info->blob_limit > options->blob_limit_value)
594 info->blob_limit = options->blob_limit_value;
595 list_objects_filter_release(options);
596 }
597 return 1;
598
599 case LOFC_TREE_DEPTH:
600 if (options->tree_exclude_depth) {
601 error(_("tree:%lu filter not supported by the path-walk API"),
602 options->tree_exclude_depth);
603 return 0;
604 }
605 if (info) {
606 info->trees = 0;
607 info->blobs = 0;
608 }
609 return 1;
610
611 case LOFC_OBJECT_TYPE:
612 if (info) {
613 info->commits &= options->object_type == OBJ_COMMIT;
614 info->tags &= options->object_type == OBJ_TAG;
615 info->trees &= options->object_type == OBJ_TREE;
616 info->blobs &= options->object_type == OBJ_BLOB;
617 info->strict_types = 1;
618 list_objects_filter_release(options);
619 }
620 return 1;
621
622 case LOFC_SPARSE_OID:
623 if (info) {
624 struct object_id sparse_oid;
625 struct repository *repo = info->revs->repo;
626
627 if (info->pl) {
628 warning(_("sparse filter cannot be combined with existing sparse patterns"));
629 return 0;
630 }
631
632 if (repo_get_oid_with_flags(repo,
633 options->sparse_oid_name,
634 &sparse_oid,
635 GET_OID_BLOB)) {
636 error(_("unable to access sparse blob in '%s'"),
637 options->sparse_oid_name);
638 return 0;
639 }
640
641 CALLOC_ARRAY(info->pl, 1);
642 info->pl->use_cone_patterns = 1;
643
644 if (add_patterns_from_blob_to_list(&sparse_oid, "", 0,
645 info->pl) < 0) {
646 clear_pattern_list(info->pl);
647 FREE_AND_NULL(info->pl);
648 error(_("unable to parse sparse filter data in '%s'"),
649 oid_to_hex(&sparse_oid));
650 return 0;
651 }
652
653 if (!info->pl->use_cone_patterns) {
654 clear_pattern_list(info->pl);
655 FREE_AND_NULL(info->pl);
656 warning(_("sparse filter is not cone-mode compatible"));
657 return 0;
658 }
659 }
660 return 1;
661
662 case LOFC_COMBINE:
663 for (size_t i = 0; i < options->sub_nr; i++) {
664 if (!prepare_filters_one(info, &options->sub[i]))
665 return 0;
666 }
667 return 1;
668
669 default:
670 error(_("object filter '%s' not supported by the path-walk API"),
671 list_objects_filter_spec(options));
672 return 0;
673 }
674 }
675
676 static int prepare_filters(struct path_walk_info *info,
677 struct list_objects_filter_options *options)
678 {
679 if (!prepare_filters_one(info, options))
680 return 0;
681 if (info)
682 list_objects_filter_release(options);
683 return 1;
684 }
685
686 int path_walk_filter_compatible(struct list_objects_filter_options *options)
687 {
688 return prepare_filters(NULL, options);
689 }
690
691 /**
692 * Given the configuration of 'info', walk the commits based on 'info->revs' and
693 * call 'info->path_fn' on each discovered path.
694 *
695 * Returns nonzero on an error.
696 */
697 int walk_objects_by_path(struct path_walk_info *info)
698 {
699 int ret;
700 size_t commits_nr = 0, paths_nr = 0;
701 struct commit *c;
702 struct type_and_oid_list *root_tree_list;
703 struct type_and_oid_list *commit_list;
704 struct path_walk_context ctx = {
705 .repo = info->revs->repo,
706 .revs = info->revs,
707 .info = info,
708 .path_stack = {
709 .compare = compare_by_type,
710 .cb_data = &ctx
711 },
712 .path_stack_pushed = STRSET_INIT,
713 .paths_to_lists = STRMAP_INIT
714 };
715
716 trace2_region_enter("path-walk", "commit-walk", info->revs->repo);
717
718 if (!prepare_filters(info, &info->revs->filter))
719 return -1;
720
721 CALLOC_ARRAY(commit_list, 1);
722 commit_list->type = OBJ_COMMIT;
723
724 if (info->tags)
725 info->revs->tag_objects = 1;
726
727 if (ctx.revs->prune_data.nr) {
728 if (!ctx.revs->prune_data.has_wildcard &&
729 !ctx.revs->prune_data.magic)
730 ctx.exact_pathspecs = 1;
731 }
732
733 /* Insert a single list for the root tree into the paths. */
734 CALLOC_ARRAY(root_tree_list, 1);
735 root_tree_list->type = OBJ_TREE;
736 root_tree_list->maybe_interesting = 1;
737 strmap_put(&ctx.paths_to_lists, root_path, root_tree_list);
738 push_to_stack(&ctx, root_path);
739
740 /*
741 * Ensure that prepare_revision_walk() keeps all pending objects
742 * even through an object type filter.
743 */
744 info->revs->blob_objects = info->revs->tree_objects = 1;
745
746 if (prepare_revision_walk(info->revs))
747 die(_("failed to setup revision walk"));
748
749 info->revs->blob_objects = info->blobs;
750 info->revs->tree_objects = info->trees;
751
752 /*
753 * Walk trees to mark them as UNINTERESTING.
754 * This is particularly important when 'edge_aggressive' is set.
755 */
756 info->revs->edge_hint_aggressive = info->edge_aggressive;
757 edge_repo = info->revs->repo;
758 edge_tree_list = root_tree_list;
759 mark_edges_uninteresting(info->revs, show_edge,
760 info->prune_all_uninteresting);
761 edge_repo = NULL;
762 edge_tree_list = NULL;
763
764 info->revs->blob_objects = info->revs->tree_objects = 0;
765
766 trace2_region_enter("path-walk", "pending-walk", info->revs->repo);
767 ret = setup_pending_objects(info, &ctx);
768 trace2_region_leave("path-walk", "pending-walk", info->revs->repo);
769
770 if (ret)
771 return ret;
772
773 while ((c = get_revision(info->revs))) {
774 struct object_id *oid;
775 struct tree *t;
776 commits_nr++;
777
778 if (info->commits)
779 oid_array_append(&commit_list->oids,
780 &c->object.oid);
781
782 /* If we only care about commits, then skip trees. */
783 if (!info->trees && !info->blobs)
784 continue;
785
786 oid = get_commit_tree_oid(c);
787 t = lookup_tree(info->revs->repo, oid);
788
789 if (!t) {
790 error("could not find tree %s", oid_to_hex(oid));
791 return -1;
792 }
793
794 if (t->object.flags & SEEN)
795 continue;
796 t->object.flags |= SEEN;
797 oid_array_append(&root_tree_list->oids, oid);
798 }
799
800 trace2_data_intmax("path-walk", ctx.repo, "commits", commits_nr);
801 trace2_region_leave("path-walk", "commit-walk", info->revs->repo);
802
803 /* Track all commits. */
804 if (info->commits && commit_list->oids.nr)
805 ret = info->path_fn("", &commit_list->oids, OBJ_COMMIT,
806 info->path_fn_data);
807 oid_array_clear(&commit_list->oids);
808 free(commit_list);
809
810 trace2_region_enter("path-walk", "path-walk", info->revs->repo);
811 while (!ret && ctx.path_stack.nr) {
812 char *path = prio_queue_get(&ctx.path_stack);
813 paths_nr++;
814
815 ret = walk_path(&ctx, path);
816
817 free(path);
818 }
819
820 /* Are there paths remaining? Likely they are from indexed objects. */
821 if (!strmap_empty(&ctx.paths_to_lists)) {
822 struct hashmap_iter iter;
823 struct strmap_entry *entry;
824
825 strmap_for_each_entry(&ctx.paths_to_lists, &iter, entry)
826 push_to_stack(&ctx, entry->key);
827
828 while (!ret && ctx.path_stack.nr) {
829 char *path = prio_queue_get(&ctx.path_stack);
830 paths_nr++;
831
832 ret = walk_path(&ctx, path);
833
834 free(path);
835 }
836 }
837
838 trace2_data_intmax("path-walk", ctx.repo, "paths", paths_nr);
839 trace2_region_leave("path-walk", "path-walk", info->revs->repo);
840
841 clear_paths_to_lists(&ctx.paths_to_lists);
842 strset_clear(&ctx.path_stack_pushed);
843 clear_prio_queue(&ctx.path_stack);
844 return ret;
845 }
846
847 void path_walk_info_init(struct path_walk_info *info)
848 {
849 struct path_walk_info empty = PATH_WALK_INFO_INIT;
850 memcpy(info, &empty, sizeof(empty));
851 }
852
853 void path_walk_info_clear(struct path_walk_info *info)
854 {
855 if (info->pl) {
856 clear_pattern_list(info->pl);
857 free(info->pl);
858 }
859 }