graph: flatten edges that fuse with their right neighbor

When a merge commit is printed and its final parent is the same commit that occupies the column to the right of the merge, this results in a kink in the displayed edges: * | |\ \ | |/ | * Graphs containing these shapes can be hard to read, as the expansion to the right followed immediately by collapsing back to the left creates a lot of zig-zagging edges, especially when many columns are present. We can improve this by eliminating the zig-zag and having the merge's final parent edge fuse immediately with its neighbor: * | |\| | * This reduces the horizontal width for the current commit by 2, and requires one less row, making the graph display more compact. Taken in combination with other graph-smoothing enhancements, it greatly compresses the space needed to display certain histories: * |\ | * * | |\ |\ | | * | * | | | | |\ | | \ | | * | *-. \ | * | | |\ \ \ => |/|\| |/ / / / | | * | | | / | * | | | |/ | |/ | | * * / | * | |/ | |/ * * | |/ * One of the test cases here cannot be correctly rendered in Git v2.23.0; it produces this output following commit E: | | *-. \ 5_E | | |\ \ \ | |/ / / / | | | / _ | |_|/ |/| | The new implementation makes sure that the rightmost edge in this history is not left dangling as above. Signed-off-by: James Coglan <jcoglan@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

James Coglan committed Oct 15, 2019 at 23:47 UTC 92beecc136ad51f358baacf948b4c4b734fd5a4c
3 files changed +86 -34
graph.c
+26 -8
@@ -557,8 +557,24 @@ static void graph_insert_into_new_columns(struct git_graph *graph,
557 shift = (dist > 1) ? 2 * dist - 3 : 1;
558
559 graph->merge_layout = (dist > 0) ? 0 : 1;
560 + graph->edges_added = graph->num_parents + graph->merge_layout - 2;
561 +
562 mapping_idx = graph->width + (graph->merge_layout - 1) * shift;
563 graph->width += 2 * graph->merge_layout;
564 +
565 + } else if (graph->edges_added > 0 && i == graph->mapping[graph->width - 2]) {
566 + /*
567 + * If some columns have been added by a merge, but this commit
568 + * was found in the last existing column, then adjust the
569 + * numbers so that the two edges immediately join, i.e.:
570 + *
571 + * * | * |
572 + * |\ \ => |\|
573 + * | |/ | *
574 + * | *
575 + */
576 + mapping_idx = graph->width - 2;
577 + graph->edges_added = -1;
578 } else {
579 mapping_idx = graph->width;
580 graph->width += 2;
@@ -604,6 +620,8 @@ static void graph_update_columns(struct git_graph *graph)
620 graph->mapping[i] = -1;
621
622 graph->width = 0;
623 + graph->prev_edges_added = graph->edges_added;
624 + graph->edges_added = 0;
625
626 /*
627 * Populate graph->new_columns and graph->mapping
@@ -731,9 +749,6 @@ void graph_update(struct git_graph *graph, struct commit *commit)
749 */
750 graph_update_columns(graph);
751
734 - graph->prev_edges_added = graph->edges_added;
735 - graph->edges_added = graph->num_parents + graph->merge_layout - 2;
736 -
752 graph->expansion_row = 0;
753
754 /*
@@ -1041,7 +1056,7 @@ const char merge_chars[] = {'/', '|', '\\'};
1056 static void graph_output_post_merge_line(struct git_graph *graph, struct graph_line *line)
1057 {
1058 int seen_this = 0;
1044 - int i;
1059 + int i, j;
1060
1061 struct commit_list *first_parent = first_interesting_parent(graph);
1062 int seen_parent = 0;
@@ -1073,16 +1088,19 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
1088 char c;
1089 seen_this = 1;
1090
1076 - for (; parents; parents = next_interesting_parent(graph, parents)) {
1091 + for (j = 0; j < graph->num_parents; j++) {
1092 par_column = graph_find_new_column_by_commit(graph, parents->item);
1093 assert(par_column >= 0);
1094
1095 c = merge_chars[idx];
1096 graph_line_write_column(line, &graph->new_columns[par_column], c);
1082 - if (idx == 2)
1083 - graph_line_addch(line, ' ');
1084 - else
1097 + if (idx == 2) {
1098 + if (graph->edges_added > 0 || j < graph->num_parents - 1)
1099 + graph_line_addch(line, ' ');
1100 + } else {
1101 idx++;
1102 + }
1103 + parents = next_interesting_parent(graph, parents);
1104 }
1105 if (graph->edges_added == 0)
1106 graph_line_addch(line, ' ');
t/t4215-log-skewed-merges.sh
+47 -9
@@ -11,9 +11,8 @@ test_expect_success 'log --graph with merge fusing with its left and right neigh
11 | * G
12 | |\
13 | | * F
14 - | * | E
15 - |/|\ \
16 - | | |/
14 + | * | E
15 + |/|\|
16 | | * D
17 | * | C
18 | |/
@@ -43,9 +42,9 @@ test_expect_success 'log --graph with left-skewed merge' '
42 | | | | * 0_G
43 | |_|_|/|
44 |/| | | |
46 - | | | * | 0_F
47 - | |_|/|\ \
48 - |/| | | |/
45 + | | | * | 0_F
46 + | |_|/|\|
47 + |/| | | |
48 | | | | * 0_E
49 | |_|_|/
50 |/| | |
@@ -153,9 +152,8 @@ test_expect_success 'log --graph with nested right-skewed merge following left-s
152 | | * 3_G
153 | * | 3_F
154 |/| |
156 - | * | 3_E
157 - | |\ \
158 - | | |/
155 + | * | 3_E
156 + | |\|
157 | | * 3_D
158 | * | 3_C
159 | |/
@@ -216,4 +214,44 @@ test_expect_success 'log --graph with right-skewed merge following a left-skewed
214 test_cmp expect actual
215 '
216
217 +test_expect_success 'log --graph with octopus merge with column joining its penultimate parent' '
218 + cat >expect <<-\EOF &&
219 + * 5_H
220 + |\
221 + | *-. 5_G
222 + | |\ \
223 + | | | * 5_F
224 + | | * | 5_E
225 + | |/|\ \
226 + | |_|/ /
227 + |/| | /
228 + | | |/
229 + * | | 5_D
230 + | | * 5_C
231 + | |/
232 + |/|
233 + | * 5_B
234 + |/
235 + * 5_A
236 + EOF
237 +
238 + git checkout --orphan 5_p &&
239 + test_commit 5_A &&
240 + git branch 5_q &&
241 + git branch 5_r &&
242 + test_commit 5_B &&
243 + git checkout 5_q && test_commit 5_C &&
244 + git checkout 5_r && test_commit 5_D &&
245 + git checkout 5_p &&
246 + git merge --no-ff 5_q 5_r -m 5_E &&
247 + git checkout 5_q && test_commit 5_F &&
248 + git checkout -b 5_s 5_p^ &&
249 + git merge --no-ff 5_p 5_q -m 5_G &&
250 + git checkout 5_r &&
251 + git merge --no-ff 5_s -m 5_H &&
252 +
253 + git log --graph --pretty=tformat:%s | sed "s/ *$//" >actual &&
254 + test_cmp expect actual
255 +'
256 +
257 test_done
t/t6016-rev-list-graph-simplify-history.sh
+13 -17
@@ -67,11 +67,10 @@ test_expect_success '--graph --all' '
67 echo "| * $C4" >> expected &&
68 echo "| * $C3" >> expected &&
69 echo "* | $A5" >> expected &&
70 - echo "| | " >> expected &&
71 - echo "| \\ " >> expected &&
72 - echo "*-. \\ $A4" >> expected &&
73 - echo "|\\ \\ \\ " >> expected &&
74 - echo "| | |/ " >> expected &&
70 + echo "| | " >> expected &&
71 + echo "| \\ " >> expected &&
72 + echo "*-. | $A4" >> expected &&
73 + echo "|\\ \\| " >> expected &&
74 echo "| | * $C2" >> expected &&
75 echo "| | * $C1" >> expected &&
76 echo "| * | $B2" >> expected &&
@@ -97,11 +96,10 @@ test_expect_success '--graph --simplify-by-decoration' '
96 echo "| * $C4" >> expected &&
97 echo "| * $C3" >> expected &&
98 echo "* | $A5" >> expected &&
100 - echo "| | " >> expected &&
101 - echo "| \\ " >> expected &&
102 - echo "*-. \\ $A4" >> expected &&
103 - echo "|\\ \\ \\ " >> expected &&
104 - echo "| | |/ " >> expected &&
99 + echo "| | " >> expected &&
100 + echo "| \\ " >> expected &&
101 + echo "*-. | $A4" >> expected &&
102 + echo "|\\ \\| " >> expected &&
103 echo "| | * $C2" >> expected &&
104 echo "| | * $C1" >> expected &&
105 echo "| * | $B2" >> expected &&
@@ -131,9 +129,8 @@ test_expect_success '--graph --simplify-by-decoration prune branch B' '
129 echo "| * $C4" >> expected &&
130 echo "| * $C3" >> expected &&
131 echo "* | $A5" >> expected &&
134 - echo "* | $A4" >> expected &&
135 - echo "|\\ \\ " >> expected &&
136 - echo "| |/ " >> expected &&
132 + echo "* | $A4" >> expected &&
133 + echo "|\\| " >> expected &&
134 echo "| * $C2" >> expected &&
135 echo "| * $C1" >> expected &&
136 echo "* | $A3" >> expected &&
@@ -151,10 +148,9 @@ test_expect_success '--graph --full-history -- bar.txt' '
148 echo "|\\ " >> expected &&
149 echo "| * $C4" >> expected &&
150 echo "* | $A5" >> expected &&
154 - echo "* | $A4" >> expected &&
155 - echo "|\\ \\ " >> expected &&
156 - echo "| |/ " >> expected &&
157 - echo "* / $A3" >> expected &&
151 + echo "* | $A4" >> expected &&
152 + echo "|\\| " >> expected &&
153 + echo "* | $A3" >> expected &&
154 echo "|/ " >> expected &&
155 echo "* $A2" >> expected &&
156 git rev-list --graph --full-history --all -- bar.txt > actual &&