* [PATCH] graph API: Added logic for colored edges [not found] <20090331235922.GA7411@linux.vnet> @ 2009-04-07 18:57 ` Allan Caffee 2009-04-08 7:59 ` Junio C Hamano 2009-04-09 17:58 ` [PATCH] " Teemu Likonen 0 siblings, 2 replies; 13+ messages in thread From: Allan Caffee @ 2009-04-07 18:57 UTC (permalink / raw) To: git; +Cc: Johannes Schindelin, Jeff King, Nanako Shiraishi Modified the graph drawing logic to colorize edges based on parent-child relationships similiarly to gitk. Signed-off-by: Allan Caffee <allan.caffee@gmail.com> --- It seemed like it was time for another iteration so here it is. Changes this round include: (Only the first two were actually posted to the list as full patches.) * graph.c: avoid compile warnings (Thanks Johannes) * --graph: respect --no-color (Thanks Johannes) * Remove some outdated TODO comments * Avoid using forward declarations * Remove commented out debugging code * Avoid an unnecessary function call * Refactor/rename some local functions I replaced get_next_column_column color with graph_increment_column_color since the return value is never actually used. I also renamed get_current_column_color to graph_get_current_column_color. * Handle column colors as const pointers The color codes used should never be modified once we begin graphing, so columns should store/handle them as const char*. * graph API: Add handling for merges with 3+ parents I accidentally left this one out of the first round. ~Allan color.h | 1 + graph.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 145 insertions(+), 31 deletions(-) diff --git a/color.h b/color.h index 6846be1..18abeb7 100644 --- a/color.h +++ b/color.h @@ -11,6 +11,7 @@ #define GIT_COLOR_GREEN "\033[32m" #define GIT_COLOR_YELLOW "\033[33m" #define GIT_COLOR_BLUE "\033[34m" +#define GIT_COLOR_MAGENTA "\033[35m" #define GIT_COLOR_CYAN "\033[36m" #define GIT_COLOR_BG_RED "\033[41m" diff --git a/graph.c b/graph.c index 162a516..0e112d7 100644 --- a/graph.c +++ b/graph.c @@ -1,5 +1,6 @@ #include "cache.h" #include "commit.h" +#include "color.h" #include "graph.h" #include "diff.h" #include "revision.h" @@ -43,10 +44,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb); /* * TODO: - * - Add colors to the graph. - * Pick a color for each column, and print all characters - * in that column with the specified color. - * * - Limit the number of columns, similar to the way gitk does. * If we reach more than a specified number of columns, omit * sections of some columns. @@ -72,11 +69,21 @@ struct column { */ struct commit *commit; /* - * XXX: Once we add support for colors, struct column could also - * contain the color of its branch line. + * The color to (optionally) print this column in. */ + const char *color; }; +static void strbuf_write_column(struct strbuf *sb, const struct column *c, + const char *s) +{ + if (c->color) + strbuf_addstr(sb, c->color); + strbuf_addstr(sb, s); + if (c->color) + strbuf_addstr(sb, GIT_COLOR_RESET); +} + enum graph_state { GRAPH_PADDING, GRAPH_SKIP, @@ -86,6 +93,24 @@ enum graph_state { GRAPH_COLLAPSING }; +/* + * The list of available column colors. + */ +static char column_colors[][COLOR_MAXLEN] = { + GIT_COLOR_RED, + GIT_COLOR_GREEN, + GIT_COLOR_YELLOW, + GIT_COLOR_BLUE, + GIT_COLOR_MAGENTA, + GIT_COLOR_CYAN, + GIT_COLOR_BOLD GIT_COLOR_RED, + GIT_COLOR_BOLD GIT_COLOR_GREEN, + GIT_COLOR_BOLD GIT_COLOR_YELLOW, + GIT_COLOR_BOLD GIT_COLOR_BLUE, + GIT_COLOR_BOLD GIT_COLOR_MAGENTA, + GIT_COLOR_BOLD GIT_COLOR_CYAN, +}; + struct git_graph { /* * The commit currently being processed @@ -185,6 +210,11 @@ struct git_graph { * temporary array each time we have to output a collapsing line. */ int *new_mapping; + /* + * The current default column color being used. This is + * stored as an index into the array column_colors. + */ + short default_column_color; }; struct git_graph *graph_init(struct rev_info *opt) @@ -201,6 +231,7 @@ struct git_graph *graph_init(struct rev_info *opt) graph->num_columns = 0; graph->num_new_columns = 0; graph->mapping_size = 0; + graph->default_column_color = 0; /* * Allocate a reasonably large default number of columns @@ -312,6 +343,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph) return next_interesting_parent(graph, parents); } +static const char* graph_get_current_column_color(const struct git_graph* graph) +{ + if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF)) + return NULL; + return column_colors[graph->default_column_color]; +} + +/* + * Update the graph's default column color. + */ +static void graph_increment_column_color(struct git_graph* graph) +{ + graph->default_column_color = (graph->default_column_color + 1) % + ARRAY_SIZE(column_colors); +} + +static const char * graph_find_commit_color(const struct git_graph *graph, + const struct commit *commit) +{ + int i; + for (i = 0; i < graph->num_columns; i++) { + if (graph->columns[i].commit == commit) + return graph->columns[i].color; + } + return graph_get_current_column_color(graph); +} + static void graph_insert_into_new_columns(struct git_graph *graph, struct commit *commit, int *mapping_index) @@ -334,6 +392,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph, * This commit isn't already in new_columns. Add it. */ graph->new_columns[graph->num_new_columns].commit = commit; + graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit); graph->mapping[*mapping_index] = graph->num_new_columns; *mapping_index += 2; graph->num_new_columns++; @@ -445,6 +504,12 @@ static void graph_update_columns(struct git_graph *graph) for (parent = first_interesting_parent(graph); parent; parent = next_interesting_parent(graph, parent)) { + /* + * If this is a merge increment the current + * color. + */ + if (graph->num_parents > 1) + graph_increment_column_color(graph); graph_insert_into_new_columns(graph, parent->item, &mapping_idx); @@ -596,7 +661,7 @@ static void graph_output_padding_line(struct git_graph *graph, * Output a padding row, that leaves all branch lines unchanged */ for (i = 0; i < graph->num_new_columns; i++) { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, &graph->new_columns[i], "| "); } graph_pad_horizontally(graph, sb); @@ -648,8 +713,11 @@ static void graph_output_pre_commit_line(struct git_graph *graph, for (i = 0; i < graph->num_columns; i++) { struct column *col = &graph->columns[i]; if (col->commit == graph->commit) { + struct strbuf tmp = STRBUF_INIT; seen_this = 1; - strbuf_addf(sb, "| %*s", graph->expansion_row, ""); + strbuf_addf(&tmp, "| %*s", graph->expansion_row, ""); + strbuf_write_column(sb, col, tmp.buf); + strbuf_release(&tmp); } else if (seen_this && (graph->expansion_row == 0)) { /* * This is the first line of the pre-commit output. @@ -662,13 +730,13 @@ static void graph_output_pre_commit_line(struct git_graph *graph, */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, "\\ "); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, "| "); } else if (seen_this && (graph->expansion_row > 0)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, "\\ "); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, "| "); } } @@ -728,6 +796,7 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) */ seen_this = 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) @@ -744,14 +813,25 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) if (graph->num_parents < 3) strbuf_addch(sb, ' '); else { + /* + * Here dashless_commits represents the + * number of parents which don't need + * to have dashes (because their edges + * fit neatly under the commit). + */ + const int dashless_commits = 2; int num_dashes = - ((graph->num_parents - 2) * 2) - 1; + ((graph->num_parents - dashless_commits) * 2) - 1; for (j = 0; j < num_dashes; j++) - strbuf_addch(sb, '-'); - strbuf_addstr(sb, ". "); + strbuf_write_column(sb, + &graph->new_columns[(j / 2) + dashless_commits], + "-"); + strbuf_write_column(sb, + &graph->new_columns[(j / 2) + dashless_commits], + ". "); } } else if (seen_this && (graph->num_parents > 2)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, "\\ "); } else if (seen_this && (graph->num_parents == 2)) { /* * This is a 2-way merge commit. @@ -768,11 +848,11 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, "\\ "); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, "| "); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, "| "); } } @@ -789,6 +869,17 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) graph_update_state(graph, GRAPH_COLLAPSING); } +inline 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 0; +} + static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; @@ -798,24 +889,43 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf * Output the post-merge row */ 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 = graph->columns[i].commit; + 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; - strbuf_addch(sb, '|'); - for (j = 0; j < graph->num_parents - 1; j++) - strbuf_addstr(sb, "\\ "); + 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, "|"); + 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, "\\ "); + } } else if (seen_this) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, "\\ "); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, "| "); } } @@ -912,9 +1022,11 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf if (target < 0) strbuf_addch(sb, ' '); else if (target * 2 == i) - strbuf_addch(sb, '|'); - else - strbuf_addch(sb, '/'); + strbuf_write_column(sb, &graph->new_columns[target], "|"); + else { + strbuf_write_column(sb, &graph->new_columns[target], "/"); + + } } graph_pad_horizontally(graph, sb); @@ -979,9 +1091,10 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) * children that we have already processed.) */ for (i = 0; i < graph->num_columns; i++) { - struct commit *col_commit = graph->columns[i].commit; + struct column *col = &graph->columns[i]; + struct commit *col_commit = col->commit; if (col_commit == graph->commit) { - strbuf_addch(sb, '|'); + strbuf_write_column(sb, col, "|"); if (graph->num_parents < 3) strbuf_addch(sb, ' '); @@ -991,7 +1104,7 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, ' '); } } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, "| "); } } -- 1.5.6.3 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] graph API: Added logic for colored edges 2009-04-07 18:57 ` [PATCH] graph API: Added logic for colored edges Allan Caffee @ 2009-04-08 7:59 ` Junio C Hamano 2009-04-08 21:41 ` Allan Caffee 2009-04-09 17:58 ` [PATCH] " Teemu Likonen 1 sibling, 1 reply; 13+ messages in thread From: Junio C Hamano @ 2009-04-08 7:59 UTC (permalink / raw) To: git; +Cc: Johannes Schindelin, Jeff King, Nanako Shiraishi Allan Caffee <allan.caffee@gmail.com> writes: > @@ -72,11 +69,21 @@ struct column { > */ > struct commit *commit; > /* > - * XXX: Once we add support for colors, struct column could also > - * contain the color of its branch line. > + * The color to (optionally) print this column in. > */ > + const char *color; You already use short for git_graph.default_column_color and I suspect in the longer term you want to make this one an index into column_colors[] array the same way. We may someday decide to support non-ANSI color scheme using ncurses or something, and at that point the "hardware color" constants like GIT_COLOR_RED and friends may change from strings to small integers we use to call our (yet to be written) curses interface layer with. > @@ -312,6 +343,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph) > return next_interesting_parent(graph, parents); > } > > +static const char* graph_get_current_column_color(const struct git_graph* graph) Style. Asterisk comes next to identifiers, not types (the parameter to graph_increment_column_color has the same issue). static const char *graph_...color(const struct git_graph *graph) > @@ -596,7 +661,7 @@ static void graph_output_padding_line(struct git_graph *graph, > * Output a padding row, that leaves all branch lines unchanged > */ > for (i = 0; i < graph->num_new_columns; i++) { > - strbuf_addstr(sb, "| "); > + strbuf_write_column(sb, &graph->new_columns[i], "| "); Hmmm, this forbids us to use reverse color in the color palette because that would highlight the trailing whitespace. Is that something we care about, or a reversed "|", "/" and "\" are already too ugly that we won't want to support them? > @@ -648,8 +713,11 @@ static void graph_output_pre_commit_line(struct git_graph *graph, > for (i = 0; i < graph->num_columns; i++) { > struct column *col = &graph->columns[i]; > if (col->commit == graph->commit) { > + struct strbuf tmp = STRBUF_INIT; > seen_this = 1; > - strbuf_addf(sb, "| %*s", graph->expansion_row, ""); > + strbuf_addf(&tmp, "| %*s", graph->expansion_row, ""); > + strbuf_write_column(sb, col, tmp.buf); > + strbuf_release(&tmp); Same here. > @@ -662,13 +730,13 @@ static void graph_output_pre_commit_line(struct git_graph *graph, > */ > if (graph->prev_state == GRAPH_POST_MERGE && > graph->prev_commit_index < i) > - strbuf_addstr(sb, "\\ "); > + strbuf_write_column(sb, col, "\\ "); > else > - strbuf_addstr(sb, "| "); > + strbuf_write_column(sb, col, "| "); > } else if (seen_this && (graph->expansion_row > 0)) { > - strbuf_addstr(sb, "\\ "); > + strbuf_write_column(sb, col, "\\ "); > } else { > - strbuf_addstr(sb, "| "); > + strbuf_write_column(sb, col, "| "); > } > } Likewise. > @@ -744,14 +813,25 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) > if (graph->num_parents < 3) > strbuf_addch(sb, ' '); > else { > + /* > + * Here dashless_commits represents the > + * number of parents which don't need > + * to have dashes (because their edges > + * fit neatly under the commit). > + */ > + const int dashless_commits = 2; > int num_dashes = > - ((graph->num_parents - 2) * 2) - 1; > + ((graph->num_parents - dashless_commits) * 2) - 1; > for (j = 0; j < num_dashes; j++) > - strbuf_addch(sb, '-'); > - strbuf_addstr(sb, ". "); > + strbuf_write_column(sb, > + &graph->new_columns[(j / 2) + dashless_commits], > + "-"); > + strbuf_write_column(sb, > + &graph->new_columns[(j / 2) + dashless_commits], > + ". "); The nesting seems to be becoming too deep and the body of the for loop is getting too long. Time to make it a helper function that handles only one column, perhaps? ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] graph API: Added logic for colored edges 2009-04-08 7:59 ` Junio C Hamano @ 2009-04-08 21:41 ` Allan Caffee 2009-04-09 0:29 ` Junio C Hamano 0 siblings, 1 reply; 13+ messages in thread From: Allan Caffee @ 2009-04-08 21:41 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Johannes Schindelin, Jeff King, Nanako Shiraishi On Wed, 08 Apr 2009, Junio C Hamano wrote: > Allan Caffee <allan.caffee@gmail.com> writes: > > > @@ -72,11 +69,21 @@ struct column { > > */ > > struct commit *commit; > > /* > > - * XXX: Once we add support for colors, struct column could also > > - * contain the color of its branch line. > > + * The color to (optionally) print this column in. > > */ > > + const char *color; > > You already use short for git_graph.default_column_color and I suspect in > the longer term you want to make this one an index into column_colors[] > array the same way. We may someday decide to support non-ANSI color > scheme using ncurses or something, and at that point the "hardware color" > constants like GIT_COLOR_RED and friends may change from strings to small > integers we use to call our (yet to be written) curses interface layer > with. The problem with making it an index into the column_colors array is that we don't have a convenient place to test whether the user actually wants color. We can't do it in strbuf_write_column because AFAIK there's no way to get the rev-info to test the options. I suppose we could define GIT_NOT_A_COLOR to -1 and just set the color to that when we don't intend to use color. (Either way I should probably change that to an unsigned short.) What do you think? > > @@ -312,6 +343,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph) > > return next_interesting_parent(graph, parents); > > } > > > > +static const char* graph_get_current_column_color(const struct git_graph* graph) > > Style. Asterisk comes next to identifiers, not types (the parameter to > graph_increment_column_color has the same issue). Okay. > static const char *graph_...color(const struct git_graph *graph) > > > @@ -596,7 +661,7 @@ static void graph_output_padding_line(struct git_graph *graph, > > * Output a padding row, that leaves all branch lines unchanged > > */ > > for (i = 0; i < graph->num_new_columns; i++) { > > - strbuf_addstr(sb, "| "); > > + strbuf_write_column(sb, &graph->new_columns[i], "| "); > > Hmmm, this forbids us to use reverse color in the color palette because > that would highlight the trailing whitespace. Is that something we care > about, or a reversed "|", "/" and "\" are already too ugly that we won't > want to support them? Personally I don't think it's a good idea to limit the colors just because they would "look funny". It seems to me that if the user wanted the background colored they would expect to see just the line segment colored and not the whitespace. The simplest way to fix this AFAIKS is to change strbuf_write_column to take a single character and change the existing code to add spaces in seperately. /* For example */ strbuf_write_column(sb, &graph->new_columns[i], "| "); /* Becomes */ strbuf_write_column(sb, &graph->new_columns[i], '|'); strbuf_addch(sb, ' '); This would fix the problem at the minor expense of adding ~15 lines of code. > [...] > > > @@ -744,14 +813,25 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) > > if (graph->num_parents < 3) > > strbuf_addch(sb, ' '); > > else { > > + /* > > + * Here dashless_commits represents the > > + * number of parents which don't need > > + * to have dashes (because their edges > > + * fit neatly under the commit). > > + */ > > + const int dashless_commits = 2; > > int num_dashes = > > - ((graph->num_parents - 2) * 2) - 1; > > + ((graph->num_parents - dashless_commits) * 2) - 1; > > for (j = 0; j < num_dashes; j++) > > - strbuf_addch(sb, '-'); > > - strbuf_addstr(sb, ". "); > > + strbuf_write_column(sb, > > + &graph->new_columns[(j / 2) + dashless_commits], > > + "-"); > > + strbuf_write_column(sb, > > + &graph->new_columns[(j / 2) + dashless_commits], > > + ". "); > > The nesting seems to be becoming too deep and the body of the for loop is > getting too long. Time to make it a helper function that handles only one > column, perhaps? If I understand this correctly in order to write a per column function would be like: /* * Draw one peice of the commit line and return the new value of * seen_this. */ static int graph_commit_line_draw_column(struct git_graph *graph, struct strbuf *sb, int i, int seen_this) Which moves the entire body of the for loop into a function and only reduces the indentation by one level. Am I missing something? ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] graph API: Added logic for colored edges 2009-04-08 21:41 ` Allan Caffee @ 2009-04-09 0:29 ` Junio C Hamano 2009-04-09 22:22 ` [PATCH v3] " Allan Caffee 0 siblings, 1 reply; 13+ messages in thread From: Junio C Hamano @ 2009-04-09 0:29 UTC (permalink / raw) To: git; +Cc: Johannes Schindelin, Jeff King, Nanako Shiraishi Allan Caffee <allan.caffee@gmail.com> writes: > The problem with making it an index into the column_colors array is that > we don't have a convenient place to test whether the user actually wants > color. We can't do it in strbuf_write_column because AFAIK there's no > way to get the rev-info to test the options. I suppose we could define > GIT_NOT_A_COLOR to -1 and just set the color to that when we don't > intend to use color. (Either way I should probably change that to an > unsigned short.) What do you think? Sounds sensible. ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3] graph API: Added logic for colored edges 2009-04-09 0:29 ` Junio C Hamano @ 2009-04-09 22:22 ` Allan Caffee 2009-04-12 8:44 ` Junio C Hamano 0 siblings, 1 reply; 13+ messages in thread From: Allan Caffee @ 2009-04-09 22:22 UTC (permalink / raw) To: git Cc: Johannes Schindelin, Jeff King, Nanako Shiraishi, Junio C Hamano, Teemu Likonen Modified the graph drawing logic to colorize edges based on parent-child relationships similiarly to gitk. Signed-off-by: Allan Caffee <allan.caffee@gmail.com> --- Here's a list of the changes this round (I can send individual patches if it's easier to review): * graph API: Fix graph_pad_horizontal to work properly with colors The horizontal padding was counting the number of characters in the strbuf in order to decide how much whitespace to pad the row with. The ANSI escape sequences for colors and attributes are not printable characters but do contribute to the length of the strbuf. This fix teaches strbuf's to count printing characters (i.e. characters that consume space). * graph.c: Add graph_draw_octopus_merge() to simplify graph_output_commit_line() * --graph: Fix whitespace discoloration Background colors now work as expected (but boy are they ugly ;). * graph.c: Store colors as unsigned short * graph: Fixed up some stylistic issues with pointers ~Allan color.h | 1 + graph.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++------------ strbuf.c | 39 +++++++++++ strbuf.h | 1 + 4 files changed, 211 insertions(+), 42 deletions(-) diff --git a/color.h b/color.h index 6846be1..18abeb7 100644 --- a/color.h +++ b/color.h @@ -11,6 +11,7 @@ #define GIT_COLOR_GREEN "\033[32m" #define GIT_COLOR_YELLOW "\033[33m" #define GIT_COLOR_BLUE "\033[34m" +#define GIT_COLOR_MAGENTA "\033[35m" #define GIT_COLOR_CYAN "\033[36m" #define GIT_COLOR_BG_RED "\033[41m" diff --git a/graph.c b/graph.c index 162a516..beb622a 100644 --- a/graph.c +++ b/graph.c @@ -1,5 +1,6 @@ #include "cache.h" #include "commit.h" +#include "color.h" #include "graph.h" #include "diff.h" #include "revision.h" @@ -43,10 +44,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb); /* * TODO: - * - Add colors to the graph. - * Pick a color for each column, and print all characters - * in that column with the specified color. - * * - Limit the number of columns, similar to the way gitk does. * If we reach more than a specified number of columns, omit * sections of some columns. @@ -72,11 +69,14 @@ struct column { */ struct commit *commit; /* - * XXX: Once we add support for colors, struct column could also - * contain the color of its branch line. + * The color to (optionally) print this column in. This is an + * index into column_colors. */ + unsigned short color; }; +const unsigned short GIT_NOT_A_COLOR = -1; + enum graph_state { GRAPH_PADDING, GRAPH_SKIP, @@ -86,6 +86,39 @@ enum graph_state { GRAPH_COLLAPSING }; +/* + * The list of available column colors. + */ +static char column_colors[][COLOR_MAXLEN] = { + GIT_COLOR_RED, + GIT_COLOR_GREEN, + GIT_COLOR_YELLOW, + GIT_COLOR_BLUE, + GIT_COLOR_MAGENTA, + GIT_COLOR_CYAN, + GIT_COLOR_BOLD GIT_COLOR_RED, + GIT_COLOR_BOLD GIT_COLOR_GREEN, + GIT_COLOR_BOLD GIT_COLOR_YELLOW, + GIT_COLOR_BOLD GIT_COLOR_BLUE, + GIT_COLOR_BOLD GIT_COLOR_MAGENTA, + GIT_COLOR_BOLD GIT_COLOR_CYAN, +}; + +static const char *column_get_color_code(const struct column *c) +{ + return column_colors[c->color]; +} + +static void strbuf_write_column(struct strbuf *sb, const struct column *c, + char col_char) +{ + if (c->color != GIT_NOT_A_COLOR) + strbuf_addstr(sb, column_get_color_code(c)); + strbuf_addch(sb, col_char); + if (c->color != GIT_NOT_A_COLOR) + strbuf_addstr(sb, GIT_COLOR_RESET); +} + struct git_graph { /* * The commit currently being processed @@ -185,6 +218,11 @@ struct git_graph { * temporary array each time we have to output a collapsing line. */ int *new_mapping; + /* + * The current default column color being used. This is + * stored as an index into the array column_colors. + */ + unsigned short default_column_color; }; struct git_graph *graph_init(struct rev_info *opt) @@ -201,6 +239,7 @@ struct git_graph *graph_init(struct rev_info *opt) graph->num_columns = 0; graph->num_new_columns = 0; graph->mapping_size = 0; + graph->default_column_color = 0; /* * Allocate a reasonably large default number of columns @@ -312,6 +351,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph) return next_interesting_parent(graph, parents); } +inline unsigned short graph_get_current_column_color(const struct git_graph *graph) +{ + if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF)) + return GIT_NOT_A_COLOR; + return graph->default_column_color; +} + +/* + * Update the graph's default column color. + */ +static void graph_increment_column_color(struct git_graph *graph) +{ + graph->default_column_color = (graph->default_column_color + 1) % + ARRAY_SIZE(column_colors); +} + +inline unsigned short graph_find_commit_color(const struct git_graph *graph, + const struct commit *commit) +{ + int i; + for (i = 0; i < graph->num_columns; i++) { + if (graph->columns[i].commit == commit) + return graph->columns[i].color; + } + return graph_get_current_column_color(graph); +} + static void graph_insert_into_new_columns(struct git_graph *graph, struct commit *commit, int *mapping_index) @@ -334,6 +400,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph, * This commit isn't already in new_columns. Add it. */ graph->new_columns[graph->num_new_columns].commit = commit; + graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit); graph->mapping[*mapping_index] = graph->num_new_columns; *mapping_index += 2; graph->num_new_columns++; @@ -445,6 +512,12 @@ static void graph_update_columns(struct git_graph *graph) for (parent = first_interesting_parent(graph); parent; parent = next_interesting_parent(graph, parent)) { + /* + * If this is a merge increment the current + * color. + */ + if (graph->num_parents > 1) + graph_increment_column_color(graph); graph_insert_into_new_columns(graph, parent->item, &mapping_idx); @@ -569,11 +642,11 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb) * This way, fields printed to the right of the graph will remain * aligned for the entire commit. */ - int extra; - if (sb->len >= graph->width) + int extra, printing; + printing = strbuf_count_printing_chars(sb); + if (printing >= graph->width) return; - - extra = graph->width - sb->len; + extra = graph->width - printing; strbuf_addf(sb, "%*s", (int) extra, ""); } @@ -596,7 +669,8 @@ static void graph_output_padding_line(struct git_graph *graph, * Output a padding row, that leaves all branch lines unchanged */ for (i = 0; i < graph->num_new_columns; i++) { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, &graph->new_columns[i], '|'); + strbuf_addch(sb, ' '); } graph_pad_horizontally(graph, sb); @@ -649,7 +723,8 @@ static void graph_output_pre_commit_line(struct git_graph *graph, struct column *col = &graph->columns[i]; if (col->commit == graph->commit) { seen_this = 1; - strbuf_addf(sb, "| %*s", graph->expansion_row, ""); + strbuf_write_column(sb, col, '|'); + strbuf_addf(sb, " %*s", graph->expansion_row, ""); } else if (seen_this && (graph->expansion_row == 0)) { /* * This is the first line of the pre-commit output. @@ -662,14 +737,15 @@ static void graph_output_pre_commit_line(struct git_graph *graph, */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } else if (seen_this && (graph->expansion_row > 0)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } + strbuf_addch(sb, ' '); } graph_pad_horizontally(graph, sb); @@ -714,10 +790,30 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, '*'); } +inline void graph_draw_octopus_merge(const struct git_graph *graph, + struct strbuf *sb) +{ + /* + * Here dashless_commits represents the number of parents + * which don't need to have dashes (because their edges fit + * neatly under the commit). + */ + const int dashless_commits = 2; + int col_num, i; + int num_dashes = + ((graph->num_parents - dashless_commits) * 2) - 1; + for (i = 0; i < num_dashes; i++) { + col_num = (i / 2) + dashless_commits; + strbuf_write_column(sb, &graph->new_columns[col_num], '-'); + } + col_num = (i / 2) + dashless_commits; + strbuf_write_column(sb, &graph->new_columns[col_num], '.'); +} + static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; - int i, j; + int i; /* * Output the row containing this commit @@ -728,6 +824,7 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) */ seen_this = 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) @@ -741,17 +838,10 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) seen_this = 1; graph_output_commit_char(graph, sb); - if (graph->num_parents < 3) - strbuf_addch(sb, ' '); - else { - int num_dashes = - ((graph->num_parents - 2) * 2) - 1; - for (j = 0; j < num_dashes; j++) - strbuf_addch(sb, '-'); - strbuf_addstr(sb, ". "); - } + if (graph->num_parents > 3) + graph_draw_octopus_merge(graph, sb); } else if (seen_this && (graph->num_parents > 2)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); } else if (seen_this && (graph->num_parents == 2)) { /* * This is a 2-way merge commit. @@ -768,12 +858,13 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } + strbuf_addch(sb, ' '); } graph_pad_horizontally(graph, sb); @@ -789,6 +880,17 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) graph_update_state(graph, GRAPH_COLLAPSING); } +inline 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 0; +} + static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; @@ -798,24 +900,46 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf * Output the post-merge row */ 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 = graph->columns[i].commit; + 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; - strbuf_addch(sb, '|'); - for (j = 0; j < graph->num_parents - 1; j++) - strbuf_addstr(sb, "\\ "); + 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, '|'); + 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, ' '); + } } else if (seen_this) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); + strbuf_addch(sb, ' '); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + strbuf_addch(sb, ' '); } } @@ -912,9 +1036,11 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf if (target < 0) strbuf_addch(sb, ' '); else if (target * 2 == i) - strbuf_addch(sb, '|'); - else - strbuf_addch(sb, '/'); + strbuf_write_column(sb, &graph->new_columns[target], '|'); + else { + strbuf_write_column(sb, &graph->new_columns[target], '/'); + + } } graph_pad_horizontally(graph, sb); @@ -979,9 +1105,10 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) * children that we have already processed.) */ for (i = 0; i < graph->num_columns; i++) { - struct commit *col_commit = graph->columns[i].commit; + struct column *col = &graph->columns[i]; + struct commit *col_commit = col->commit; if (col_commit == graph->commit) { - strbuf_addch(sb, '|'); + strbuf_write_column(sb, col, '|'); if (graph->num_parents < 3) strbuf_addch(sb, ' '); @@ -991,7 +1118,8 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, ' '); } } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + strbuf_addch(sb, ' '); } } diff --git a/strbuf.c b/strbuf.c index a884960..f525d51 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1,3 +1,4 @@ +#include <ctype.h> #include "cache.h" #include "refs.h" @@ -138,6 +139,44 @@ void strbuf_list_free(struct strbuf **sbs) free(sbs); } +/* + * Return the length of the escape sequence in a string buffer + * starting at index i. If there is no escape sequence starting at + * return 0. + */ +inline size_t strbuf_esc_sequence_length(const struct strbuf *sb, size_t i) +{ + size_t start = i; + if (sb->buf[i] != '\033') + return 0; + ++i; + + if (i >= sb->len || sb->buf[i] != '[') + return 0; + ++i; + while (i < sb->len && isdigit(sb->buf[i])) + ++i; + + if (i >= sb->len || sb->buf[i] != 'm') + return 0; + return i - start; +} + +size_t strbuf_count_printing_chars(const struct strbuf *sb) +{ + int i; + size_t n = 0; + size_t esc_len; + for (i = 0; i < sb->len; i++) { + esc_len = strbuf_esc_sequence_length(sb, i); + if (esc_len) + i += esc_len; + else if (isgraph(sb->buf[i]) || sb->buf[i] == ' ') + ++n; + } + return n; +} + int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) { int len = a->len < b->len ? a->len: b->len; diff --git a/strbuf.h b/strbuf.h index 9ee908a..3851ddf 100644 --- a/strbuf.h +++ b/strbuf.h @@ -86,6 +86,7 @@ extern void strbuf_tolower(struct strbuf *); extern struct strbuf **strbuf_split(const struct strbuf *, int delim); extern void strbuf_list_free(struct strbuf **); +extern size_t strbuf_count_printing_chars(const struct strbuf *sb); /*----- add data in your buffer -----*/ static inline void strbuf_addch(struct strbuf *sb, int c) { strbuf_grow(sb, 1); -- 1.5.6.3 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v3] graph API: Added logic for colored edges 2009-04-09 22:22 ` [PATCH v3] " Allan Caffee @ 2009-04-12 8:44 ` Junio C Hamano 2009-04-12 17:43 ` Allan Caffee 0 siblings, 1 reply; 13+ messages in thread From: Junio C Hamano @ 2009-04-12 8:44 UTC (permalink / raw) To: Allan Caffee Cc: git, Johannes Schindelin, Jeff King, Nanako Shiraishi, Teemu Likonen Allan Caffee <allan.caffee@gmail.com> writes: > diff --git a/graph.c b/graph.c > index 162a516..beb622a 100644 > --- a/graph.c > +++ b/graph.c > @@ -1,5 +1,6 @@ > #include "cache.h" > #include "commit.h" > +#include "color.h" > #include "graph.h" > #include "diff.h" > #include "revision.h" > @@ -72,11 +69,14 @@ struct column { > */ > struct commit *commit; > /* > - * XXX: Once we add support for colors, struct column could also > - * contain the color of its branch line. > + * The color to (optionally) print this column in. This is an > + * index into column_colors. > */ > + unsigned short color; > }; > > +const unsigned short GIT_NOT_A_COLOR = -1; That (-1) is an unusual value for an *unsigned* short variable. > @@ -714,10 +790,30 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) > strbuf_addch(sb, '*'); > } > > +inline void graph_draw_octopus_merge(const struct git_graph *graph, > + struct strbuf *sb) > +{ > + /* > + * Here dashless_commits represents the number of parents > + * which don't need to have dashes (because their edges fit > + * neatly under the commit). > + */ > + const int dashless_commits = 2; > + int col_num, i; > + int num_dashes = > + ((graph->num_parents - dashless_commits) * 2) - 1; > + for (i = 0; i < num_dashes; i++) { > + col_num = (i / 2) + dashless_commits; > + strbuf_write_column(sb, &graph->new_columns[col_num], '-'); graph.c: In function 'graph_draw_octopus_merge': graph.c:807: error: 'strbuf_write_column' is static but used in inline function 'graph_draw_octopus_merge' which is not static graph.c:810: error: 'strbuf_write_column' is static but used in inline function 'graph_draw_octopus_merge' which is not static make: *** [graph.o] Error 1 In general, I'd prefer people not to say "inline" unless (1) they know what they are doing, and (2) the code is really performance critical. At least I do not think the colored commit graph is performance critical, especially a function that only deals with octopus merges. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v3] graph API: Added logic for colored edges 2009-04-12 8:44 ` Junio C Hamano @ 2009-04-12 17:43 ` Allan Caffee 2009-04-12 18:45 ` Junio C Hamano 0 siblings, 1 reply; 13+ messages in thread From: Allan Caffee @ 2009-04-12 17:43 UTC (permalink / raw) To: Junio C Hamano Cc: git, Johannes Schindelin, Jeff King, Nanako Shiraishi, Teemu Likonen On Sun, 12 Apr 2009, Junio C Hamano wrote: > Allan Caffee <allan.caffee@gmail.com> writes: > > > diff --git a/graph.c b/graph.c > > index 162a516..beb622a 100644 > > --- a/graph.c > > +++ b/graph.c > > @@ -1,5 +1,6 @@ > > #include "cache.h" > > #include "commit.h" > > +#include "color.h" > > #include "graph.h" > > #include "diff.h" > > #include "revision.h" > > @@ -72,11 +69,14 @@ struct column { > > */ > > struct commit *commit; > > /* > > - * XXX: Once we add support for colors, struct column could also > > - * contain the color of its branch line. > > + * The color to (optionally) print this column in. This is an > > + * index into column_colors. > > */ > > + unsigned short color; > > }; > > > > +const unsigned short GIT_NOT_A_COLOR = -1; > > That (-1) is an unusual value for an *unsigned* short variable. Perhaps you would prefer USHRT_MAX? I noticed that none of the existing code #includes limits.h. Is it safe to assume this header is present? > > @@ -714,10 +790,30 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) > > strbuf_addch(sb, '*'); > > } > > > > +inline void graph_draw_octopus_merge(const struct git_graph *graph, > > + struct strbuf *sb) > > +{ > > + /* > > + * Here dashless_commits represents the number of parents > > + * which don't need to have dashes (because their edges fit > > + * neatly under the commit). > > + */ > > + const int dashless_commits = 2; > > + int col_num, i; > > + int num_dashes = > > + ((graph->num_parents - dashless_commits) * 2) - 1; > > + for (i = 0; i < num_dashes; i++) { > > + col_num = (i / 2) + dashless_commits; > > + strbuf_write_column(sb, &graph->new_columns[col_num], '-'); > > graph.c: In function 'graph_draw_octopus_merge': > graph.c:807: error: 'strbuf_write_column' is static but used in inline function 'graph_draw_octopus_merge' which is not static > graph.c:810: error: 'strbuf_write_column' is static but used in inline function 'graph_draw_octopus_merge' which is not static > make: *** [graph.o] Error 1 > > In general, I'd prefer people not to say "inline" unless (1) they know > what they are doing, and (2) the code is really performance critical. > > At least I do not think the colored commit graph is performance critical, > especially a function that only deals with octopus merges. Okay. I'll remove the inline specifier from all the functions I added. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v3] graph API: Added logic for colored edges 2009-04-12 17:43 ` Allan Caffee @ 2009-04-12 18:45 ` Junio C Hamano 2009-04-12 20:27 ` [PATCH v4] " Allan Caffee 0 siblings, 1 reply; 13+ messages in thread From: Junio C Hamano @ 2009-04-12 18:45 UTC (permalink / raw) To: Allan Caffee Cc: git, Johannes Schindelin, Jeff King, Nanako Shiraishi, Teemu Likonen Allan Caffee <allan.caffee@gmail.com> writes: >> > +const unsigned short GIT_NOT_A_COLOR = -1; >> >> That (-1) is an unusual value for an *unsigned* short variable. > > Perhaps you would prefer USHRT_MAX? I noticed that none of the existing > code #includes limits.h. Is it safe to assume this header is present? I expected to see something like #define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors)+1) write_with_color(...) { if (c->color < COLUMN_COLORS_MAX) add color prefix; add string if (c->color < COLUMN_COLORS_MAX) add color suffix; } instead, actually, and was a bit surprised with (-1). ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v4] graph API: Added logic for colored edges 2009-04-12 18:45 ` Junio C Hamano @ 2009-04-12 20:27 ` Allan Caffee 2009-04-12 21:59 ` Junio C Hamano 0 siblings, 1 reply; 13+ messages in thread From: Allan Caffee @ 2009-04-12 20:27 UTC (permalink / raw) To: Junio C Hamano Cc: git, Johannes Schindelin, Jeff King, Nanako Shiraishi, Teemu Likonen Modified the graph drawing logic to colorize edges based on parent-child relationships similiarly to gitk. Signed-off-by: Allan Caffee <allan.caffee@gmail.com> --- On Sun, 12 Apr 2009, Junio C Hamano wrote: > Allan Caffee <allan.caffee@gmail.com> writes: > > >> > +const unsigned short GIT_NOT_A_COLOR = -1; > >> > >> That (-1) is an unusual value for an *unsigned* short variable. > > > > Perhaps you would prefer USHRT_MAX? I noticed that none of the > > existing > > code #includes limits.h. Is it safe to assume this header is > > present? > > I expected to see something like > > #define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors)+1) > > write_with_color(...) { > if (c->color < COLUMN_COLORS_MAX) > add color prefix; > add string > if (c->color < COLUMN_COLORS_MAX) > add color suffix; > } > > instead, actually, and was a bit surprised with (-1). Junio, I assumed that the +1 in your example was a typo since AFAIKS ARRAY_SIZE should give us one past the last index. If that's not correct let me know and I'll fix it. Also if git is to be expanded allow the use of non-ANSI color codes (or already does so) the strbuf_escape_sequence_length needs to be updated to accept the relevant escape codes. Thanks for all the help, Allan color.h | 1 + graph.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++------------ strbuf.c | 39 +++++++++++ strbuf.h | 1 + 4 files changed, 211 insertions(+), 42 deletions(-) diff --git a/color.h b/color.h index 6846be1..18abeb7 100644 --- a/color.h +++ b/color.h @@ -11,6 +11,7 @@ #define GIT_COLOR_GREEN "\033[32m" #define GIT_COLOR_YELLOW "\033[33m" #define GIT_COLOR_BLUE "\033[34m" +#define GIT_COLOR_MAGENTA "\033[35m" #define GIT_COLOR_CYAN "\033[36m" #define GIT_COLOR_BG_RED "\033[41m" diff --git a/graph.c b/graph.c index 162a516..1ae7a9c 100644 --- a/graph.c +++ b/graph.c @@ -1,5 +1,6 @@ #include "cache.h" #include "commit.h" +#include "color.h" #include "graph.h" #include "diff.h" #include "revision.h" @@ -43,10 +44,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb); /* * TODO: - * - Add colors to the graph. - * Pick a color for each column, and print all characters - * in that column with the specified color. - * * - Limit the number of columns, similar to the way gitk does. * If we reach more than a specified number of columns, omit * sections of some columns. @@ -72,11 +69,14 @@ struct column { */ struct commit *commit; /* - * XXX: Once we add support for colors, struct column could also - * contain the color of its branch line. + * The color to (optionally) print this column in. This is an + * index into column_colors. */ + unsigned short color; }; +#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors)) + enum graph_state { GRAPH_PADDING, GRAPH_SKIP, @@ -86,6 +86,39 @@ enum graph_state { GRAPH_COLLAPSING }; +/* + * The list of available column colors. + */ +static char column_colors[][COLOR_MAXLEN] = { + GIT_COLOR_RED, + GIT_COLOR_GREEN, + GIT_COLOR_YELLOW, + GIT_COLOR_BLUE, + GIT_COLOR_MAGENTA, + GIT_COLOR_CYAN, + GIT_COLOR_BOLD GIT_COLOR_RED, + GIT_COLOR_BOLD GIT_COLOR_GREEN, + GIT_COLOR_BOLD GIT_COLOR_YELLOW, + GIT_COLOR_BOLD GIT_COLOR_BLUE, + GIT_COLOR_BOLD GIT_COLOR_MAGENTA, + GIT_COLOR_BOLD GIT_COLOR_CYAN, +}; + +static const char *column_get_color_code(const struct column *c) +{ + return column_colors[c->color]; +} + +static void strbuf_write_column(struct strbuf *sb, const struct column *c, + char col_char) +{ + if (c->color < COLUMN_COLORS_MAX) + strbuf_addstr(sb, column_get_color_code(c)); + strbuf_addch(sb, col_char); + if (c->color < COLUMN_COLORS_MAX) + strbuf_addstr(sb, GIT_COLOR_RESET); +} + struct git_graph { /* * The commit currently being processed @@ -185,6 +218,11 @@ struct git_graph { * temporary array each time we have to output a collapsing line. */ int *new_mapping; + /* + * The current default column color being used. This is + * stored as an index into the array column_colors. + */ + unsigned short default_column_color; }; struct git_graph *graph_init(struct rev_info *opt) @@ -201,6 +239,7 @@ struct git_graph *graph_init(struct rev_info *opt) graph->num_columns = 0; graph->num_new_columns = 0; graph->mapping_size = 0; + graph->default_column_color = 0; /* * Allocate a reasonably large default number of columns @@ -312,6 +351,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph) return next_interesting_parent(graph, parents); } +unsigned short graph_get_current_column_color(const struct git_graph *graph) +{ + if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF)) + return COLUMN_COLORS_MAX; + return graph->default_column_color; +} + +/* + * Update the graph's default column color. + */ +static void graph_increment_column_color(struct git_graph *graph) +{ + graph->default_column_color = (graph->default_column_color + 1) % + ARRAY_SIZE(column_colors); +} + +unsigned short graph_find_commit_color(const struct git_graph *graph, + const struct commit *commit) +{ + int i; + for (i = 0; i < graph->num_columns; i++) { + if (graph->columns[i].commit == commit) + return graph->columns[i].color; + } + return graph_get_current_column_color(graph); +} + static void graph_insert_into_new_columns(struct git_graph *graph, struct commit *commit, int *mapping_index) @@ -334,6 +400,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph, * This commit isn't already in new_columns. Add it. */ graph->new_columns[graph->num_new_columns].commit = commit; + graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit); graph->mapping[*mapping_index] = graph->num_new_columns; *mapping_index += 2; graph->num_new_columns++; @@ -445,6 +512,12 @@ static void graph_update_columns(struct git_graph *graph) for (parent = first_interesting_parent(graph); parent; parent = next_interesting_parent(graph, parent)) { + /* + * If this is a merge increment the current + * color. + */ + if (graph->num_parents > 1) + graph_increment_column_color(graph); graph_insert_into_new_columns(graph, parent->item, &mapping_idx); @@ -569,11 +642,11 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb) * This way, fields printed to the right of the graph will remain * aligned for the entire commit. */ - int extra; - if (sb->len >= graph->width) + int extra, printing; + printing = strbuf_count_printing_chars(sb); + if (printing >= graph->width) return; - - extra = graph->width - sb->len; + extra = graph->width - printing; strbuf_addf(sb, "%*s", (int) extra, ""); } @@ -596,7 +669,8 @@ static void graph_output_padding_line(struct git_graph *graph, * Output a padding row, that leaves all branch lines unchanged */ for (i = 0; i < graph->num_new_columns; i++) { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, &graph->new_columns[i], '|'); + strbuf_addch(sb, ' '); } graph_pad_horizontally(graph, sb); @@ -649,7 +723,8 @@ static void graph_output_pre_commit_line(struct git_graph *graph, struct column *col = &graph->columns[i]; if (col->commit == graph->commit) { seen_this = 1; - strbuf_addf(sb, "| %*s", graph->expansion_row, ""); + strbuf_write_column(sb, col, '|'); + strbuf_addf(sb, " %*s", graph->expansion_row, ""); } else if (seen_this && (graph->expansion_row == 0)) { /* * This is the first line of the pre-commit output. @@ -662,14 +737,15 @@ static void graph_output_pre_commit_line(struct git_graph *graph, */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } else if (seen_this && (graph->expansion_row > 0)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } + strbuf_addch(sb, ' '); } graph_pad_horizontally(graph, sb); @@ -714,10 +790,30 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, '*'); } +void graph_draw_octopus_merge(const struct git_graph *graph, + struct strbuf *sb) +{ + /* + * Here dashless_commits represents the number of parents + * which don't need to have dashes (because their edges fit + * neatly under the commit). + */ + const int dashless_commits = 2; + int col_num, i; + int num_dashes = + ((graph->num_parents - dashless_commits) * 2) - 1; + for (i = 0; i < num_dashes; i++) { + col_num = (i / 2) + dashless_commits; + strbuf_write_column(sb, &graph->new_columns[col_num], '-'); + } + col_num = (i / 2) + dashless_commits; + strbuf_write_column(sb, &graph->new_columns[col_num], '.'); +} + static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; - int i, j; + int i; /* * Output the row containing this commit @@ -728,6 +824,7 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) */ seen_this = 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) @@ -741,17 +838,10 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) seen_this = 1; graph_output_commit_char(graph, sb); - if (graph->num_parents < 3) - strbuf_addch(sb, ' '); - else { - int num_dashes = - ((graph->num_parents - 2) * 2) - 1; - for (j = 0; j < num_dashes; j++) - strbuf_addch(sb, '-'); - strbuf_addstr(sb, ". "); - } + if (graph->num_parents > 3) + graph_draw_octopus_merge(graph, sb); } else if (seen_this && (graph->num_parents > 2)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); } else if (seen_this && (graph->num_parents == 2)) { /* * This is a 2-way merge commit. @@ -768,12 +858,13 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); } + strbuf_addch(sb, ' '); } graph_pad_horizontally(graph, sb); @@ -789,6 +880,17 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) graph_update_state(graph, GRAPH_COLLAPSING); } +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 0; +} + static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; @@ -798,24 +900,46 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf * Output the post-merge row */ 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 = graph->columns[i].commit; + 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; - strbuf_addch(sb, '|'); - for (j = 0; j < graph->num_parents - 1; j++) - strbuf_addstr(sb, "\\ "); + 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, '|'); + 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, ' '); + } } else if (seen_this) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); + strbuf_addch(sb, ' '); } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + strbuf_addch(sb, ' '); } } @@ -912,9 +1036,11 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf if (target < 0) strbuf_addch(sb, ' '); else if (target * 2 == i) - strbuf_addch(sb, '|'); - else - strbuf_addch(sb, '/'); + strbuf_write_column(sb, &graph->new_columns[target], '|'); + else { + strbuf_write_column(sb, &graph->new_columns[target], '/'); + + } } graph_pad_horizontally(graph, sb); @@ -979,9 +1105,10 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) * children that we have already processed.) */ for (i = 0; i < graph->num_columns; i++) { - struct commit *col_commit = graph->columns[i].commit; + struct column *col = &graph->columns[i]; + struct commit *col_commit = col->commit; if (col_commit == graph->commit) { - strbuf_addch(sb, '|'); + strbuf_write_column(sb, col, '|'); if (graph->num_parents < 3) strbuf_addch(sb, ' '); @@ -991,7 +1118,8 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, ' '); } } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + strbuf_addch(sb, ' '); } } diff --git a/strbuf.c b/strbuf.c index a884960..666460d 100644 --- a/strbuf.c +++ b/strbuf.c @@ -1,3 +1,4 @@ +#include <ctype.h> #include "cache.h" #include "refs.h" @@ -138,6 +139,44 @@ void strbuf_list_free(struct strbuf **sbs) free(sbs); } +/* + * Return the length of the escape sequence in a string buffer + * starting at index i. If there is no escape sequence starting at + * return 0. + */ +size_t strbuf_esc_sequence_length(const struct strbuf *sb, size_t i) +{ + size_t start = i; + if (sb->buf[i] != '\033') + return 0; + ++i; + + if (i >= sb->len || sb->buf[i] != '[') + return 0; + ++i; + while (i < sb->len && isdigit(sb->buf[i])) + ++i; + + if (i >= sb->len || sb->buf[i] != 'm') + return 0; + return i - start; +} + +size_t strbuf_count_printing_chars(const struct strbuf *sb) +{ + int i; + size_t n = 0; + size_t esc_len; + for (i = 0; i < sb->len; i++) { + esc_len = strbuf_esc_sequence_length(sb, i); + if (esc_len) + i += esc_len; + else if (isgraph(sb->buf[i]) || sb->buf[i] == ' ') + ++n; + } + return n; +} + int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) { int len = a->len < b->len ? a->len: b->len; diff --git a/strbuf.h b/strbuf.h index 9ee908a..3851ddf 100644 --- a/strbuf.h +++ b/strbuf.h @@ -86,6 +86,7 @@ extern void strbuf_tolower(struct strbuf *); extern struct strbuf **strbuf_split(const struct strbuf *, int delim); extern void strbuf_list_free(struct strbuf **); +extern size_t strbuf_count_printing_chars(const struct strbuf *sb); /*----- add data in your buffer -----*/ static inline void strbuf_addch(struct strbuf *sb, int c) { strbuf_grow(sb, 1); -- 1.5.6.3 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v4] graph API: Added logic for colored edges 2009-04-12 20:27 ` [PATCH v4] " Allan Caffee @ 2009-04-12 21:59 ` Junio C Hamano 2009-04-13 19:53 ` [PATCH v5] " Allan Caffee 0 siblings, 1 reply; 13+ messages in thread From: Junio C Hamano @ 2009-04-12 21:59 UTC (permalink / raw) To: git; +Cc: Johannes Schindelin, Jeff King, Nanako Shiraishi, Teemu Likonen Allan Caffee <allan.caffee@gmail.com> writes: > I assumed that the +1 in your example was a typo since AFAIKS ARRAY_SIZE > should give us one past the last index. You are correct. > Also if git is to be expanded allow the use of non-ANSI color codes (or > already does so) the strbuf_escape_sequence_length needs to be updated > to accept the relevant escape codes. Actually, I am starting to hate this. Just step back a bit and imagine how you would do this, if you _were_ writing an application to do this kind of thing, generating output directly to the terminal. You obviously would not seek back and count the width of what you sent out. Instead,...? That's right. You just keep a running total of how much you sent, iow, what column you expect the current cursor should be. Can't we do the same thing here? > @@ -312,6 +351,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph) > return next_interesting_parent(graph, parents); > } > > +unsigned short graph_get_current_column_color(const struct git_graph *graph) static? > +static void graph_increment_column_color(struct git_graph *graph) > +{ > + graph->default_column_color = (graph->default_column_color + 1) % > + ARRAY_SIZE(column_colors); COLUMN_COLORS_MAX? > +unsigned short graph_find_commit_color(const struct git_graph *graph, > + const struct commit *commit) > +{ static? > @@ -714,10 +790,30 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) > strbuf_addch(sb, '*'); > } > > +void graph_draw_octopus_merge(const struct git_graph *graph, > + struct strbuf *sb) static? > @@ -789,6 +880,17 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) > graph_update_state(graph, GRAPH_COLLAPSING); > } > > +struct column *find_new_column_by_commit(struct git_graph *graph, > + struct commit *commit) > +{ static? > diff --git a/strbuf.c b/strbuf.c > index a884960..666460d 100644 > --- a/strbuf.c > +++ b/strbuf.c > @@ -1,3 +1,4 @@ > +#include <ctype.h> > #include "cache.h" > #include "refs.h" BAD. Do not directly include system headers. If you need isgraph(), please support it as part of sane_ctype. But if you count what you emitted so far, you would not have to do this at all. > +/* > + * Return the length of the escape sequence in a string buffer > + * starting at index i. If there is no escape sequence starting at > + * return 0. > + */ > +size_t strbuf_esc_sequence_length(const struct strbuf *sb, size_t i) > +{ > + size_t start = i; > + if (sb->buf[i] != '\033') > + return 0; > + ++i; > + > + if (i >= sb->len || sb->buf[i] != '[') > + return 0; > + ++i; > + while (i < sb->len && isdigit(sb->buf[i])) > + ++i; > + if (i >= sb->len || sb->buf[i] != 'm') > + return 0; These preincrements are extremely unreadable at least for me. if (sb->buf[i++] != '\033') return 0; if (sb->len <= i || sb->buf[i++] != '[') return 0; ... But again the point is hopefully moot. ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5] graph API: Added logic for colored edges 2009-04-12 21:59 ` Junio C Hamano @ 2009-04-13 19:53 ` Allan Caffee 0 siblings, 0 replies; 13+ messages in thread From: Allan Caffee @ 2009-04-13 19:53 UTC (permalink / raw) To: git Cc: Johannes Schindelin, Jeff King, Nanako Shiraishi, Junio C Hamano, Teemu Likonen Modified the graph drawing logic to colorize edges based on parent-child relationships similiarly to gitk. Signed-off-by: Allan Caffee <allan.caffee@gmail.com> --- color.h | 1 + graph.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 199 insertions(+), 48 deletions(-) diff --git a/color.h b/color.h index 6846be1..18abeb7 100644 --- a/color.h +++ b/color.h @@ -11,6 +11,7 @@ #define GIT_COLOR_GREEN "\033[32m" #define GIT_COLOR_YELLOW "\033[33m" #define GIT_COLOR_BLUE "\033[34m" +#define GIT_COLOR_MAGENTA "\033[35m" #define GIT_COLOR_CYAN "\033[36m" #define GIT_COLOR_BG_RED "\033[41m" diff --git a/graph.c b/graph.c index 162a516..0fcb61a 100644 --- a/graph.c +++ b/graph.c @@ -1,5 +1,6 @@ #include "cache.h" #include "commit.h" +#include "color.h" #include "graph.h" #include "diff.h" #include "revision.h" @@ -43,10 +44,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb); /* * TODO: - * - Add colors to the graph. - * Pick a color for each column, and print all characters - * in that column with the specified color. - * * - Limit the number of columns, similar to the way gitk does. * If we reach more than a specified number of columns, omit * sections of some columns. @@ -72,9 +69,10 @@ struct column { */ struct commit *commit; /* - * XXX: Once we add support for colors, struct column could also - * contain the color of its branch line. + * The color to (optionally) print this column in. This is an + * index into column_colors. */ + unsigned short color; }; enum graph_state { @@ -86,6 +84,41 @@ enum graph_state { GRAPH_COLLAPSING }; +/* + * The list of available column colors. + */ +static char column_colors[][COLOR_MAXLEN] = { + GIT_COLOR_RED, + GIT_COLOR_GREEN, + GIT_COLOR_YELLOW, + GIT_COLOR_BLUE, + GIT_COLOR_MAGENTA, + GIT_COLOR_CYAN, + GIT_COLOR_BOLD GIT_COLOR_RED, + GIT_COLOR_BOLD GIT_COLOR_GREEN, + GIT_COLOR_BOLD GIT_COLOR_YELLOW, + GIT_COLOR_BOLD GIT_COLOR_BLUE, + GIT_COLOR_BOLD GIT_COLOR_MAGENTA, + GIT_COLOR_BOLD GIT_COLOR_CYAN, +}; + +#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors)) + +static const char *column_get_color_code(const struct column *c) +{ + return column_colors[c->color]; +} + +static void strbuf_write_column(struct strbuf *sb, const struct column *c, + char col_char) +{ + if (c->color < COLUMN_COLORS_MAX) + strbuf_addstr(sb, column_get_color_code(c)); + strbuf_addch(sb, col_char); + if (c->color < COLUMN_COLORS_MAX) + strbuf_addstr(sb, GIT_COLOR_RESET); +} + struct git_graph { /* * The commit currently being processed @@ -185,6 +218,11 @@ struct git_graph { * temporary array each time we have to output a collapsing line. */ int *new_mapping; + /* + * The current default column color being used. This is + * stored as an index into the array column_colors. + */ + unsigned short default_column_color; }; struct git_graph *graph_init(struct rev_info *opt) @@ -201,6 +239,7 @@ struct git_graph *graph_init(struct rev_info *opt) graph->num_columns = 0; graph->num_new_columns = 0; graph->mapping_size = 0; + graph->default_column_color = 0; /* * Allocate a reasonably large default number of columns @@ -312,6 +351,33 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph) return next_interesting_parent(graph, parents); } +static unsigned short graph_get_current_column_color(const struct git_graph *graph) +{ + if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF)) + return COLUMN_COLORS_MAX; + return graph->default_column_color; +} + +/* + * Update the graph's default column color. + */ +static void graph_increment_column_color(struct git_graph *graph) +{ + graph->default_column_color = (graph->default_column_color + 1) % + COLUMN_COLORS_MAX; +} + +static unsigned short graph_find_commit_color(const struct git_graph *graph, + const struct commit *commit) +{ + int i; + for (i = 0; i < graph->num_columns; i++) { + if (graph->columns[i].commit == commit) + return graph->columns[i].color; + } + return graph_get_current_column_color(graph); +} + static void graph_insert_into_new_columns(struct git_graph *graph, struct commit *commit, int *mapping_index) @@ -334,6 +400,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph, * This commit isn't already in new_columns. Add it. */ graph->new_columns[graph->num_new_columns].commit = commit; + graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit); graph->mapping[*mapping_index] = graph->num_new_columns; *mapping_index += 2; graph->num_new_columns++; @@ -445,6 +512,12 @@ static void graph_update_columns(struct git_graph *graph) for (parent = first_interesting_parent(graph); parent; parent = next_interesting_parent(graph, parent)) { + /* + * If this is a merge increment the current + * color. + */ + if (graph->num_parents > 1) + graph_increment_column_color(graph); graph_insert_into_new_columns(graph, parent->item, &mapping_idx); @@ -560,7 +633,8 @@ static int graph_is_mapping_correct(struct git_graph *graph) return 1; } -static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb) +static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb, + int chars_written) { /* * Add additional spaces to the end of the strbuf, so that all @@ -570,10 +644,10 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb) * aligned for the entire commit. */ int extra; - if (sb->len >= graph->width) + if (chars_written >= graph->width) return; - extra = graph->width - sb->len; + extra = graph->width - chars_written; strbuf_addf(sb, "%*s", (int) extra, ""); } @@ -596,10 +670,11 @@ static void graph_output_padding_line(struct git_graph *graph, * Output a padding row, that leaves all branch lines unchanged */ for (i = 0; i < graph->num_new_columns; i++) { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, &graph->new_columns[i], '|'); + strbuf_addch(sb, ' '); } - graph_pad_horizontally(graph, sb); + graph_pad_horizontally(graph, sb, graph->num_new_columns * 2); } static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb) @@ -609,7 +684,7 @@ static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb) * of the graph is missing. */ strbuf_addstr(sb, "..."); - graph_pad_horizontally(graph, sb); + graph_pad_horizontally(graph, sb, 3); if (graph->num_parents >= 3 && graph->commit_index < (graph->num_columns - 1)) @@ -623,6 +698,7 @@ static void graph_output_pre_commit_line(struct git_graph *graph, { int num_expansion_rows; int i, seen_this; + int chars_written; /* * This function formats a row that increases the space around a commit @@ -645,11 +721,14 @@ static void graph_output_pre_commit_line(struct git_graph *graph, * Output the row */ seen_this = 0; + chars_written = 0; for (i = 0; i < graph->num_columns; i++) { struct column *col = &graph->columns[i]; if (col->commit == graph->commit) { seen_this = 1; - strbuf_addf(sb, "| %*s", graph->expansion_row, ""); + strbuf_write_column(sb, col, '|'); + strbuf_addf(sb, " %*s", graph->expansion_row, ""); + chars_written += 2 + graph->expansion_row; } else if (seen_this && (graph->expansion_row == 0)) { /* * This is the first line of the pre-commit output. @@ -662,17 +741,22 @@ static void graph_output_pre_commit_line(struct git_graph *graph, */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + chars_written++; } else if (seen_this && (graph->expansion_row > 0)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); + chars_written++; } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + chars_written++; } + strbuf_addch(sb, ' '); + chars_written++; } - graph_pad_horizontally(graph, sb); + graph_pad_horizontally(graph, sb, chars_written); /* * Increment graph->expansion_row, @@ -714,10 +798,34 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, '*'); } +/* + * Draw an octopus merge and return the number of characters written. + */ +static int graph_draw_octopus_merge(struct git_graph *graph, + struct strbuf *sb) +{ + /* + * Here dashless_commits represents the number of parents + * which don't need to have dashes (because their edges fit + * neatly under the commit). + */ + const int dashless_commits = 2; + int col_num, i; + int num_dashes = + ((graph->num_parents - dashless_commits) * 2) - 1; + for (i = 0; i < num_dashes; i++) { + col_num = (i / 2) + dashless_commits; + strbuf_write_column(sb, &graph->new_columns[col_num], '-'); + } + col_num = (i / 2) + dashless_commits; + strbuf_write_column(sb, &graph->new_columns[col_num], '.'); + return num_dashes + 1; +} + static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; - int i, j; + int i, chars_written; /* * Output the row containing this commit @@ -727,7 +835,9 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) * children that we have already processed.) */ seen_this = 0; + 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) @@ -740,18 +850,14 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) if (col_commit == graph->commit) { seen_this = 1; graph_output_commit_char(graph, sb); + chars_written++; - if (graph->num_parents < 3) - strbuf_addch(sb, ' '); - else { - int num_dashes = - ((graph->num_parents - 2) * 2) - 1; - for (j = 0; j < num_dashes; j++) - strbuf_addch(sb, '-'); - strbuf_addstr(sb, ". "); - } + if (graph->num_parents > 3) + chars_written += graph_draw_octopus_merge(graph, + sb); } else if (seen_this && (graph->num_parents > 2)) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); + chars_written++; } else if (seen_this && (graph->num_parents == 2)) { /* * This is a 2-way merge commit. @@ -768,15 +874,19 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) */ if (graph->prev_state == GRAPH_POST_MERGE && graph->prev_commit_index < i) - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); else - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + chars_written++; } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + chars_written++; } + strbuf_addch(sb, ' '); + chars_written++; } - graph_pad_horizontally(graph, sb); + graph_pad_horizontally(graph, sb, chars_written); /* * Update graph->state @@ -789,37 +899,75 @@ static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) 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 0; +} + static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; - int i, j; + 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 = graph->columns[i].commit; + 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; - strbuf_addch(sb, '|'); - for (j = 0; j < graph->num_parents - 1; j++) - strbuf_addstr(sb, "\\ "); + 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, ' '); + } + chars_written += j * 2; } else if (seen_this) { - strbuf_addstr(sb, "\\ "); + strbuf_write_column(sb, col, '\\'); + strbuf_addch(sb, ' '); + chars_written += 2; } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + strbuf_addch(sb, ' '); + chars_written += 2; } } - graph_pad_horizontally(graph, sb); + graph_pad_horizontally(graph, sb, chars_written); /* * Update graph->state @@ -912,12 +1060,12 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf if (target < 0) strbuf_addch(sb, ' '); else if (target * 2 == i) - strbuf_addch(sb, '|'); + strbuf_write_column(sb, &graph->new_columns[target], '|'); else - strbuf_addch(sb, '/'); + strbuf_write_column(sb, &graph->new_columns[target], '/'); } - graph_pad_horizontally(graph, sb); + graph_pad_horizontally(graph, sb, graph->mapping_size); /* * Swap mapping and new_mapping @@ -979,9 +1127,10 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) * children that we have already processed.) */ for (i = 0; i < graph->num_columns; i++) { - struct commit *col_commit = graph->columns[i].commit; + struct column *col = &graph->columns[i]; + struct commit *col_commit = col->commit; if (col_commit == graph->commit) { - strbuf_addch(sb, '|'); + strbuf_write_column(sb, col, '|'); if (graph->num_parents < 3) strbuf_addch(sb, ' '); @@ -991,11 +1140,12 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, ' '); } } else { - strbuf_addstr(sb, "| "); + strbuf_write_column(sb, col, '|'); + strbuf_addch(sb, ' '); } } - graph_pad_horizontally(graph, sb); + graph_pad_horizontally(graph, sb, graph->num_columns); /* * Update graph->prev_state since we have output a padding line -- 1.5.6.3 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] graph API: Added logic for colored edges 2009-04-07 18:57 ` [PATCH] graph API: Added logic for colored edges Allan Caffee 2009-04-08 7:59 ` Junio C Hamano @ 2009-04-09 17:58 ` Teemu Likonen 2009-04-09 22:08 ` Allan Caffee 1 sibling, 1 reply; 13+ messages in thread From: Teemu Likonen @ 2009-04-09 17:58 UTC (permalink / raw) To: Allan Caffee; +Cc: git, Johannes Schindelin, Jeff King, Nanako Shiraishi On 2009-04-07 14:57 (-0400), Allan Caffee wrote: > Modified the graph drawing logic to colorize edges based on > parent-child relationships similiarly to gitk. I like the colored graph very much, thanks. Unfortunately there are some problems with aligning of log messages and headers. For example, try this in git.git repository: $ git log -1 --graph 796b137 * commit 796b13781aecce551b8f92049a66646e60f31dce |\ Merge: 6da14ee db12d97 | | Author: Junio C Hamano <gitster@pobox.com> | | Date: 2009-04-08 23:41:27 -0700 Without colors or without your patch the alignment is correct: $ git log -1 --graph --no-color 796b137 * commit 796b13781aecce551b8f92049a66646e60f31dce |\ Merge: 6da14ee db12d97 | | Author: Junio C Hamano <gitster@pobox.com> | | Date: 2009-04-08 23:41:27 -0700 (Perhaps the "Merge:" header could have two spaces befor the data, but this is unrelated to --graph.) ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] graph API: Added logic for colored edges 2009-04-09 17:58 ` [PATCH] " Teemu Likonen @ 2009-04-09 22:08 ` Allan Caffee 0 siblings, 0 replies; 13+ messages in thread From: Allan Caffee @ 2009-04-09 22:08 UTC (permalink / raw) To: Teemu Likonen; +Cc: git, Johannes Schindelin, Jeff King, Nanako Shiraishi On Thu, 09 Apr 2009, Teemu Likonen wrote: > On 2009-04-07 14:57 (-0400), Allan Caffee wrote: > > > Modified the graph drawing logic to colorize edges based on > > parent-child relationships similiarly to gitk. > > I like the colored graph very much, thanks. Unfortunately there are some > problems with aligning of log messages and headers. For example, try > this in git.git repository: > > > $ git log -1 --graph 796b137 > > * commit 796b13781aecce551b8f92049a66646e60f31dce > |\ Merge: 6da14ee db12d97 > | | Author: Junio C Hamano <gitster@pobox.com> > | | Date: 2009-04-08 23:41:27 -0700 > > > Without colors or without your patch the alignment is correct: > > $ git log -1 --graph --no-color 796b137 > > * commit 796b13781aecce551b8f92049a66646e60f31dce > |\ Merge: 6da14ee db12d97 > | | Author: Junio C Hamano <gitster@pobox.com> > | | Date: 2009-04-08 23:41:27 -0700 > > > (Perhaps the "Merge:" header could have two spaces befor the data, but > this is unrelated to --graph.) Thanks for the feedback! The problem here is that graph_horizontal_padding was counting the number of characters in the strbuf in order to decide how much whitespace to pad the row with. The ANSI escape sequences for colors and attributes are not printable characters but do contribute to the length of the strbuf. This can be fixed by adding a new function that counts only printing characters/spaces. I'll include this in my next round. ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2009-04-13 19:55 UTC | newest] Thread overview: 13+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <20090331235922.GA7411@linux.vnet> 2009-04-07 18:57 ` [PATCH] graph API: Added logic for colored edges Allan Caffee 2009-04-08 7:59 ` Junio C Hamano 2009-04-08 21:41 ` Allan Caffee 2009-04-09 0:29 ` Junio C Hamano 2009-04-09 22:22 ` [PATCH v3] " Allan Caffee 2009-04-12 8:44 ` Junio C Hamano 2009-04-12 17:43 ` Allan Caffee 2009-04-12 18:45 ` Junio C Hamano 2009-04-12 20:27 ` [PATCH v4] " Allan Caffee 2009-04-12 21:59 ` Junio C Hamano 2009-04-13 19:53 ` [PATCH v5] " Allan Caffee 2009-04-09 17:58 ` [PATCH] " Teemu Likonen 2009-04-09 22:08 ` Allan Caffee
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).