]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'hx/negotiator-non-recursive'
authorJunio C Hamano <gitster@pobox.com>
Wed, 10 May 2023 17:23:27 +0000 (10:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 10 May 2023 17:23:28 +0000 (10:23 -0700)
The implementation of the default "negotiator", used to find common
ancestor over the network for object tranfer, used to be recursive;
it was updated to be iterative to conserve stackspace usage.

* hx/negotiator-non-recursive:
  negotiator/skipping: fix some problems in mark_common()
  negotiator/default: avoid stack overflow

negotiator/default.c
negotiator/skipping.c

index 5cb2b1d2046a152d782250aeffaee52947297878..9a5b69632728d1d6877556a330e666ba530a894a 100644 (file)
@@ -56,30 +56,49 @@ static int clear_marks(const char *refname, const struct object_id *oid,
 static void mark_common(struct negotiation_state *ns, struct commit *commit,
                int ancestors_only, int dont_parse)
 {
-       if (commit != NULL && !(commit->object.flags & COMMON)) {
-               struct object *o = (struct object *)commit;
+       struct prio_queue queue = { NULL };
+
+       if (!commit || (commit->object.flags & COMMON))
+               return;
+
+       prio_queue_put(&queue, commit);
+       if (!ancestors_only) {
+               commit->object.flags |= COMMON;
 
-               if (!ancestors_only)
-                       o->flags |= COMMON;
+               if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED))
+                       ns->non_common_revs--;
+       }
+       while ((commit = prio_queue_get(&queue))) {
+               struct object *o = (struct object *)commit;
 
                if (!(o->flags & SEEN))
                        rev_list_push(ns, commit, SEEN);
                else {
                        struct commit_list *parents;
 
-                       if (!ancestors_only && !(o->flags & POPPED))
-                               ns->non_common_revs--;
                        if (!o->parsed && !dont_parse)
                                if (repo_parse_commit(the_repository, commit))
-                                       return;
+                                       continue;
 
                        for (parents = commit->parents;
                                        parents;
-                                       parents = parents->next)
-                               mark_common(ns, parents->item, 0,
-                                           dont_parse);
+                                       parents = parents->next) {
+                               struct commit *p = parents->item;
+
+                               if (p->object.flags & COMMON)
+                                       continue;
+
+                               p->object.flags |= COMMON;
+
+                               if ((p->object.flags & SEEN) && !(p->object.flags & POPPED))
+                                       ns->non_common_revs--;
+
+                               prio_queue_put(&queue, parents->item);
+                       }
                }
        }
+
+       clear_prio_queue(&queue);
 }
 
 /*
index 97e7e1ae72521c1cc4338e19836293dbfdeccf29..5b91520430c1703762e8712e632850420cba7be8 100644 (file)
@@ -86,29 +86,37 @@ static int clear_marks(const char *refname, const struct object_id *oid,
 }
 
 /*
- * Mark this SEEN commit and all its SEEN ancestors as COMMON.
+ * Mark this SEEN commit and all its parsed SEEN ancestors as COMMON.
  */
 static void mark_common(struct data *data, struct commit *seen_commit)
 {
        struct prio_queue queue = { NULL };
        struct commit *c;
 
+       if (seen_commit->object.flags & COMMON)
+               return;
+
        prio_queue_put(&queue, seen_commit);
+       seen_commit->object.flags |= COMMON;
        while ((c = prio_queue_get(&queue))) {
                struct commit_list *p;
-               if (c->object.flags & COMMON)
-                       return;
-               c->object.flags |= COMMON;
+
                if (!(c->object.flags & POPPED))
                        data->non_common_revs--;
 
                if (!c->object.parsed)
-                       return;
+                       continue;
                for (p = c->parents; p; p = p->next) {
-                       if (p->item->object.flags & SEEN)
-                               prio_queue_put(&queue, p->item);
+                       if (!(p->item->object.flags & SEEN) ||
+                           (p->item->object.flags & COMMON))
+                               continue;
+
+                       p->item->object.flags |= COMMON;
+                       prio_queue_put(&queue, p->item);
                }
        }
+
+       clear_prio_queue(&queue);
 }
 
 /*