negotiator/skipping: avoid stack overflow

mark_common() in negotiator/skipping.c may overflow the stack due to recursive function calls. Avoid this by instead recursing using a heap-allocated data structure. Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Tan committed Oct 25, 2022 at 16:29 UTC 4654134976fd4d80ea664c159f798f21c7917d8c
1 file changed +17 -12
negotiator/skipping.c
+17 -12
@@ -86,21 +86,26 @@ static int clear_marks(const char *refname, const struct object_id *oid,
86 /*
87 * Mark this SEEN commit and all its SEEN ancestors as COMMON.
88 */
89 -static void mark_common(struct data *data, struct commit *c)
89 +static void mark_common(struct data *data, struct commit *seen_commit)
90 {
91 - struct commit_list *p;
91 + struct prio_queue queue = { NULL };
92 + struct commit *c;
93
93 - if (c->object.flags & COMMON)
94 - return;
95 - c->object.flags |= COMMON;
96 - if (!(c->object.flags & POPPED))
97 - data->non_common_revs--;
94 + prio_queue_put(&queue, seen_commit);
95 + while ((c = prio_queue_get(&queue))) {
96 + struct commit_list *p;
97 + if (c->object.flags & COMMON)
98 + return;
99 + c->object.flags |= COMMON;
100 + if (!(c->object.flags & POPPED))
101 + data->non_common_revs--;
102
99 - if (!c->object.parsed)
100 - return;
101 - for (p = c->parents; p; p = p->next) {
102 - if (p->item->object.flags & SEEN)
103 - mark_common(data, p->item);
103 + if (!c->object.parsed)
104 + return;
105 + for (p = c->parents; p; p = p->next) {
106 + if (p->item->object.flags & SEEN)
107 + prio_queue_put(&queue, p->item);
108 + }
109 }
110 }
111