X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/2b42e5d686833a9f45020dfeb8148832d0ad30f7..b668954412c6709e54c22991b3f7aff012050d7b:/tig.c diff --git a/tig.c b/tig.c index 384fe21..7121a42 100644 --- a/tig.c +++ b/tig.c @@ -59,8 +59,21 @@ 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 */ + +/* 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. */ +/* Size of rev graph with no "padding" columns */ +#define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2)) + /* This color name can be used to refer to the default term colors. */ #define COLOR_DEFAULT (-1) @@ -109,7 +122,7 @@ static size_t utf8_length(const char *string, size_t max_width, int *coloffset, struct ref { char *name; /* Ref name; tag or head names are shortened. */ - char id[41]; /* Commit SHA1 ID */ + char id[SIZEOF_REV]; /* Commit SHA1 ID */ unsigned int tag:1; /* Is it a tag? */ unsigned int next:1; /* For ref lists: are there more refs? */ }; @@ -565,6 +578,7 @@ LINE(TREE, "tree ", COLOR_BLUE, COLOR_DEFAULT, 0), \ LINE(AUTHOR, "author ", COLOR_CYAN, COLOR_DEFAULT, 0), \ LINE(COMMITTER, "committer ", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(SIGNOFF, " Signed-off-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(ACKED, " Acked-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(DEFAULT, "", COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL), \ LINE(CURSOR, "", COLOR_WHITE, COLOR_GREEN, A_BOLD), \ LINE(STATUS, "", COLOR_GREEN, COLOR_DEFAULT, 0), \ @@ -1523,7 +1537,7 @@ scroll_view(struct view *view, enum request request) static void move_view(struct view *view, enum request request) { - bool scroll = FALSE; + int scroll_steps = 0; int steps; switch (request) { @@ -1573,19 +1587,18 @@ move_view(struct view *view, enum request request) /* Check whether the view needs to be scrolled */ if (view->lineno < view->offset || view->lineno >= view->offset + view->height) { + scroll_steps = steps; if (steps < 0 && -steps > view->offset) { - steps = -view->offset; + scroll_steps = -view->offset; } else if (steps > 0) { if (view->lineno == view->lines - 1 && view->lines > view->height) { - steps = view->lines - view->offset - 1; - if (steps >= view->height) - steps -= view->height - 1; + scroll_steps = view->lines - view->offset - 1; + if (scroll_steps >= view->height) + scroll_steps -= view->height - 1; } } - - scroll = TRUE; } if (!view_is_displayed(view)) { @@ -1598,8 +1611,8 @@ move_view(struct view *view, enum request request) if (ABS(steps) < view->height) draw_view_line(view, view->lineno - steps - view->offset); - if (scroll) { - do_scroll_view(view, steps); + if (scroll_steps) { + do_scroll_view(view, scroll_steps); return; } @@ -2447,7 +2460,7 @@ static struct view_ops pager_ops = { * Tree backend */ -/* Parse output from git ls-tree: +/* Parse output from git-ls-tree(1): * * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README @@ -2558,7 +2571,6 @@ static bool tree_enter(struct view *view, struct line *line) { enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; - char *data = line->data; enum request request; switch (line->type) { @@ -2577,6 +2589,7 @@ tree_enter(struct view *view, struct line *line) } else { size_t pathlen = strlen(opt_path); size_t origlen = pathlen; + char *data = line->data; char *basename = data + SIZEOF_TREE_ATTR; if (!string_format_from(opt_path, &pathlen, "%s/", basename)) { @@ -2601,29 +2614,27 @@ tree_enter(struct view *view, struct line *line) open_view(view, request, flags); - if (!VIEW(request)->pipe) - return TRUE; - - /* For tree views insert the path to the parent as the first line. */ - if (request == REQ_VIEW_BLOB) { - /* Mirror what is showed in the title bar. */ - string_ncopy(ref_blob, data + STRING_SIZE("100644 blob "), 40); - string_copy(VIEW(REQ_VIEW_BLOB)->ref, ref_blob); - return TRUE; - } - return TRUE; } static void tree_select(struct view *view, struct line *line) { - if (line->type == LINE_TREE_DIR || line->type == LINE_TREE_FILE) { - char *text = line->data; + char *text = line->data; - string_ncopy(view->ref, text + STRING_SIZE("100644 blob "), 40); - string_copy(ref_blob, view->ref); + text += STRING_SIZE("100644 blob "); + + if (line->type == LINE_TREE_FILE) { + string_ncopy(ref_blob, text, 40); + /* Also update the blob view's ref, since all there must always + * be in sync. */ + string_copy(VIEW(REQ_VIEW_BLOB)->ref, ref_blob); + + } else if (line->type != LINE_TREE_DIR) { + return; } + + string_ncopy(view->ref, text, 40); } static struct view_ops tree_ops = { @@ -2661,7 +2672,7 @@ static struct view_ops blob_ops = { */ struct commit { - char id[41]; /* SHA1 ID. */ + char id[SIZEOF_REV]; /* SHA1 ID. */ char title[75]; /* First line of the commit message. */ char author[75]; /* Author of the commit. */ struct tm time; /* Date from the author ident. */ @@ -2777,6 +2788,119 @@ 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[3]; +static size_t graph_stack_no; + +/* Parents of the commit being visualized. */ +static struct rev_stack graph_parents[2]; + +static size_t graph_last_rev; + +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); + } +} + +static void +draw_rev_graph(struct commit *commit, size_t stackpos, + struct rev_stack *stack, struct rev_stack *parents, + struct rev_stack *prev_parents) +{ + chtype symbol, separator, line; + size_t i; + + /* Place the symbol for this commit. */ + if (parents->size == 0) + symbol = REVGRAPH_INIT; + else if (parents->size > 1) + symbol = REVGRAPH_MERGE; + else if (stackpos >= stack->size) + symbol = REVGRAPH_BRANCH; + else + symbol = REVGRAPH_COMMIT; + + separator = ' '; + line = REVGRAPH_LINE; + + for (i = 0; i < stackpos; i++) { + commit->graph[commit->graph_size++] = line; + if (prev_parents->size > 1 && + i == graph_last_rev) { + separator = '`'; + line = '.'; + } + commit->graph[commit->graph_size++] = separator; + } + + commit->graph[commit->graph_size++] = symbol; + + separator = ' '; + line = REVGRAPH_LINE; + i++; + + for (; i < stack->size; i++) { + commit->graph[commit->graph_size++] = separator; + commit->graph[commit->graph_size++] = line; + if (prev_parents->size > 1) { + if (i < graph_last_rev + prev_parents->size) { + separator = '`'; + line = '.'; + } + } + } +} + +void +update_rev_graph(struct commit *commit) +{ + struct rev_stack *parents = &graph_parents[graph_stack_no & 1]; + struct rev_stack *stack = &graph_stacks[graph_stack_no++ & 1]; + struct rev_stack *prev_parents = &graph_parents[graph_stack_no & 1]; + struct rev_stack *graph = &graph_stacks[graph_stack_no & 1]; + size_t stackpos = 0; + size_t i; + + 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)) + break; + + push_rev_stack(graph, stack->rev[stackpos]); + } + + assert(commit->graph_size < ARRAY_SIZE(commit->graph)); + + for (i = 0; i < parents->size; i++) + push_rev_stack(graph, parents->rev[i]); + + /* FIXME: Moving branches left and right when collapsing a branch. */ + for (i = stackpos + 1; i < stack->size; i++) + push_rev_stack(graph, stack->rev[i]); + + draw_rev_graph(commit, stackpos, stack, parents, prev_parents); + graph_last_rev = stackpos; + stack->size = prev_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) @@ -2796,7 +2920,14 @@ main_read(struct view *view, char *line) view->line[view->lines++].data = commit; string_copy(commit->id, line); commit->refs = get_refs(commit->id); - commit->graph[commit->graph_size++] = ACS_LTEE; + fprintf(stderr, "\n%p [%s]", &graph_stacks[graph_stack_no & 1], commit->id); + break; + + case LINE_PARENT: + if (commit) { + line += STRING_SIZE("parent "); + push_rev_stack(&graph_parents[graph_stack_no & 1], line); + } break; case LINE_AUTHOR: @@ -2807,6 +2938,8 @@ main_read(struct view *view, char *line) if (!commit) break; + update_rev_graph(commit); + if (end) { char *email = end + 1;