Diffstat (limited to 'ui-blame.c')
| -rw-r--r-- | ui-blame.c | 184 |
1 files changed, 137 insertions, 47 deletions
@@ -6,11 +6,13 @@ * (see COPYING for full license text) */ +#define USE_THE_REPOSITORY_VARIABLE + #include "cgit.h" #include "ui-blame.h" #include "html.h" #include "ui-shared.h" -#include "argv-array.h" +#include "strvec.h" #include "blame.h" @@ -41,37 +43,64 @@ static char *emit_suspect_detail(struct blame_origin *suspect) return strbuf_detach(&detail, NULL); } -static void emit_blame_entry(struct blame_scoreboard *sb, - struct blame_entry *ent) +static void emit_blame_entry_hash(struct blame_entry *ent) { struct blame_origin *suspect = ent->suspect; struct object_id *oid = &suspect->commit->object.oid; - const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n"; - const char *cp, *cpend; + unsigned long line = 0; char *detail = emit_suspect_detail(suspect); - - html("<tr><td class='sha1 lines'>"); - cgit_commit_link(find_unique_abbrev(oid->hash, DEFAULT_ABBREV), detail, + html("<span class='oid'>"); + cgit_commit_link(repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV), detail, NULL, ctx.qry.head, oid_to_hex(oid), suspect->path); - html("</td>\n"); - + html("</span>"); free(detail); - if (ctx.cfg.enable_tree_linenumbers) { - unsigned long lineno = ent->lno; - html("<td class='linenumbers'><pre>"); - while (lineno < ent->lno + ent->num_lines) - htmlf(numberfmt, ++lineno); - html("</pre></td>\n"); + if (!repo_parse_commit(the_repository, suspect->commit) && suspect->commit->parents) { + struct commit *parent = suspect->commit->parents->item; + + html(" "); + cgit_blame_link("^", "Blame the previous revision", NULL, + ctx.qry.head, oid_to_hex(&parent->object.oid), + suspect->path); } - cp = blame_nth_line(sb, ent->lno); - cpend = blame_nth_line(sb, ent->lno + ent->num_lines); + while (line++ < ent->num_lines) + html("\n"); +} + +static void emit_blame_entry_linenumber(struct blame_entry *ent) +{ + const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n"; + + unsigned long lineno = ent->lno; + while (lineno < ent->lno + ent->num_lines) + htmlf(numberfmt, ++lineno); +} + +static void emit_blame_entry_line_background(struct blame_scoreboard *sb, + struct blame_entry *ent) +{ + unsigned long line; + size_t len, maxlen = 2; + const char* pos, *endpos; + + for (line = ent->lno; line < ent->lno + ent->num_lines; line++) { + html("\n"); + pos = blame_nth_line(sb, line); + endpos = blame_nth_line(sb, line + 1); + len = 0; + while (pos < endpos) { + len++; + if (*pos++ == '\t') + len = (len + 7) & ~7; + } + if (len > maxlen) + maxlen = len; + } - html("<td class='lines'><pre><code>"); - html_ntxt(cp, cpend - cp); - html("</code></pre></td></tr>\n"); + for (len = 0; len < maxlen - 1; len++) + html(" "); } struct walk_tree_context { @@ -80,32 +109,42 @@ struct walk_tree_context { int state; }; -static void print_object(const unsigned char *sha1, const char *path, +static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev) { enum object_type type; + char *buf; unsigned long size; - struct argv_array rev_argv = ARGV_ARRAY_INIT; + struct strvec rev_argv = STRVEC_INIT; struct rev_info revs; struct blame_scoreboard sb; struct blame_origin *o; struct blame_entry *ent = NULL; - type = sha1_object_info(sha1, &size); + type = odb_read_object_info(the_repository->objects, oid, &size); if (type == OBJ_BAD) { cgit_print_error_page(404, "Not found", "Bad object name: %s", - sha1_to_hex(sha1)); + oid_to_hex(oid)); return; } - argv_array_push(&rev_argv, "blame"); - argv_array_push(&rev_argv, rev); - init_revisions(&revs, NULL); - DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV); - setup_revisions(rev_argv.argc, rev_argv.argv, &revs, NULL); + buf = odb_read_object(the_repository->objects, oid, &type, &size); + if (!buf) { + cgit_print_error_page(500, "Internal server error", + "Error reading object %s", oid_to_hex(oid)); + return; + } + + strvec_push(&rev_argv, "blame"); + strvec_push(&rev_argv, rev); + repo_init_revisions(the_repository, &revs, NULL); + revs.diffopt.flags.allow_textconv = 1; + setup_revisions(rev_argv.nr, rev_argv.v, &revs, NULL); init_scoreboard(&sb); sb.revs = &revs; - setup_scoreboard(&sb, path, &o); + sb.repo = the_repository; + sb.path = path; + setup_scoreboard(&sb, &o); o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o); prio_queue_put(&sb.commits, o->commit); blame_origin_decref(o); @@ -118,35 +157,86 @@ static void print_object(const unsigned char *sha1, const char *path, cgit_set_title_from_path(path); cgit_print_layout_start(); - htmlf("blob: %s (", sha1_to_hex(sha1)); + htmlf("blob: %s (", oid_to_hex(oid)); cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); html(") ("); cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path); html(")\n"); + if (buffer_is_binary(buf, size)) { + html("<div class='error'>blob is binary.</div>"); + goto cleanup; + } if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) { htmlf("<div class='error'>blob size (%ldKB)" " exceeds display size limit (%dKB).</div>", size / 1024, ctx.cfg.max_blob_size); - return; + goto cleanup; + } + + html("<table class='blame blob'>\n<tr>\n"); + + /* Commit hashes */ + html("<td class='hashes'>"); + for (ent = sb.ent; ent; ent = ent->next) { + html("<div class='alt'><pre>"); + emit_blame_entry_hash(ent); + html("</pre></div>"); } + html("</td>\n"); - html("<table class='blame blob'>"); + /* Line numbers */ + if (ctx.cfg.enable_tree_linenumbers) { + html("<td class='linenumbers'>"); + for (ent = sb.ent; ent; ent = ent->next) { + html("<div class='alt'><pre>"); + emit_blame_entry_linenumber(ent); + html("</pre></div>"); + } + html("</td>\n"); + } + + html("<td class='lines'><div>"); + + /* Colored bars behind lines */ + html("<div>"); for (ent = sb.ent; ent; ) { struct blame_entry *e = ent->next; - emit_blame_entry(&sb, ent); + html("<div class='alt'><pre>"); + emit_blame_entry_line_background(&sb, ent); + html("</pre></div>"); free(ent); ent = e; } - html("</table>\n"); + html("</div>"); + free((void *)sb.final_buf); + /* Lines */ + html("<pre><code>"); + if (ctx.repo->source_filter) { + char *filter_arg = xstrdup(basename); + cgit_open_filter(ctx.repo->source_filter, filter_arg); + html_raw(buf, size); + cgit_close_filter(ctx.repo->source_filter); + free(filter_arg); + } else { + html_txt(buf); + } + html("</code></pre>"); + + html("</div></td>\n"); + + html("</tr>\n</table>\n"); + cgit_print_layout_end(); + +cleanup: + free(buf); } -static int walk_tree(const unsigned char *sha1, struct strbuf *base, - const char *pathname, unsigned mode, int stage, - void *cbdata) +static int walk_tree(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; @@ -155,7 +245,7 @@ static int walk_tree(const unsigned char *sha1, struct strbuf *base, struct strbuf buffer = STRBUF_INIT; strbuf_addbuf(&buffer, base); strbuf_addstr(&buffer, pathname); - print_object(sha1, buffer.buf, pathname, + print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev); strbuf_release(&buffer); walk_tree_ctx->state = 1; @@ -173,7 +263,7 @@ static int walk_tree(const unsigned char *sha1, struct strbuf *base, static int basedir_len(const char *path) { - char *p = strrchr(path, '/'); + const char *p = strrchr(path, '/'); if (p) return p - path + 1; return 0; @@ -181,7 +271,7 @@ static int basedir_len(const char *path) void cgit_print_blame(void) { - const char *rev = ctx.qry.sha1; + const char *rev = ctx.qry.oid; struct object_id oid; struct commit *commit; struct pathspec_item path_items = { @@ -199,13 +289,13 @@ void cgit_print_blame(void) if (!rev) rev = ctx.qry.head; - if (get_oid(rev, &oid)) { + if (repo_get_oid(the_repository, rev, &oid)) { cgit_print_error_page(404, "Not found", "Invalid revision name: %s", rev); return; } - commit = lookup_commit_reference(&oid); - if (!commit || parse_commit(commit)) { + commit = lookup_commit_reference(the_repository, &oid); + if (!commit || repo_parse_commit(the_repository, commit)) { cgit_print_error_page(404, "Not found", "Invalid commit reference: %s", rev); return; @@ -215,8 +305,8 @@ void cgit_print_blame(void) walk_tree_ctx.match_baselen = (path_items.match) ? basedir_len(path_items.match) : -1; - read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, - &walk_tree_ctx); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.state) cgit_print_error_page(404, "Not found", "Not found"); else if (walk_tree_ctx.state == 2) |