Before this commit, graphs with merges have an empty line after a merge commit to make room for a new branch. But this causes the child commits (commits merged with the merge commit) to be separated from the merge commit. Especially with the --tree option this is confusing, as there is no empty line after a series of child commits anymore (because it is a tree after all). Therefore this change modifies the merge point to not need an extra line (the POST_MERGE state is removed), using an underscore to branch out instead of a '\'. It also optimizes the case where a branch point meets a merge commit. Signed-off-by: Micha Nelissen <nelissen.micha@xxxxxxxxx> --- graph.c | 281 +++++++++++++++++--------------------------------------- 1 file changed, 85 insertions(+), 196 deletions(-) diff --git graph.c graph.c index f53135485f..f0e30bce62 100644 --- graph.c +++ graph.c @@ -63,7 +63,6 @@ enum graph_state { GRAPH_SKIP, GRAPH_PRE_COMMIT, GRAPH_COMMIT, - GRAPH_POST_MERGE, GRAPH_COLLAPSING }; @@ -153,12 +152,6 @@ struct git_graph { * This tells us what kind of line graph_next_line() should output. */ enum graph_state state; - /* - * The output state for the previous line of output. - * This is primarily used to determine how the first merge line - * should appear, based on the last line of the previous commit. - */ - enum graph_state prev_state; /* * The index of the column that refers to this commit. * @@ -167,13 +160,9 @@ struct git_graph { */ int commit_index; /* - * The commit_index for the previously displayed commit. - * - * This is used to determine how the first line of a merge - * graph output should appear, based on the last line of the - * previous commit. + * If previous line was a merge commit, then this is its commit index */ - int prev_commit_index; + int prev_merge_index; /* * The maximum number of columns that can be stored in the columns * and new_columns arrays. This is also half the number of entries @@ -282,9 +271,7 @@ struct git_graph *graph_init(struct rev_info *opt) graph->num_parents = 0; graph->expansion_row = 0; graph->state = GRAPH_PADDING; - graph->prev_state = GRAPH_PADDING; graph->commit_index = 0; - graph->prev_commit_index = 0; graph->num_columns = 0; graph->num_new_columns = 0; graph->mapping_size = 0; @@ -317,7 +304,6 @@ struct git_graph *graph_init(struct rev_info *opt) static void graph_update_state(struct git_graph *graph, enum graph_state s) { - graph->prev_state = graph->state; graph->state = s; } @@ -602,6 +588,13 @@ static void graph_update_columns(struct git_graph *graph) graph_update_width(graph, is_commit_in_columns); } +static int need_pre_commit(struct git_graph *graph) +{ + return (graph->num_parents == 2 && graph->prev_merge_index != 0xffff) + || (graph->num_parents >= 3 && + graph->commit_index < (graph->num_columns - 1)); +} + void graph_update(struct git_graph *graph, struct commit *commit) { struct commit_list *parent; @@ -622,13 +615,6 @@ void graph_update(struct git_graph *graph, struct commit *commit) graph->num_parents++; } - /* - * Store the old commit_index in prev_commit_index. - * graph_update_columns() will update graph->commit_index for this - * commit. - */ - graph->prev_commit_index = graph->commit_index; - /* * Call graph_update_columns() to update * columns, new_columns, and mapping. @@ -639,9 +625,6 @@ void graph_update(struct git_graph *graph, struct commit *commit) /* * Update graph->state. - * Note that we don't call graph_update_state() here, since - * we don't want to update graph->prev_state. No line for - * graph->state was ever printed. * * If the previous commit didn't get to the GRAPH_PADDING state, * it never finished its output. Goto GRAPH_SKIP, to print out @@ -657,8 +640,7 @@ void graph_update(struct git_graph *graph, struct commit *commit) */ if (graph->state != GRAPH_PADDING) graph->state = GRAPH_SKIP; - else if (graph->num_parents >= 3 && - graph->commit_index < (graph->num_columns - 1)) + else if (need_pre_commit(graph)) graph->state = GRAPH_PRE_COMMIT; else graph->state = GRAPH_COMMIT; @@ -742,8 +724,7 @@ static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb) strbuf_addstr(sb, "..."); graph_pad_horizontally(graph, sb, 3); - if (graph->num_parents >= 3 && - graph->commit_index < (graph->num_columns - 1)) + if (need_pre_commit(graph)) graph_update_state(graph, GRAPH_PRE_COMMIT); else graph_update_state(graph, GRAPH_COMMIT); @@ -763,14 +744,16 @@ static void graph_output_pre_commit_line(struct git_graph *graph, * * We need 2 extra rows for every parent over 2. */ - assert(graph->num_parents >= 3); + assert(graph->num_parents >= 2); num_expansion_rows = (graph->num_parents - 2) * 2; + if (num_expansion_rows == 0) + graph->expansion_row = -1; /* * graph->expansion_row tracks the current expansion row we are on. * It should be in the range [0, num_expansion_rows - 1] */ - assert(0 <= graph->expansion_row && + assert(-1 <= graph->expansion_row && graph->expansion_row < num_expansion_rows); /* @@ -783,25 +766,9 @@ static void graph_output_pre_commit_line(struct git_graph *graph, if (col->commit == graph->commit) { seen_this = 1; strbuf_write_column(sb, col, '|'); - strbuf_addchars(sb, ' ', graph->expansion_row); - chars_written += 1 + graph->expansion_row; - } else if (seen_this && (graph->expansion_row == 0)) { - /* - * This is the first line of the pre-commit output. - * If the previous commit was a merge commit and - * ended in the GRAPH_POST_MERGE state, all branch - * lines after graph->prev_commit_index were - * printed as "\" on the previous line. Continue - * to print them as "\" on this line. Otherwise, - * print the branch lines as "|". - */ - if (graph->prev_state == GRAPH_POST_MERGE && - graph->prev_commit_index < i) - strbuf_write_column(sb, col, '\\'); - else - strbuf_write_column(sb, col, '|'); - chars_written++; - } else if (seen_this && (graph->expansion_row > 0)) { + strbuf_addchars(sb, ' ', graph->expansion_row + 1); + chars_written += 2 + graph->expansion_row; + } else if (seen_this) { strbuf_write_column(sb, col, '\\'); chars_written++; } else { @@ -887,8 +854,7 @@ static int graph_draw_octopus_merge(struct git_graph *graph, int i; for (i = 0; i < dashful_parents; i++) { strbuf_write_column(sb, &graph->new_columns[i+first_col], '-'); - strbuf_write_column(sb, &graph->new_columns[i+first_col], - i == dashful_parents-1 ? '.' : '-'); + strbuf_write_column(sb, &graph->new_columns[i+first_col], '-'); } return 2 * dashful_parents; } @@ -896,7 +862,8 @@ static int graph_draw_octopus_merge(struct git_graph *graph, static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; - int i, chars_written; + int i, j, k, chars_written; + char draw_ch; /* * Output the row containing this commit @@ -910,6 +877,7 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) for (i = 0; i <= graph->num_columns; i++) { struct column *col = &graph->columns[i]; struct commit *col_commit; + int add_space = 1; if (i == graph->num_columns) { if (seen_this) break; @@ -923,138 +891,76 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) graph_output_commit_char(graph, sb); chars_written++; - if (graph->num_parents > 2) + draw_ch = graph->num_parents == 2 ? '_' : 0; + if (graph->num_parents > 2) { chars_written += graph_draw_octopus_merge(graph, sb); - } else if (seen_this && (graph->num_parents > 2)) { - strbuf_write_column(sb, col, '\\'); - chars_written++; - } else if (seen_this && (graph->num_parents == 2)) { - /* - * This is a 2-way merge commit. - * There is no GRAPH_PRE_COMMIT stage for 2-way - * merges, so this is the first line of output - * for this commit. Check to see what the previous - * line of output was. - * - * If it was GRAPH_POST_MERGE, the branch line - * coming into this commit may have been '\', - * and not '|' or '/'. If so, output the branch - * line as '\' on this line, instead of '|'. This - * makes the output look nicer. - */ - if (graph->prev_state == GRAPH_POST_MERGE && - graph->prev_commit_index < i) - strbuf_write_column(sb, col, '\\'); - else - strbuf_write_column(sb, col, '|'); - chars_written++; - } else { - strbuf_write_column(sb, col, '|'); - chars_written++; - } - strbuf_addch(sb, ' '); - chars_written++; - } - - graph_pad_horizontally(graph, sb, chars_written); - - /* - * Update graph->state - */ - if (graph->num_parents > 1) - graph_update_state(graph, GRAPH_POST_MERGE); - else if (graph_is_mapping_correct(graph)) - graph_update_state(graph, GRAPH_PADDING); - else - graph_update_state(graph, GRAPH_COLLAPSING); -} - -static struct column *find_new_column_by_commit(struct git_graph *graph, - struct commit *commit) -{ - int i; - for (i = 0; i < graph->num_new_columns; i++) { - if (graph->new_columns[i].commit == commit) - return &graph->new_columns[i]; - } - return NULL; -} - -static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) -{ - int seen_this = 0; - int i, j, chars_written; - - /* - * Output the post-merge row - */ - chars_written = 0; - for (i = 0; i <= graph->num_columns; i++) { - struct column *col = &graph->columns[i]; - struct commit *col_commit; - if (i == graph->num_columns) { - if (seen_this) - break; - col_commit = graph->commit; - } else { - col_commit = col->commit; - } - - if (col_commit == graph->commit) { - /* - * Since the current commit is a merge find - * the columns for the parent commits in - * new_columns and use those to format the - * edges. - */ - struct commit_list *parents = NULL; - struct column *par_column; - seen_this = 1; - parents = first_interesting_parent(graph); - assert(parents); - par_column = find_new_column_by_commit(graph, parents->item); - assert(par_column); - - strbuf_write_column(sb, par_column, '|'); - chars_written++; - for (j = 0; j < graph->num_parents - 1; j++) { - parents = next_interesting_parent(graph, parents); - assert(parents); - par_column = find_new_column_by_commit(graph, parents->item); - assert(par_column); - strbuf_write_column(sb, par_column, '\\'); - strbuf_addch(sb, ' '); + draw_ch = '.'; } - chars_written += j * 2; - } else if (seen_this) { - strbuf_write_column(sb, col, '\\'); - strbuf_addch(sb, ' '); - chars_written += 2; + if (graph->num_parents > 1) { + j = i+1, k = i+2; + if (i > 0 && graph->mapping[j*2] == i-1) + col = &graph->new_columns[i-1]; + else + col = &graph->new_columns[i+graph->num_parents-1]; + /* + optimize collapse after merge case: + i j k + | * <hash> some commit + *. \ <hash> merge branch with another commit + | |/ <--- collapse line + | * <hash> another commit + + this happens if the mapping of the second next + column merges with the next column + remember this by marking seen_this = 2 + adjust mapping array because we fixed it here + + output: + | * <hash> some commit + *.| <hash> merge branch with another commit + | * <hash> another commit + + this saves a line, plus the merge commit is + not indented anymore relative to what's merged + */ + if (graph->mapping[j*2] == j && graph->mapping[k*2] == j) { + for (; k*2 < graph->mapping_size; k++) + graph->mapping[k*2] = k; + add_space = 0; + seen_this = 2; /* draw a '|' */ + } + } + } else if (seen_this == 1 && (graph->num_parents >= 2)) { + draw_ch = '\\'; } else { - strbuf_write_column(sb, col, '|'); - strbuf_addch(sb, ' '); - chars_written += 2; + draw_ch = '|'; } + if (draw_ch) + strbuf_write_column(sb, col, draw_ch); + if (add_space) + strbuf_addch(sb, ' '); + chars_written += 1 + add_space; } - graph_pad_horizontally(graph, sb, chars_written); - /* * Update graph->state */ - if (graph_is_mapping_correct(graph)) + graph->prev_merge_index = 0xffff; + if (graph_is_mapping_correct(graph)) { graph_update_state(graph, GRAPH_PADDING); - else + if (graph->num_parents > 1) + graph->prev_merge_index = graph->commit_index; + } else graph_update_state(graph, GRAPH_COLLAPSING); } static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb) { - int i; - short used_horizontal = 0; + int i, j; int horizontal_edge = -1; int horizontal_edge_target = -1; + char draw_ch; /* * Clear out the new_mapping array @@ -1097,16 +1003,15 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf * select this one. */ if (horizontal_edge == -1) { - int j; horizontal_edge = i; horizontal_edge_target = target; /* * The variable target is the index of the graph - * column, and therefore target*2+3 is the + * column, and therefore target*2+1 is the * actual screen column of the first horizontal * line. */ - for (j = (target * 2)+3; j < (i - 2); j += 2) + for (j = (target * 2)+1; j < i; j += 2) graph->new_mapping[j] = target; } } else if (graph->new_mapping[i - 1] == target) { @@ -1158,27 +1063,19 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf */ for (i = 0; i < graph->mapping_size; i++) { int target = graph->new_mapping[i]; - if (target < 0) + if (target < 0) { strbuf_addch(sb, ' '); - else if (target * 2 == i) - strbuf_write_column(sb, &graph->new_columns[target], '|'); - else if (target == horizontal_edge_target && - i != horizontal_edge - 1) { - /* - * Set the mappings for all but the - * first segment to -1 so that they - * won't continue into the next line. - */ - if (i != (target * 2)+3) - graph->new_mapping[i] = -1; - used_horizontal = 1; - strbuf_write_column(sb, &graph->new_columns[target], '_'); + continue; + } + if (target * 2 == i) { + draw_ch = '|'; } else { - if (used_horizontal && i < horizontal_edge) + if (i < horizontal_edge) graph->new_mapping[i] = -1; - strbuf_write_column(sb, &graph->new_columns[target], '/'); - + draw_ch = target == horizontal_edge_target && + i != horizontal_edge-1 ? '_' : '/'; } + strbuf_write_column(sb, &graph->new_columns[target], draw_ch); } graph_pad_horizontally(graph, sb, graph->mapping_size); @@ -1212,9 +1109,6 @@ int graph_next_line(struct git_graph *graph, struct strbuf *sb) case GRAPH_COMMIT: graph_output_commit_line(graph, sb); return 1; - case GRAPH_POST_MERGE: - graph_output_post_merge_line(graph, sb); - return 0; case GRAPH_COLLAPSING: graph_output_collapsing_line(graph, sb); return 0; @@ -1258,11 +1152,6 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) } graph_pad_horizontally(graph, sb, chars_written); - - /* - * Update graph->prev_state since we have output a padding line - */ - graph->prev_state = GRAPH_PADDING; } int graph_is_commit_finished(struct git_graph const *graph) -- 2.17.1