X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=graph.c;h=edfab2d5b461f5bfd39d32fa843ee59841b4ce0a;hb=8f1b0637c15bdd67170db423c86f06ee5db273b7;hp=9d6ed30b0bef22bd8b56da9a12688c760b661776;hpb=0724cb86c51a230090ef12d6bfe17a67351631a8;p=git.git diff --git a/graph.c b/graph.c index 9d6ed30b0..edfab2d5b 100644 --- a/graph.c +++ b/graph.c @@ -54,10 +54,14 @@ struct git_graph { * The commit currently being processed */ struct commit *commit; + /* The rev-info used for the current traversal */ + struct rev_info *revs; /* - * The number of parents this commit has. - * (Stored so we don't have to walk over them each time we need - * this number) + * The number of interesting parents that this commit has. + * + * Note that this is not the same as the actual number of parents. + * This count excludes parents that won't be printed in the graph + * output, as determined by graph_is_interesting(). */ int num_parents; /* @@ -76,6 +80,27 @@ 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. + * + * If none of the incoming columns refer to this commit, + * this will be equal to num_columns. + */ + 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. + */ + int prev_commit_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 @@ -125,13 +150,17 @@ struct git_graph { int *new_mapping; }; -struct git_graph *graph_init(void) +struct git_graph *graph_init(struct rev_info *opt) { struct git_graph *graph = xmalloc(sizeof(struct git_graph)); graph->commit = NULL; + graph->revs = 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; @@ -159,6 +188,12 @@ void graph_release(struct git_graph *graph) free(graph); } +static void graph_update_state(struct git_graph *graph, enum graph_state s) +{ + graph->prev_state = graph->state; + graph->state = s; +} + static void graph_ensure_capacity(struct git_graph *graph, int num_columns) { if (graph->column_capacity >= num_columns) @@ -180,6 +215,28 @@ static void graph_ensure_capacity(struct git_graph *graph, int num_columns) sizeof(int) * 2 * graph->column_capacity); } +/* + * Returns 1 if the commit will be printed in the graph output, + * and 0 otherwise. + */ +static int graph_is_interesting(struct git_graph *graph, struct commit *commit) +{ + /* + * If revs->boundary is set, commits whose children have + * been shown are always interesting, even if they have the + * UNINTERESTING or TREESAME flags set. + */ + if (graph->revs && graph->revs->boundary) { + if (commit->object.flags & CHILD_SHOWN) + return 1; + } + + /* + * Uninteresting and pruned commits won't be printed + */ + return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1; +} + static void graph_insert_into_new_columns(struct git_graph *graph, struct commit *commit, int *mapping_index) @@ -187,9 +244,9 @@ static void graph_insert_into_new_columns(struct git_graph *graph, int i; /* - * Ignore uinteresting and pruned commits + * Ignore uinteresting commits */ - if (commit->object.flags & (UNINTERESTING | TREESAME)) + if (!graph_is_interesting(graph, commit)) return; /* @@ -228,8 +285,8 @@ static void graph_update_width(struct git_graph *graph, int max_cols = graph->num_columns + graph->num_parents; /* - * Even if the current commit has no parents, it still takes up a - * column for itself. + * Even if the current commit has no parents to be printed, it + * still takes up a column for itself. */ if (graph->num_parents < 1) max_cols++; @@ -313,7 +370,9 @@ static void graph_update_columns(struct git_graph *graph) } if (col_commit == graph->commit) { + int old_mapping_idx = mapping_idx; seen_this = 1; + graph->commit_index = i; for (parent = graph->commit->parents; parent; parent = parent->next) { @@ -321,6 +380,14 @@ static void graph_update_columns(struct git_graph *graph) parent->item, &mapping_idx); } + /* + * We always need to increment mapping_idx by at + * least 2, even if it has no interesting parents. + * The current commit always takes up at least 2 + * spaces. + */ + if (mapping_idx == old_mapping_idx) + mapping_idx += 2; } else { graph_insert_into_new_columns(graph, col_commit, &mapping_idx); @@ -350,11 +417,20 @@ void graph_update(struct git_graph *graph, struct commit *commit) graph->commit = commit; /* - * Count how many parents this commit has + * Count how many interesting parents this commit has */ graph->num_parents = 0; - for (parent = commit->parents; parent; parent = parent->next) - graph->num_parents++; + for (parent = commit->parents; parent; parent = parent->next) { + if (graph_is_interesting(graph, parent->item)) + 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 @@ -366,21 +442,26 @@ 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 * a line to indicate that portion of the graph is missing. * - * Otherwise, if there are 3 or more parents, we need to print - * extra rows before the commit, to expand the branch lines around - * it and make room for it. + * If there are 3 or more parents, we may need to print extra rows + * before the commit, to expand the branch lines around it and make + * room for it. We need to do this only if there is a branch row + * (or more) to the right of this commit. * * If there are less than 3 parents, we can immediately print the * commit line. */ if (graph->state != GRAPH_PADDING) graph->state = GRAPH_SKIP; - else if (graph->num_parents >= 3) + else if (graph->num_parents >= 3 && + graph->commit_index < (graph->num_columns - 1)) graph->state = GRAPH_PRE_COMMIT; else graph->state = GRAPH_COMMIT; @@ -459,10 +540,11 @@ static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb) strbuf_addstr(sb, "..."); graph_pad_horizontally(graph, sb); - if (graph->num_parents >= 3) - graph->state = GRAPH_PRE_COMMIT; + if (graph->num_parents >= 3 && + graph->commit_index < (graph->num_columns - 1)) + graph_update_state(graph, GRAPH_PRE_COMMIT); else - graph->state = GRAPH_COMMIT; + graph_update_state(graph, GRAPH_COMMIT); } static void graph_output_pre_commit_line(struct git_graph *graph, @@ -497,7 +579,22 @@ static void graph_output_pre_commit_line(struct git_graph *graph, if (col->commit == graph->commit) { seen_this = 1; strbuf_addf(sb, "| %*s", graph->expansion_row, ""); - } else if (seen_this) { + } 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_addstr(sb, "\\ "); + else + strbuf_addstr(sb, "| "); + } else if (seen_this && (graph->expansion_row > 0)) { strbuf_addstr(sb, "\\ "); } else { strbuf_addstr(sb, "| "); @@ -512,7 +609,52 @@ static void graph_output_pre_commit_line(struct git_graph *graph, */ graph->expansion_row++; if (graph->expansion_row >= num_expansion_rows) - graph->state = GRAPH_COMMIT; + graph_update_state(graph, GRAPH_COMMIT); +} + +static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) +{ + /* + * For boundary commits, print 'o' + * (We should only see boundary commits when revs->boundary is set.) + */ + if (graph->commit->object.flags & BOUNDARY) { + assert(graph->revs->boundary); + strbuf_addch(sb, 'o'); + return; + } + + /* + * If revs->left_right is set, print '<' for commits that + * come from the left side, and '>' for commits from the right + * side. + */ + if (graph->revs && graph->revs->left_right) { + if (graph->commit->object.flags & SYMMETRIC_LEFT) + strbuf_addch(sb, '<'); + else + strbuf_addch(sb, '>'); + return; + } + + /* + * Print 'M' for merge commits + * + * Note that we don't check graph->num_parents to determine if the + * commit is a merge, since that only tracks the number of + * "interesting" parents. We want to print 'M' for merge commits + * even if they have less than 2 interesting parents. + */ + if (graph->commit->parents != NULL && + graph->commit->parents->next != NULL) { + strbuf_addch(sb, 'M'); + return; + } + + /* + * Print '*' in all other cases + */ + strbuf_addch(sb, '*'); } void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) @@ -540,15 +682,10 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) if (col_commit == graph->commit) { seen_this = 1; - if (graph->num_parents > 1) - strbuf_addch(sb, 'M'); - else - strbuf_addch(sb, '*'); + graph_output_commit_char(graph, sb); - if (graph->num_parents < 2) + if (graph->num_parents < 3) strbuf_addch(sb, ' '); - else if (graph->num_parents == 2) - strbuf_addstr(sb, " "); else { int num_dashes = ((graph->num_parents - 2) * 2) - 1; @@ -556,8 +693,27 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, '-'); strbuf_addstr(sb, ". "); } - } else if (seen_this && (graph->num_parents > 1)) { + } else if (seen_this && (graph->num_parents > 2)) { strbuf_addstr(sb, "\\ "); + } 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_addstr(sb, "\\ "); + else + strbuf_addstr(sb, "| "); } else { strbuf_addstr(sb, "| "); } @@ -569,11 +725,11 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) * Update graph->state */ if (graph->num_parents > 1) - graph->state = GRAPH_POST_MERGE; + graph_update_state(graph, GRAPH_POST_MERGE); else if (graph_is_mapping_correct(graph)) - graph->state = GRAPH_PADDING; + graph_update_state(graph, GRAPH_PADDING); else - graph->state = GRAPH_COLLAPSING; + graph_update_state(graph, GRAPH_COLLAPSING); } void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) @@ -599,9 +755,7 @@ void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, '|'); for (j = 0; j < graph->num_parents - 1; j++) strbuf_addstr(sb, "\\ "); - if (graph->num_parents == 2) - strbuf_addch(sb, ' '); - } else if (seen_this && (graph->num_parents > 2)) { + } else if (seen_this) { strbuf_addstr(sb, "\\ "); } else { strbuf_addstr(sb, "| "); @@ -614,9 +768,9 @@ void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) * Update graph->state */ if (graph_is_mapping_correct(graph)) - graph->state = GRAPH_PADDING; + graph_update_state(graph, GRAPH_PADDING); else - graph->state = GRAPH_COLLAPSING; + graph_update_state(graph, GRAPH_COLLAPSING); } void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb) @@ -721,7 +875,7 @@ void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb) * Otherwise, we need to collapse some branch lines together. */ if (graph_is_mapping_correct(graph)) - graph->state = GRAPH_PADDING; + graph_update_state(graph, GRAPH_PADDING); } int graph_next_line(struct git_graph *graph, struct strbuf *sb) @@ -785,6 +939,11 @@ void graph_padding_line(struct git_graph *graph, struct strbuf *sb) } graph_pad_horizontally(graph, sb); + + /* + * 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)