]> git.ipfire.org Git - thirdparty/git.git/blobdiff - list-objects.c
Merge branch 'rs/pull-leakfix'
[thirdparty/git.git] / list-objects.c
index c41cc80db5bc86279bcf560109f599a9783b2755..e19589baa045fe053d9bef285c78513b97f1f34c 100644 (file)
@@ -18,8 +18,7 @@ struct traversal_context {
        show_object_fn show_object;
        show_commit_fn show_commit;
        void *show_data;
-       filter_object_fn filter_fn;
-       void *filter_data;
+       struct filter *filter;
 };
 
 static void process_blob(struct traversal_context *ctx,
@@ -29,7 +28,7 @@ static void process_blob(struct traversal_context *ctx,
 {
        struct object *obj = &blob->object;
        size_t pathlen;
-       enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
+       enum list_objects_filter_result r;
 
        if (!ctx->revs->blob_objects)
                return;
@@ -54,10 +53,10 @@ static void process_blob(struct traversal_context *ctx,
 
        pathlen = path->len;
        strbuf_addstr(path, name);
-       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
-               r = ctx->filter_fn(LOFS_BLOB, obj,
-                                  path->buf, &path->buf[pathlen],
-                                  ctx->filter_data);
+       r = list_objects_filter__filter_object(ctx->revs->repo,
+                                              LOFS_BLOB, obj,
+                                              path->buf, &path->buf[pathlen],
+                                              ctx->filter);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
@@ -113,7 +112,8 @@ static void process_tree_contents(struct traversal_context *ctx,
 
        while (tree_entry(&desc, &entry)) {
                if (match != all_entries_interesting) {
-                       match = tree_entry_interesting(&entry, base, 0,
+                       match = tree_entry_interesting(ctx->revs->repo->index,
+                                                      &entry, base, 0,
                                                       &ctx->revs->diffopt.pathspec);
                        if (match == all_entries_not_interesting)
                                break;
@@ -122,15 +122,25 @@ static void process_tree_contents(struct traversal_context *ctx,
                }
 
                if (S_ISDIR(entry.mode)) {
-                       struct tree *t = lookup_tree(the_repository, entry.oid);
+                       struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);
+                       if (!t) {
+                               die(_("entry '%s' in tree %s has tree mode, "
+                                     "but is not a tree"),
+                                   entry.path, oid_to_hex(&tree->object.oid));
+                       }
                        t->object.flags |= NOT_USER_GIVEN;
                        process_tree(ctx, t, base, entry.path);
                }
                else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(ctx, entry.oid->hash,
+                       process_gitlink(ctx, entry.oid.hash,
                                        base, entry.path);
                else {
-                       struct blob *b = lookup_blob(the_repository, entry.oid);
+                       struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
+                       if (!b) {
+                               die(_("entry '%s' in tree %s has blob mode, "
+                                     "but is not a blob"),
+                                   entry.path, oid_to_hex(&tree->object.oid));
+                       }
                        b->object.flags |= NOT_USER_GIVEN;
                        process_blob(ctx, b, base, entry.path);
                }
@@ -145,7 +155,7 @@ static void process_tree(struct traversal_context *ctx,
        struct object *obj = &tree->object;
        struct rev_info *revs = ctx->revs;
        int baselen = base->len;
-       enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
+       enum list_objects_filter_result r;
        int failed_parse;
 
        if (!revs->tree_objects)
@@ -174,10 +184,10 @@ static void process_tree(struct traversal_context *ctx,
        }
 
        strbuf_addstr(base, name);
-       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
-               r = ctx->filter_fn(LOFS_BEGIN_TREE, obj,
-                                  base->buf, &base->buf[baselen],
-                                  ctx->filter_data);
+       r = list_objects_filter__filter_object(ctx->revs->repo,
+                                              LOFS_BEGIN_TREE, obj,
+                                              base->buf, &base->buf[baselen],
+                                              ctx->filter);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
@@ -190,15 +200,14 @@ static void process_tree(struct traversal_context *ctx,
        else if (!failed_parse)
                process_tree_contents(ctx, tree, base);
 
-       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) {
-               r = ctx->filter_fn(LOFS_END_TREE, obj,
-                                  base->buf, &base->buf[baselen],
-                                  ctx->filter_data);
-               if (r & LOFR_MARK_SEEN)
-                       obj->flags |= SEEN;
-               if (r & LOFR_DO_SHOW)
-                       ctx->show_object(obj, base->buf, ctx->show_data);
-       }
+       r = list_objects_filter__filter_object(ctx->revs->repo,
+                                              LOFS_END_TREE, obj,
+                                              base->buf, &base->buf[baselen],
+                                              ctx->filter);
+       if (r & LOFR_MARK_SEEN)
+               obj->flags |= SEEN;
+       if (r & LOFR_DO_SHOW)
+               ctx->show_object(obj, base->buf, ctx->show_data);
 
        strbuf_setlen(base, baselen);
        free_tree_buffer(tree);
@@ -222,25 +231,73 @@ static void mark_edge_parents_uninteresting(struct commit *commit,
        }
 }
 
-void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
+static void add_edge_parents(struct commit *commit,
+                            struct rev_info *revs,
+                            show_edge_fn show_edge,
+                            struct oidset *set)
+{
+       struct commit_list *parents;
+
+       for (parents = commit->parents; parents; parents = parents->next) {
+               struct commit *parent = parents->item;
+               struct tree *tree = get_commit_tree(parent);
+
+               if (!tree)
+                       continue;
+
+               oidset_insert(set, &tree->object.oid);
+
+               if (!(parent->object.flags & UNINTERESTING))
+                       continue;
+               tree->object.flags |= UNINTERESTING;
+
+               if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
+                       parent->object.flags |= SHOWN;
+                       show_edge(parent);
+               }
+       }
+}
+
+void mark_edges_uninteresting(struct rev_info *revs,
+                             show_edge_fn show_edge,
+                             int sparse)
 {
        struct commit_list *list;
        int i;
 
-       for (list = revs->commits; list; list = list->next) {
-               struct commit *commit = list->item;
+       if (sparse) {
+               struct oidset set;
+               oidset_init(&set, 16);
 
-               if (commit->object.flags & UNINTERESTING) {
-                       mark_tree_uninteresting(revs->repo,
-                                               get_commit_tree(commit));
-                       if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
-                               commit->object.flags |= SHOWN;
-                               show_edge(commit);
+               for (list = revs->commits; list; list = list->next) {
+                       struct commit *commit = list->item;
+                       struct tree *tree = get_commit_tree(commit);
+
+                       if (commit->object.flags & UNINTERESTING)
+                               tree->object.flags |= UNINTERESTING;
+
+                       oidset_insert(&set, &tree->object.oid);
+                       add_edge_parents(commit, revs, show_edge, &set);
+               }
+
+               mark_trees_uninteresting_sparse(revs->repo, &set);
+               oidset_clear(&set);
+       } else {
+               for (list = revs->commits; list; list = list->next) {
+                       struct commit *commit = list->item;
+                       if (commit->object.flags & UNINTERESTING) {
+                               mark_tree_uninteresting(revs->repo,
+                                                       get_commit_tree(commit));
+                               if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
+                                       commit->object.flags |= SHOWN;
+                                       show_edge(commit);
+                               }
+                               continue;
                        }
-                       continue;
+                       mark_edge_parents_uninteresting(commit, revs, show_edge);
                }
-               mark_edge_parents_uninteresting(commit, revs, show_edge);
        }
+
        if (revs->edge_hint_aggressive) {
                for (i = 0; i < revs->cmdline.nr; i++) {
                        struct object *obj = revs->cmdline.rev[i].item;
@@ -308,10 +365,15 @@ static void do_traverse(struct traversal_context *ctx)
                 * an uninteresting boundary commit may not have its tree
                 * parsed yet, but we are not going to show them anyway
                 */
-               if (get_commit_tree(commit)) {
+               if (!ctx->revs->tree_objects)
+                       ; /* do not bother loading tree */
+               else if (get_commit_tree(commit)) {
                        struct tree *tree = get_commit_tree(commit);
                        tree->object.flags |= NOT_USER_GIVEN;
                        add_pending_tree(ctx->revs, tree);
+               } else if (commit->object.parsed) {
+                       die(_("unable to load root tree for commit %s"),
+                             oid_to_hex(&commit->object.oid));
                }
                ctx->show_commit(commit, ctx->show_data);
 
@@ -337,8 +399,7 @@ void traverse_commit_list(struct rev_info *revs,
        ctx.show_commit = show_commit;
        ctx.show_object = show_object;
        ctx.show_data = show_data;
-       ctx.filter_fn = NULL;
-       ctx.filter_data = NULL;
+       ctx.filter = NULL;
        do_traverse(&ctx);
 }
 
@@ -351,17 +412,12 @@ void traverse_commit_list_filtered(
        struct oidset *omitted)
 {
        struct traversal_context ctx;
-       filter_free_fn filter_free_fn = NULL;
 
        ctx.revs = revs;
        ctx.show_object = show_object;
        ctx.show_commit = show_commit;
        ctx.show_data = show_data;
-       ctx.filter_fn = NULL;
-
-       ctx.filter_data = list_objects_filter__init(omitted, filter_options,
-                                                   &ctx.filter_fn, &filter_free_fn);
+       ctx.filter = list_objects_filter__init(omitted, filter_options);
        do_traverse(&ctx);
-       if (ctx.filter_data && filter_free_fn)
-               filter_free_fn(ctx.filter_data);
+       list_objects_filter__free(ctx.filter);
 }