X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/2b7575338c7e82c0f0973c0f50e76b20083862e2..18ffaa238dfd737bc8205a6a949dc3d55cb4fd7b:/tig.c diff --git a/tig.c b/tig.c index 4073723..0948cb7 100644 --- a/tig.c +++ b/tig.c @@ -60,10 +60,16 @@ static size_t utf8_length(const char *string, size_t max_width, int *coloffset, #define SIZEOF_STR 1024 /* Default string size. */ #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */ #define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL */ -#define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */ -/* Size of rev graph with no "padding" columns */ -#define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2)) +/* Revision graph */ + +#define REVGRAPH_INIT 'I' +#define REVGRAPH_MERGE 'M' +#define REVGRAPH_BRANCH '+' +#define REVGRAPH_COMMIT '*' +#define REVGRAPH_LINE '|' + +#define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */ /* This color name can be used to refer to the default term colors. */ #define COLOR_DEFAULT (-1) @@ -2659,7 +2665,7 @@ static struct view_ops blob_ops = { /* - * Main view backend + * Revision graph */ struct commit { @@ -2672,6 +2678,175 @@ struct commit { size_t graph_size; /* The width of the graph array. */ }; +/* Size of rev graph with no "padding" columns */ +#define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2)) + +struct rev_graph { + struct rev_graph *prev, *next, *parents; + char rev[SIZEOF_REVITEMS][SIZEOF_REV]; + size_t size; + struct commit *commit; + size_t pos; +}; + +/* Parents of the commit being visualized. */ +static struct rev_graph graph_parents[3]; + +/* The current stack of revisions on the graph. */ +static struct rev_graph graph_stacks[3] = { + { &graph_stacks[2], &graph_stacks[1], &graph_parents[0] }, + { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] }, + { &graph_stacks[1], &graph_stacks[0], &graph_parents[2] }, +}; + +static inline bool +graph_parent_is_merge(struct rev_graph *graph) +{ + return graph->parents->size > 1; +} + +static inline void +append_to_rev_graph(struct rev_graph *graph, chtype symbol) +{ + if (graph->commit->graph_size < ARRAY_SIZE(graph->commit->graph) - 1) + graph->commit->graph[graph->commit->graph_size++] = symbol; +} + +static void +done_rev_graph(struct rev_graph *graph) +{ + if (graph_parent_is_merge(graph) && + graph->pos < graph->size - 1 && + graph->next->size == graph->size + graph->parents->size - 1) { + size_t i = graph->pos + graph->parents->size - 1; + + graph->commit->graph_size = i * 2; + while (i < graph->next->size - 1) { + append_to_rev_graph(graph, ' '); + append_to_rev_graph(graph, '\\'); + i++; + } + } + + graph->size = graph->pos = 0; + graph->commit = NULL; + memset(graph->parents, 0, sizeof(*graph->parents)); +} + +static void +push_rev_graph(struct rev_graph *graph, char *parent) +{ + /* Combine duplicate parents lines. */ + if (graph->size > 0 && + !strncmp(graph->rev[graph->size - 1], parent, SIZEOF_REV)) + return; + + if (graph->size < SIZEOF_REVITEMS) { + string_ncopy(graph->rev[graph->size++], parent, SIZEOF_REV); + } +} + +static chtype +get_rev_graph_symbol(struct rev_graph *graph) +{ + chtype symbol; + + if (graph->parents->size == 0) + symbol = REVGRAPH_INIT; + else if (graph_parent_is_merge(graph)) + symbol = REVGRAPH_MERGE; + else if (graph->pos >= graph->size) + symbol = REVGRAPH_BRANCH; + else + symbol = REVGRAPH_COMMIT; + + return symbol; +} + +static void +draw_rev_graph(struct rev_graph *graph) +{ + chtype separator, line; + chtype symbol = get_rev_graph_symbol(graph); + size_t i; + + separator = ' '; + line = REVGRAPH_LINE; + + for (i = 0; i < graph->pos; i++) { + append_to_rev_graph(graph, line); + if (graph_parent_is_merge(graph->prev) && + graph->prev->pos == i) { + separator = '`'; + line = '.'; + } + append_to_rev_graph(graph, separator); + } + + /* Place the symbol for this revision. */ + append_to_rev_graph(graph, symbol); + + if (graph->prev->size > graph->size) { + separator = '\''; + line = ' '; + } else { + separator = ' '; + line = REVGRAPH_LINE; + } + i++; + + for (; i < graph->size; i++) { + append_to_rev_graph(graph, separator); + append_to_rev_graph(graph, line); + if (graph_parent_is_merge(graph->prev)) { + if (i < graph->prev->pos + graph->parents->size) { + separator = '`'; + line = '.'; + } + } + if (graph->prev->size > graph->size) { + separator = '/'; + line = ' '; + } + } + + if (graph->prev->size > graph->size) { + append_to_rev_graph(graph, separator); + if (line != ' ') + append_to_rev_graph(graph, line); + } +} + +void +update_rev_graph(struct rev_graph *graph) +{ + size_t i; + + /* First, traverse all lines of revisions up to the active one. */ + for (graph->pos = 0; graph->pos < graph->size; graph->pos++) { + if (!strcmp(graph->rev[graph->pos], graph->commit->id)) + break; + + push_rev_graph(graph->next, graph->rev[graph->pos]); + } + + /* Interleave the new revision parent(s). */ + for (i = 0; i < graph->parents->size; i++) + push_rev_graph(graph->next, graph->parents->rev[i]); + + /* Lastly, put any remaining revisions. */ + for (i = graph->pos + 1; i < graph->size; i++) + push_rev_graph(graph->next, graph->rev[i]); + + draw_rev_graph(graph); + done_rev_graph(graph->prev); +} + + +/* + * Main view backend + */ + static bool main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) { @@ -2779,90 +2954,11 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select return TRUE; } - -struct rev_stack { - char rev[SIZEOF_REVITEMS][SIZEOF_REV]; - size_t size; -}; - -/* The current stack of revisions on the graph. */ -static struct rev_stack graph_stacks[2]; -static unsigned int graph_stack_no; - -/* Parents of the commit being visualized. */ -static struct rev_stack graph_parents; - -static void -push_rev_stack(struct rev_stack *stack, char *parent) -{ - fprintf(stderr, " (%s)", parent); - - /* Combine duplicate parents lines. */ - if (stack->size > 0 && - !strncmp(stack->rev[stack->size - 1], parent, SIZEOF_REV)) - return; - - if (stack->size < SIZEOF_REVITEMS) { - string_ncopy(stack->rev[stack->size++], parent, SIZEOF_REV); - } -} - -void -update_rev_graph(struct commit *commit) -{ - struct rev_stack *stack = &graph_stacks[graph_stack_no++ & 1]; - struct rev_stack *graph = &graph_stacks[graph_stack_no & 1]; - size_t stackpos = 0; - size_t i; - - // FIXME: Initial commit ... assert(rev_graph_commit == commit); - fprintf(stderr, "\n%p <%s> ", graph, commit->id); - - /* First traverse all lines of revisions up to the active one. */ - for (stackpos = 0; stackpos < stack->size; stackpos++) { - if (!strcmp(stack->rev[stackpos], commit->id)) { - while (stackpos + 1< stack->size && - !strcmp(stack->rev[stackpos + 1], commit->id)) - stackpos++; - break; - } - - push_rev_stack(graph, stack->rev[stackpos]); - commit->graph[commit->graph_size++] = ACS_VLINE; - commit->graph[commit->graph_size++] = ' '; - } - - assert(commit->graph_size < ARRAY_SIZE(commit->graph)); - - for (i = 0; i < graph_parents.size; i++) - push_rev_stack(graph, graph_parents.rev[i]); - - /* Place the symbol for this commit. */ - if (graph_parents.size == 0) - commit->graph[commit->graph_size++] = 'I'; - else if (graph_parents.size > 1) - commit->graph[commit->graph_size++] = 'M'; - else if (stackpos >= stack->size) - commit->graph[commit->graph_size++] = '+'; - else - commit->graph[commit->graph_size++] = '*'; - - stackpos++; - - /* FIXME: Moving branches left and right when collapsing a branch. */ - while (stackpos < stack->size) { - push_rev_stack(graph, stack->rev[stackpos++]); - commit->graph[commit->graph_size++] = ' '; - commit->graph[commit->graph_size++] = ACS_VLINE; - } - - stack->size = graph_parents.size = 0; -} - /* Reads git log --pretty=raw output and parses it into the commit struct. */ static bool main_read(struct view *view, char *line) { + static struct rev_graph *graph = graph_stacks; enum line_type type = get_line_type(line); struct commit *commit = view->lines ? view->line[view->lines - 1].data : NULL; @@ -2878,13 +2974,13 @@ main_read(struct view *view, char *line) view->line[view->lines++].data = commit; string_copy(commit->id, line); commit->refs = get_refs(commit->id); - fprintf(stderr, "\n%p [%s]", &graph_stacks[graph_stack_no], commit->id); + graph->commit = commit; break; case LINE_PARENT: if (commit) { line += STRING_SIZE("parent "); - push_rev_stack(&graph_parents, line); + push_rev_graph(graph->parents, line); } break; @@ -2896,7 +2992,8 @@ main_read(struct view *view, char *line) if (!commit) break; - update_rev_graph(commit); + update_rev_graph(graph); + graph = graph->next; if (end) { char *email = end + 1;