]> git.cameronkatri.com Git - cgit.git/blobdiff - ui-blame.c
cgitrc.5: add local tar signature example
[cgit.git] / ui-blame.c
index 62cf4315da77b1a3c429d9c333af72b09ce5a4a9..50d05803b4e621f4cefe4e6e67b4a88f887eafa5 100644 (file)
@@ -41,37 +41,55 @@ 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='sha1'>");
+       cgit_commit_link(find_unique_abbrev(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");
-       }
+       while (line++ < ent->num_lines)
+               html("\n");
+}
 
-       cp = blame_nth_line(sb, ent->lno);
-       cpend = blame_nth_line(sb, ent->lno + ent->num_lines);
+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";
 
-       html("<td class='lines'><pre><code>");
-       html_ntxt(cp, cpend - cp);
-       html("</code></pre></td></tr>\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;
+       }
+
+       for (len = 0; len < maxlen - 1; len++)
+               html(" ");
 }
 
 struct walk_tree_context {
@@ -80,10 +98,11 @@ 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 rev_info revs;
@@ -91,17 +110,24 @@ static void print_object(const unsigned char *sha1, const char *path,
        struct blame_origin *o;
        struct blame_entry *ent = NULL;
 
-       type = sha1_object_info(sha1, &size);
+       type = oid_object_info(the_repository, 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;
+       }
+
+       buf = read_object_file(oid, &type, &size);
+       if (!buf) {
+               cgit_print_error_page(500, "Internal server error",
+                       "Error reading object %s", 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);
+       revs.diffopt.flags.allow_textconv = 1;
        setup_revisions(rev_argv.argc, rev_argv.argv, &revs, NULL);
        init_scoreboard(&sb);
        sb.revs = &revs;
@@ -118,7 +144,7 @@ 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);
@@ -128,23 +154,71 @@ static void print_object(const unsigned char *sha1, const char *path,
                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'>");
+       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");
+
+       /* 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,
+static int walk_tree(const struct object_id *oid, struct strbuf *base,
                     const char *pathname, unsigned mode, int stage,
                     void *cbdata)
 {
@@ -155,7 +229,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;
@@ -215,7 +289,7 @@ 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,
+       read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree,
                &walk_tree_ctx);
        if (!walk_tree_ctx.state)
                cgit_print_error_page(404, "Not found", "Not found");