From: Derrick Stolee Date: Mon, 22 Jun 2026 18:10:27 GMT Subject: Re: [PATCH/RFC 2/6] commit-reach: introduce struct paint_queue with per-side counters Message-ID: In-Reply-To: <316e4dfe261043730c77142639f86f5c3cabe370.1781951820.git.gitgitgadget@gmail.com> On 6/20/2026 6:36 AM, Kristofer Karlsson via GitGitGadget wrote: > From: Kristofer Karlsson > + if (!(old_paint & STALE)) { > + switch (old_paint & (PARENT1 | PARENT2)) { > + case 0: break; > + case PARENT1: queue->p1_count--; break; > + case PARENT2: queue->p2_count--; break; > + case PARENT1 | PARENT2: queue->pending_merge_bases--; break; > + default: BUG("unexpected paint state"); > + } > + } > + if (!(new_paint & STALE)) { > + switch (new_paint & (PARENT1 | PARENT2)) { > + case 0: break; > + case PARENT1: queue->p1_count++; break; > + case PARENT2: queue->p2_count++; break; > + case PARENT1 | PARENT2: queue->pending_merge_bases++; break; > + default: BUG("unexpected paint state"); > + } > + } While correct and compact, I don't believe that these switch statements follow the coding guidelines. We should split the lines appropriately so they are more standard, such as: if (!(new_paint & STALE)) { switch (new_paint & (PARENT1 | PARENT2)) { case 0: break; case PARENT1: queue->p1_count++; break; case PARENT2: queue->p2_count++; break; case PARENT1 | PARENT2: queue->pending_merge_bases++; break; default: BUG("unexpected paint state"); } } Also: technically "case 0" should be a BUG() state, right? We shouldn't be walking any commit that isn't reachable from at least one side. (case 0 does happen for old_paint, though.) > } > > -static void clear_nonstale_queue(struct nonstale_queue *queue) > +static void paint_queue_put(struct paint_queue *queue, > + struct commit *c, unsigned add_flags) > { > - clear_prio_queue(&queue->pq); > - queue->max_nonstale = NULL; > -} > + unsigned old_flags = c->object.flags; > + c->object.flags |= add_flags; Diffs like this are part of the reason I'd like to see a _new_ data structure instead of replacing the old one. Keeping the old one for ahead_behind seems like a good idea to me, but even if we don't land on that end state then deleting the old code _after_ adding the new code will make the diff more readable. > - struct nonstale_queue queue = { > - { compare_commits_by_gen_then_commit_date } > + struct paint_queue queue = { > + .pq = { compare_commits_by_gen_then_commit_date } > }; I didn't notice when reading the struct definition, but looking at 'pq' here makes me think that we shouldn't be using that abbreviation as it could stand for "prio_queue" or "paint_queue". > + while ((commit = paint_queue_get(&queue))) { ...> + > + if (queue.p1_count + queue.p2_count + > + queue.pending_merge_bases == 0) > + break; > } When possible, I like to try to make loops only have one terminating condition. Should we have paint_queue_get() return NULL when it sees this internal state condition? Also, I'd rather see it of the form of (!count) instead of using addition to make it clear that we care about each value being zero. Finally, I think we actually want this case to get the benefit: if ((!queue.p1_count || !queue.p2_count) && !queue.pending_merge_bases) I do see that you have this condition in patch 3 with the extra detail that the max generation in the queue is finite. I think this is more reason to include this in the data structure method and not in the loop. Thanks, -Stolee