graph: add a 2 commit buffer for lookahead

In a subsequent commit the graph renderer needs to know if the next commit is a visual root or if it is the last commit to be shown. This requires peeking 2 commits ahead. Commits are pre-fetched in get_revision() through next_commit_to_show() where they are also marked as SHOWN, regardless the source they come from. Update graph_is_interesting() so it considers commits inside the lookahead buffer as interesting as well. Helped-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Pablo Sabater committed Jul 14, 2026 at 14:09 UTC bf3c696b44eb82f90b25101e36cd79651d1480f8
3 files changed +84 -2
graph.c
+51
@@ -315,6 +315,14 @@ struct git_graph {
315 * diff_output_prefix_callback().
316 */
317 struct strbuf prefix_buf;
318 +
319 + /*
320 + * Lookahead buffer: up to 2 pre-fetched commits that will be shown.
321 + * Populated by get_revision() so graph_peek_next_visible() can use
322 + * actual walk results instead of peeking at rev_info internals.
323 + */
324 + struct commit *lookahead[2];
325 + int lookahead_nr;
326 };
327
328 static inline int graph_needs_truncation(struct git_graph *graph, int lane)
@@ -388,6 +396,9 @@ struct git_graph *graph_init(struct rev_info *opt)
396 graph->num_columns = 0;
397 graph->num_new_columns = 0;
398 graph->mapping_size = 0;
399 + graph->lookahead[0] = NULL;
400 + graph->lookahead[1] = NULL;
401 + graph->lookahead_nr = 0;
402 /*
403 * Start the column color at the maximum value, since we'll
404 * always increment it for the first commit we output.
@@ -456,6 +467,15 @@ static void graph_ensure_capacity(struct git_graph *graph, int num_columns)
467 */
468 static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
469 {
470 + /*
471 + * Commits in the lookahead buffer have been pre-fetched by
472 + * get_revision() and will be shown in the future. They already have
473 + * the SHOWN flag set when they were pre-fetched but the graph still
474 + * needs to treat them as interesting parents.
475 + */
476 + for (int i = 0; i < graph->lookahead_nr; i++)
477 + if (graph->lookahead[i] == commit)
478 + return 1;
479 /*
480 * If revs->boundary is set, commits whose children have
481 * been shown are always interesting, even if they have the
@@ -763,6 +783,37 @@ static int graph_needs_pre_commit_line(struct git_graph *graph)
783 graph->expansion_row < graph_num_expansion_rows(graph);
784 }
785
786 +struct commit *graph_pop_lookahead(struct git_graph *graph)
787 +{
788 + struct commit *c;
789 +
790 + if (!graph->lookahead_nr)
791 + return NULL;
792 +
793 + c = graph->lookahead[0];
794 + if (!c)
795 + BUG("lookahead buffer has %d entries but the first one is NULL",
796 + graph->lookahead_nr);
797 +
798 + graph->lookahead[0] = graph->lookahead[1];
799 + graph->lookahead[1] = NULL;
800 + graph->lookahead_nr--;
801 + return c;
802 +}
803 +
804 +int graph_get_lookahead_room(struct git_graph *graph)
805 +{
806 + return (int)ARRAY_SIZE(graph->lookahead) - graph->lookahead_nr;
807 +}
808 +
809 +void graph_push_lookahead(struct git_graph *graph, struct commit *c)
810 +{
811 + if (!graph_get_lookahead_room(graph))
812 + BUG("pushing into lookahead buffer when it is already full");
813 +
814 + graph->lookahead[graph->lookahead_nr++] = c;
815 +}
816 +
817 void graph_update(struct git_graph *graph, struct commit *commit)
818 {
819 struct commit_list *parent;
graph.h
+17
@@ -262,4 +262,21 @@ void graph_show_commit_msg(struct git_graph *graph,
262 FILE *file,
263 struct strbuf const *sb);
264
265 +/*
266 + * Pop the first commit from the graph's lookahead buffer.
267 + * Returns NULL if the buffer is empty.
268 + */
269 +struct commit *graph_pop_lookahead(struct git_graph *graph);
270 +
271 +/*
272 + * Returns how many more commits can be added to the lookahead buffer.
273 + */
274 +int graph_get_lookahead_room(struct git_graph *graph);
275 +
276 +/*
277 + * Push a commit into the lookahead buffer. Must only be called when
278 + * graph_get_lookahead_room() returns > 0.
279 + */
280 +void graph_push_lookahead(struct git_graph *graph, struct commit *c);
281 +
282 #endif /* GRAPH_H */
revision.c
+16 -2
@@ -4715,10 +4715,24 @@ struct commit *get_revision(struct rev_info *revs)
4715 return c;
4716 }
4717
4718 - c = next_commit_to_show(revs);
4718 + if (revs->graph) {
4719 + c = graph_pop_lookahead(revs->graph);
4720 + if (!c)
4721 + c = next_commit_to_show(revs);
4722 + } else {
4723 + c = next_commit_to_show(revs);
4724 + }
4725
4720 - if (c && revs->graph)
4726 + if (c && revs->graph) {
4727 + while (graph_get_lookahead_room(revs->graph)) {
4728 + struct commit *next = next_commit_to_show(revs);
4729 + if (!next)
4730 + break;
4731 + graph_push_lookahead(revs->graph, next);
4732 + }
4733 graph_update(revs->graph, c);
4734 + }
4735 +
4736 if (!c) {
4737 free_saved_parents(revs);
4738 commit_list_free(revs->previous_parents);