graph: add truncation mark to capped lanes

When lanes are hidden by --graph-lane-limit, show a "~" truncation mark, so users know that there are lanes being truncated. The "~" is chosen because it is not used elsewhere in the graph and it is discrete. Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Pablo Sabater committed Mar 28, 2026 at 01:11 UTC 9bab3ce5553b2333b8f8ee1aff27a9fe6a938f65
3 files changed +52 -39
Documentation/rev-list-options.adoc
+3 -2
@@ -1261,8 +1261,9 @@ This implies the `--topo-order` option by default, but the
1261
1262 `--graph-lane-limit=<n>`::
1263 When `--graph` is used, limit the number of graph lanes to be shown.
1264 - Lanes over the limit are not shown. By default it is set to 0
1265 - (no limit), zero and negative values are ignored and treated as no limit.
1264 + Lanes over the limit are replaced with a truncation mark '~'.
1265 + By default it is set to 0 (no limit), zero and negative values
1266 + are ignored and treated as no limit.
1267
1268 ifdef::git-rev-list[]
1269 `--count`::
graph.c
+17 -5
@@ -706,11 +706,11 @@ static void graph_update_columns(struct git_graph *graph)
706 }
707
708 /*
709 - * If graph_max_lanes is set, cap the width
709 + * If graph_max_lanes is set, cap the width
710 */
711 if (graph->revs->graph_max_lanes > 0) {
712 /*
713 - * Width is column index while a lane is half that.
713 + * width of "| " per lanes plus truncation mark "~ ".
714 * Allow commits from merges to align to the merged lane.
715 */
716 int max_width = graph->revs->graph_max_lanes * 2 + 2;
@@ -868,8 +868,10 @@ static void graph_output_padding_line(struct git_graph *graph,
868 * Output a padding row, that leaves all branch lines unchanged
869 */
870 for (i = 0; i < graph->num_new_columns; i++) {
871 - if (graph_needs_truncation(graph, i))
871 + if (graph_needs_truncation(graph, i)) {
872 + graph_line_addstr(line, "~ ");
873 break;
874 + }
875 graph_line_write_column(line, &graph->new_columns[i], '|');
876 graph_line_addch(line, ' ');
877 }
@@ -928,6 +930,7 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
930 graph_line_write_column(line, col, '|');
931 graph_line_addchars(line, ' ', graph->expansion_row);
932 } else if (seen_this && graph_needs_truncation(graph, i)) {
933 + graph_line_addstr(line, "~ ");
934 break;
935 } else if (seen_this && (graph->expansion_row == 0)) {
936 /*
@@ -1025,8 +1028,10 @@ static void graph_draw_octopus_merge(struct git_graph *graph, struct graph_line
1028 * Commit is at commit_index, each iteration move one lane to
1029 * the right from the commit.
1030 */
1028 - if (graph_needs_truncation(graph, graph->commit_index + 1 + i))
1031 + if (graph_needs_truncation(graph, graph->commit_index + 1 + i)) {
1032 + graph_line_addstr(line, "~ ");
1033 break;
1034 + }
1035
1036 graph_line_write_column(line, col, (i == dashed_parents - 1) ? '.' : '-');
1037 }
@@ -1070,6 +1075,7 @@ static void graph_output_commit_line(struct git_graph *graph, struct graph_line
1075 if (graph->num_parents > 2)
1076 graph_draw_octopus_merge(graph, line);
1077 } else if (graph_needs_truncation(graph, i)) {
1078 + graph_line_addstr(line, "~ ");
1079 seen_this = 1;
1080 break;
1081 } else if (seen_this && (graph->edges_added > 1)) {
@@ -1203,6 +1209,7 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
1209 j / 2 + i <= graph->num_columns) {
1210 if ((j + i * 2) % 2 != 0)
1211 graph_line_addch(line, ' ');
1212 + graph_line_addstr(line, "~ ");
1213 truncated = 1;
1214 break;
1215 }
@@ -1214,6 +1221,7 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
1221 */
1222 if (graph_needs_truncation(graph, (j + 1) / 2 + i) &&
1223 j < graph->num_parents - 1) {
1224 + graph_line_addstr(line, "~ ");
1225 truncated = 1;
1226 break;
1227 } else if (graph->edges_added > 0 || j < graph->num_parents - 1)
@@ -1228,6 +1236,7 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
1236 if (graph->edges_added == 0)
1237 graph_line_addch(line, ' ');
1238 } else if (graph_needs_truncation(graph, i)) {
1239 + graph_line_addstr(line, "~ ");
1240 break;
1241 } else if (seen_this) {
1242 if (graph->edges_added > 0)
@@ -1388,6 +1397,7 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct graph_l
1397 int target = graph->mapping[i];
1398
1399 if (!truncated && graph_needs_truncation(graph, i / 2)) {
1400 + graph_line_addstr(line, "~ ");
1401 truncated = 1;
1402 }
1403
@@ -1487,8 +1497,10 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
1497 for (i = 0; i < graph->num_columns; i++) {
1498 struct column *col = &graph->columns[i];
1499
1490 - if (graph_needs_truncation(graph, i))
1500 + if (graph_needs_truncation(graph, i)) {
1501 + graph_line_addstr(&line, "~ ");
1502 break;
1503 + }
1504
1505 graph_line_write_column(&line, col, '|');
1506
t/t4215-log-skewed-merges.sh
+32 -32
@@ -376,9 +376,9 @@ test_expect_success 'log --graph --graph-lane-limit=2 limited to two lanes' '
376 |\ \
377 | | * 7_G
378 | | * 7_F
379 - | * 7_E
380 - | * 7_D
381 - * | 7_C
379 + | * ~ 7_E
380 + | * ~ 7_D
381 + * | ~ 7_C
382 | |/
383 |/|
384 * | 7_B
@@ -389,16 +389,16 @@ test_expect_success 'log --graph --graph-lane-limit=2 limited to two lanes' '
389
390 test_expect_success 'log --graph --graph-lane-limit=1 truncate mid octopus merge' '
391 check_graph --graph-lane-limit=1 M_7 <<-\EOF
392 - *- 7_M4
393 - |\
394 - | 7_G
395 - | 7_F
392 + *-~ 7_M4
393 + |\~
394 + | ~ 7_G
395 + | ~ 7_F
396 | * 7_E
397 | * 7_D
398 - * 7_C
399 - |
400 - |/
401 - * 7_B
398 + * ~ 7_C
399 + | ~
400 + |/~
401 + * ~ 7_B
402 |/
403 * 7_A
404 EOF
@@ -411,24 +411,24 @@ test_expect_success 'log --graph --graph-lane-limit=3 limited to three lanes' '
411 | | * 7_M2
412 | | |\
413 | | | * 7_H
414 - | | | 7_M3
415 - | | | 7_J
416 - | | | 7_I
417 - | | | 7_M4
418 - | |_|_
419 - |/| |
420 - | | |_
421 - | |/|
422 - | | |
423 - | | |/
424 - | | * 7_G
425 - | | |
426 - | | |/
427 - | | * 7_F
428 - | * | 7_E
429 - | | |/
430 - | |/|
431 - | * | 7_D
414 + | | | ~ 7_M3
415 + | | | ~ 7_J
416 + | | | ~ 7_I
417 + | | | ~ 7_M4
418 + | |_|_~
419 + |/| | ~
420 + | | |_~
421 + | |/| ~
422 + | | | ~
423 + | | |/~
424 + | | * ~ 7_G
425 + | | | ~
426 + | | |/~
427 + | | * ~ 7_F
428 + | * | ~ 7_E
429 + | | |/~
430 + | |/| ~
431 + | * | ~ 7_D
432 | | |/
433 | |/|
434 * | | 7_C
@@ -452,9 +452,9 @@ test_expect_success 'log --graph --graph-lane-limit=6 check if it only shows fir
452 | | | | | * 7_J
453 | | | | * | 7_I
454 | | | | | | * 7_M4
455 - | |_|_|_|_|/
456 - |/| | | | |/
457 - | | |_|_|/|
455 + | |_|_|_|_|/~
456 + |/| | | | |/~
457 + | | |_|_|/| ~
458 | |/| | | |/
459 | | | |_|/|
460 | | |/| | |