diff options
Diffstat (limited to 'ui-log.c')
-rw-r--r-- | ui-log.c | 136 |
1 files changed, 123 insertions, 13 deletions
@@ -12,7 +12,7 @@ #include "ui-shared.h" #include "vector.h" -int files, add_lines, rem_lines; +int files, add_lines, rem_lines, lines_counted; /* * The list of available column colors in the commit graph. @@ -66,7 +66,7 @@ void show_commit_decorations(struct commit *commit) strncpy(buf, deco->name + 11, sizeof(buf) - 1); cgit_log_link(buf, NULL, "branch-deco", buf, NULL, ctx.qry.vpath, 0, NULL, NULL, - ctx.qry.showmsg); + ctx.qry.showmsg, 0); } else if (!prefixcmp(deco->name, "tag: refs/tags/")) { strncpy(buf, deco->name + 15, sizeof(buf) - 1); @@ -83,7 +83,7 @@ void show_commit_decorations(struct commit *commit) cgit_log_link(buf, NULL, "remote-deco", NULL, sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0, NULL, NULL, - ctx.qry.showmsg); + ctx.qry.showmsg, 0); } else { strncpy(buf, deco->name, sizeof(buf) - 1); @@ -96,6 +96,74 @@ next: } } +static void handle_rename(struct diff_filepair *pair) +{ + /* + * After we have seen a rename, we generate links to the previous + * name of the file so that commit & diff views get fed the path + * that is correct for the commit they are showing, avoiding the + * need to walk the entire history leading back to every commit we + * show in order detect renames. + */ + if (0 != strcmp(ctx.qry.vpath, pair->two->path)) { + free(ctx.qry.vpath); + ctx.qry.vpath = xstrdup(pair->two->path); + } + inspect_files(pair); +} + +static int show_commit(struct commit *commit, struct rev_info *revs) +{ + struct commit_list *parents = commit->parents; + struct commit *parent; + int found = 0, saved_fmt; + unsigned saved_flags = revs->diffopt.flags; + + + /* Always show if we're not in "follow" mode with a single file. */ + if (!ctx.qry.follow) + return 1; + + /* + * In "follow" mode, we don't show merges. This is consistent with + * "git log --follow -- <file>". + */ + if (parents && parents->next) + return 0; + + /* + * If this is the root commit, do what rev_info tells us. + */ + if (!parents) + return revs->show_root_diff; + + /* When we get here we have precisely one parent. */ + parent = parents->item; + parse_commit(parent); + + files = 0; + add_lines = 0; + rem_lines = 0; + + DIFF_OPT_SET(&revs->diffopt, RECURSIVE); + diff_tree_sha1(parent->tree->object.sha1, + commit->tree->object.sha1, + "", &revs->diffopt); + diffcore_std(&revs->diffopt); + + found = !diff_queue_is_empty(); + saved_fmt = revs->diffopt.output_format; + revs->diffopt.output_format = DIFF_FORMAT_CALLBACK; + revs->diffopt.format_callback = cgit_diff_tree_cb; + revs->diffopt.format_callback_data = handle_rename; + diff_flush(&revs->diffopt); + revs->diffopt.output_format = saved_fmt; + revs->diffopt.flags = saved_flags; + + lines_counted = 1; + return found; +} + static void print_commit(struct commit *commit, struct rev_info *revs) { struct commitinfo *info; @@ -173,7 +241,8 @@ static void print_commit(struct commit *commit, struct rev_info *revs) cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); } - if (ctx.repo->enable_log_filecount || ctx.repo->enable_log_linecount) { + if (!lines_counted && (ctx.repo->enable_log_filecount || + ctx.repo->enable_log_linecount)) { files = 0; add_lines = 0; rem_lines = 0; @@ -324,7 +393,17 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern } } } - if (commit_graph) { + + if (!path || !ctx.cfg.enable_follow_links) { + /* + * If we don't have a path, "follow" is a no-op so make sure + * the variable is set to false to avoid needing to check + * both this and whether we have a path everywhere. + */ + ctx.qry.follow = 0; + } + + if (commit_graph && !ctx.qry.follow) { static const char *graph_arg = "--graph"; static const char *color_arg = "--color"; vector_push(&vec, &graph_arg, 0); @@ -342,7 +421,10 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern } if (path) { + static const char *follow_arg = "--follow"; static const char *double_dash_arg = "--"; + if (ctx.qry.follow) + vector_push(&vec, &follow_arg, 0); vector_push(&vec, &double_dash_arg, 0); vector_push(&vec, &path, 0); } @@ -356,10 +438,17 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern rev.commit_format = CMIT_FMT_DEFAULT; rev.verbose_header = 1; rev.show_root_diff = 0; + rev.simplify_history = 1; setup_revisions(vec.count, vec.data, &rev, NULL); load_ref_decorations(DECORATE_FULL_REFS); rev.show_decorations = 1; rev.grep_filter.regflags |= REG_ICASE; + + rev.diffopt.detect_rename = 1; + rev.diffopt.rename_limit = ctx.cfg.renamelimit; + if (ctx.qry.ignorews) + DIFF_XDL_SET(&rev.diffopt, IGNORE_WHITESPACE); + compile_grep_patterns(&rev.grep_filter); prepare_revision_walk(&rev); @@ -377,11 +466,12 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep, - ctx.qry.search, ctx.qry.showmsg ? 0 : 1); + ctx.qry.search, ctx.qry.showmsg ? 0 : 1, + ctx.qry.follow); html(")"); } html("</th><th class='left'>Author</th>"); - if (commit_graph) + if (rev.graph) html("<th class='left'>Age</th>"); if (ctx.repo->enable_log_filecount) { html("<th class='left'>Files</th>"); @@ -396,15 +486,32 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern if (ofs<0) ofs = 0; - for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { + for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL;) { + if (show_commit(commit, &rev)) + i++; free(commit->buffer); commit->buffer = NULL; free_commit_list(commit->parents); commit->parents = NULL; } - for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { - print_commit(commit, &rev); + for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL;) { + /* + * In "follow" mode, we must count the files and lines the + * first time we invoke diff on a given commit, and we need + * to do that to see if the commit touches the path we care + * about, so we do it in show_commit. Hence we must clear + * lines_counted here. + * + * This has the side effect of avoiding running diff twice + * when we are both following renames and showing file + * and/or line counts. + */ + lines_counted = 0; + if (show_commit(commit, &rev)) { + i++; + print_commit(commit, &rev); + } free(commit->buffer); commit->buffer = NULL; free_commit_list(commit->parents); @@ -417,7 +524,8 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ofs - cnt, ctx.qry.grep, - ctx.qry.search, ctx.qry.showmsg); + ctx.qry.search, ctx.qry.showmsg, + ctx.qry.follow); html("</li>"); } if ((commit = get_revision(&rev)) != NULL) { @@ -425,14 +533,16 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern cgit_log_link("[next]", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ofs + cnt, ctx.qry.grep, - ctx.qry.search, ctx.qry.showmsg); + ctx.qry.search, ctx.qry.showmsg, + ctx.qry.follow); html("</li>"); } html("</ul>"); } else if ((commit = get_revision(&rev)) != NULL) { htmlf("<tr class='nohover'><td colspan='%d'>", columns); cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, - ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg); + ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, + ctx.qry.follow); html("</td></tr>\n"); } |