Skip to content

Commit 7ed10dd

Browse files
committed
Merge branch 'ps/graph-lane-limit' into seen
The graph output from commands like "git log --graph" can now be limited to a specified number of lanes, preventing overly wide output in repositories with many branches. * ps/graph-lane-limit: graph: add documentation and tests about --graph-lane-limit graph: add --graph-lane-limit option
2 parents 3bc77f3 + dedeeeb commit 7ed10dd

5 files changed

Lines changed: 284 additions & 23 deletions

File tree

Documentation/rev-list-options.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,12 @@ This implies the `--topo-order` option by default, but the
12591259
in between them in that case. If _<barrier>_ is specified, it
12601260
is the string that will be shown instead of the default one.
12611261

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 replaced with a truncation mark '~'. By default
1265+
it is set to 0 (no limit), zero and negative values are ignored and
1266+
treated as no limit.
1267+
12621268
ifdef::git-rev-list[]
12631269
`--count`::
12641270
Print a number stating how many commits would have been

graph.c

Lines changed: 127 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,15 @@ struct git_graph {
317317
struct strbuf prefix_buf;
318318
};
319319

320+
static int graph_needs_truncation(struct git_graph *graph, int lane)
321+
{
322+
int max = graph->revs->graph_max_lanes;
323+
/*
324+
* Ignore values <= 0, meaning no limit.
325+
*/
326+
return max > 0 && lane >= max;
327+
}
328+
320329
static const char *diff_output_prefix_callback(struct diff_options *opt, void *data)
321330
{
322331
struct git_graph *graph = data;
@@ -696,6 +705,18 @@ static void graph_update_columns(struct git_graph *graph)
696705
}
697706
}
698707

708+
/*
709+
* If graph_max_lanes is set, cap the padding from the branches
710+
*/
711+
if (graph->revs->graph_max_lanes > 0) {
712+
/*
713+
* width of "| " per lanes plus truncation mark "~ ".
714+
*/
715+
int max_columns_width = graph->revs->graph_max_lanes * 2 + 2;
716+
if (graph->width > max_columns_width)
717+
graph->width = max_columns_width;
718+
}
719+
699720
/*
700721
* Shrink mapping_size to be the minimum necessary
701722
*/
@@ -846,6 +867,10 @@ static void graph_output_padding_line(struct git_graph *graph,
846867
* Output a padding row, that leaves all branch lines unchanged
847868
*/
848869
for (i = 0; i < graph->num_new_columns; i++) {
870+
if (graph_needs_truncation(graph, i)) {
871+
graph_line_addstr(line, "~ ");
872+
break;
873+
}
849874
graph_line_write_column(line, &graph->new_columns[i], '|');
850875
graph_line_addch(line, ' ');
851876
}
@@ -903,6 +928,9 @@ static void graph_output_pre_commit_line(struct git_graph *graph,
903928
seen_this = 1;
904929
graph_line_write_column(line, col, '|');
905930
graph_line_addchars(line, ' ', graph->expansion_row);
931+
} else if (seen_this && graph_needs_truncation(graph, i)) {
932+
graph_line_addstr(line, "~ ");
933+
break;
906934
} else if (seen_this && (graph->expansion_row == 0)) {
907935
/*
908936
* This is the first line of the pre-commit output.
@@ -994,6 +1022,12 @@ static void graph_draw_octopus_merge(struct git_graph *graph, struct graph_line
9941022
col = &graph->new_columns[j];
9951023

9961024
graph_line_write_column(line, col, '-');
1025+
1026+
if (graph_needs_truncation(graph, j / 2 + i)) {
1027+
graph_line_addstr(line, "~ ");
1028+
break;
1029+
}
1030+
9971031
graph_line_write_column(line, col, (i == dashed_parents - 1) ? '.' : '-');
9981032
}
9991033

@@ -1028,8 +1062,17 @@ static void graph_output_commit_line(struct git_graph *graph, struct graph_line
10281062
seen_this = 1;
10291063
graph_output_commit_char(graph, line);
10301064

1065+
if (graph_needs_truncation(graph, i)) {
1066+
graph_line_addch(line, ' ');
1067+
break;
1068+
}
1069+
10311070
if (graph->num_parents > 2)
10321071
graph_draw_octopus_merge(graph, line);
1072+
} else if (graph_needs_truncation(graph, i)) {
1073+
graph_line_addstr(line, "~ ");
1074+
seen_this = 1;
1075+
break;
10331076
} else if (seen_this && (graph->edges_added > 1)) {
10341077
graph_line_write_column(line, col, '\\');
10351078
} else if (seen_this && (graph->edges_added == 1)) {
@@ -1065,10 +1108,32 @@ static void graph_output_commit_line(struct git_graph *graph, struct graph_line
10651108

10661109
/*
10671110
* Update graph->state
1111+
*
1112+
* If the commit is a merge and the first parent is in a visible lane,
1113+
* then the GRAPH_POST_MERGE is needed to draw the merge lane.
1114+
*
1115+
* If the commit is over the truncation limit, but the first parent is on
1116+
* a visible lane, then we still need the merge lane but truncated.
1117+
*
1118+
* If both commit and first parent are over the truncation limit, then
1119+
* there's no need to draw the merge lane because it would work as a
1120+
* padding lane.
10681121
*/
1069-
if (graph->num_parents > 1)
1070-
graph_update_state(graph, GRAPH_POST_MERGE);
1071-
else if (graph_is_mapping_correct(graph))
1122+
if (graph->num_parents > 1) {
1123+
if (!graph_needs_truncation(graph, graph->commit_index)) {
1124+
graph_update_state(graph, GRAPH_POST_MERGE);
1125+
} else {
1126+
struct commit_list *first_parent = first_interesting_parent(graph);
1127+
int first_parent_col = graph_find_new_column_by_commit(graph, first_parent->item);
1128+
1129+
if (!graph_needs_truncation(graph, first_parent_col))
1130+
graph_update_state(graph, GRAPH_POST_MERGE);
1131+
else if (graph_is_mapping_correct(graph))
1132+
graph_update_state(graph, GRAPH_PADDING);
1133+
else
1134+
graph_update_state(graph, GRAPH_COLLAPSING);
1135+
}
1136+
} else if (graph_is_mapping_correct(graph))
10721137
graph_update_state(graph, GRAPH_PADDING);
10731138
else
10741139
graph_update_state(graph, GRAPH_COLLAPSING);
@@ -1109,6 +1174,7 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
11091174
int par_column;
11101175
int idx = graph->merge_layout;
11111176
char c;
1177+
int truncated = 0;
11121178
seen_this = 1;
11131179

11141180
for (j = 0; j < graph->num_parents; j++) {
@@ -1117,23 +1183,46 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
11171183

11181184
c = merge_chars[idx];
11191185
graph_line_write_column(line, &graph->new_columns[par_column], c);
1186+
if (graph_needs_truncation(graph, j / 2 + i) &&
1187+
j / 2 + i <= graph->num_columns) {
1188+
if ((j + i * 2) % 2 != 0)
1189+
graph_line_addch(line, ' ');
1190+
graph_line_addstr(line, "~ ");
1191+
truncated = 1;
1192+
break;
1193+
}
1194+
11201195
if (idx == 2) {
1121-
if (graph->edges_added > 0 || j < graph->num_parents - 1)
1196+
if (graph_needs_truncation(graph, (j + 1) / 2 + i) &&
1197+
j < graph->num_parents - 1) {
1198+
graph_line_addstr(line, "~ ");
1199+
truncated = 1;
1200+
break;
1201+
} else if (graph->edges_added > 0 || j < graph->num_parents - 1)
11221202
graph_line_addch(line, ' ');
11231203
} else {
11241204
idx++;
11251205
}
11261206
parents = next_interesting_parent(graph, parents);
11271207
}
1208+
if (truncated)
1209+
break;
11281210
if (graph->edges_added == 0)
11291211
graph_line_addch(line, ' ');
1130-
1212+
} else if (graph_needs_truncation(graph, i)) {
1213+
graph_line_addstr(line, "~ ");
1214+
break;
11311215
} else if (seen_this) {
11321216
if (graph->edges_added > 0)
11331217
graph_line_write_column(line, col, '\\');
11341218
else
11351219
graph_line_write_column(line, col, '|');
1136-
graph_line_addch(line, ' ');
1220+
/*
1221+
* If it's between two lanes and next would be truncated,
1222+
* don't add space padding.
1223+
*/
1224+
if (!graph_needs_truncation(graph, i + 1))
1225+
graph_line_addch(line, ' ');
11371226
} else {
11381227
graph_line_write_column(line, col, '|');
11391228
if (graph->merge_layout != 0 || i != graph->commit_index - 1) {
@@ -1164,6 +1253,7 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct graph_l
11641253
short used_horizontal = 0;
11651254
int horizontal_edge = -1;
11661255
int horizontal_edge_target = -1;
1256+
int truncated = 0;
11671257

11681258
/*
11691259
* Swap the mapping and old_mapping arrays
@@ -1279,26 +1369,35 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct graph_l
12791369
*/
12801370
for (i = 0; i < graph->mapping_size; i++) {
12811371
int target = graph->mapping[i];
1282-
if (target < 0)
1283-
graph_line_addch(line, ' ');
1284-
else if (target * 2 == i)
1285-
graph_line_write_column(line, &graph->new_columns[target], '|');
1286-
else if (target == horizontal_edge_target &&
1287-
i != horizontal_edge - 1) {
1288-
/*
1289-
* Set the mappings for all but the
1290-
* first segment to -1 so that they
1291-
* won't continue into the next line.
1292-
*/
1293-
if (i != (target * 2)+3)
1294-
graph->mapping[i] = -1;
1295-
used_horizontal = 1;
1296-
graph_line_write_column(line, &graph->new_columns[target], '_');
1372+
1373+
if (!truncated && graph_needs_truncation(graph, i / 2)) {
1374+
graph_line_addstr(line, "~ ");
1375+
truncated = 1;
1376+
}
1377+
1378+
if (target < 0) {
1379+
if (!truncated)
1380+
graph_line_addch(line, ' ');
1381+
} else if (target * 2 == i) {
1382+
if (!truncated)
1383+
graph_line_write_column(line, &graph->new_columns[target], '|');
1384+
} else if (target == horizontal_edge_target &&
1385+
i != horizontal_edge - 1) {
1386+
/*
1387+
* Set the mappings for all but the
1388+
* first segment to -1 so that they
1389+
* won't continue into the next line.
1390+
*/
1391+
if (i != (target * 2)+3)
1392+
graph->mapping[i] = -1;
1393+
used_horizontal = 1;
1394+
if (!truncated)
1395+
graph_line_write_column(line, &graph->new_columns[target], '_');
12971396
} else {
12981397
if (used_horizontal && i < horizontal_edge)
12991398
graph->mapping[i] = -1;
1300-
graph_line_write_column(line, &graph->new_columns[target], '/');
1301-
1399+
if (!truncated)
1400+
graph_line_write_column(line, &graph->new_columns[target], '/');
13021401
}
13031402
}
13041403

@@ -1372,6 +1471,11 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
13721471
for (i = 0; i < graph->num_columns; i++) {
13731472
struct column *col = &graph->columns[i];
13741473

1474+
if (graph_needs_truncation(graph, i)) {
1475+
graph_line_addch(&line, '~');
1476+
break;
1477+
}
1478+
13751479
graph_line_write_column(&line, col, '|');
13761480

13771481
if (col->commit == graph->commit && graph->num_parents > 2) {

revision.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,6 +2605,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
26052605
} else if (!strcmp(arg, "--no-graph")) {
26062606
graph_clear(revs->graph);
26072607
revs->graph = NULL;
2608+
} else if (skip_prefix(arg, "--graph-lane-limit=", &optarg)) {
2609+
revs->graph_max_lanes = parse_count(optarg);
26082610
} else if (!strcmp(arg, "--encode-email-headers")) {
26092611
revs->encode_email_headers = 1;
26102612
} else if (!strcmp(arg, "--no-encode-email-headers")) {
@@ -3174,6 +3176,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
31743176

31753177
if (revs->no_walk && revs->graph)
31763178
die(_("options '%s' and '%s' cannot be used together"), "--no-walk", "--graph");
3179+
3180+
if (revs->graph_max_lanes > 0 && !revs->graph)
3181+
die(_("the option '%s' requires '%s'"), "--graph-lane-limit", "--graph");
3182+
31773183
if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
31783184
die(_("the option '%s' requires '%s'"), "--grep-reflog", "--walk-reflogs");
31793185

revision.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ struct rev_info {
305305

306306
/* Display history graph */
307307
struct git_graph *graph;
308+
int graph_max_lanes;
308309

309310
/* special limits */
310311
int skip_count;

0 commit comments

Comments
 (0)