From mboxrd@z Thu Jan 1 00:00:00 1970 From: whydoubt at gmail.com (Jeff Smith) Date: Fri, 22 Sep 2017 22:38:46 -0500 Subject: [RFCv2 PATCH 5/7] ui-blame: pull blame info from libgit In-Reply-To: <20170923033848.5922-1-whydoubt@gmail.com> References: <20170608021810.12964-1-whydoubt@gmail.com> <20170923033848.5922-1-whydoubt@gmail.com> Message-ID: <20170923033848.5922-6-whydoubt@gmail.com> Use the blame interface added in libgit to output the blame information of a file in the repository. Signed-off-by: Jeff Smith --- cgit.css | 8 +++ ui-blame.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 216 insertions(+), 2 deletions(-) diff --git a/cgit.css b/cgit.css index 1dc2c11..836f8ae 100644 --- a/cgit.css +++ b/cgit.css @@ -329,6 +329,14 @@ div#cgit table.ssdiff td.lineno a:hover { color: black; } +div#cgit table.blame tr:nth-child(even) { + background: #eee; +} + +div#cgit table.blame tr:nth-child(odd) { + background: white; +} + div#cgit table.bin-blob { margin-top: 0.5em; border: solid 1px black; diff --git a/ui-blame.c b/ui-blame.c index 901ca89..cc4457a 100644 --- a/ui-blame.c +++ b/ui-blame.c @@ -10,6 +10,183 @@ #include "ui-blame.h" #include "html.h" #include "ui-shared.h" +#include "argv-array.h" +#include "blame.h" + + +/* + * Information on commits, used for output. + */ +struct commit_info { + struct strbuf author; + struct strbuf author_mail; + struct ident_split author_ident; + + /* filled only when asked for details */ + struct strbuf committer; + struct strbuf committer_mail; + struct ident_split committer_ident; + + struct strbuf summary; +}; + +/* + * Parse author/committer line in the commit object buffer + */ +static void get_ac_line(const char *inbuf, const char *what, + struct strbuf *name, struct strbuf *mail, + struct ident_split *ident) +{ + size_t len, maillen, namelen; + char *tmp, *endp; + const char *namebuf, *mailbuf; + + tmp = strstr(inbuf, what); + if (!tmp) + goto error_out; + tmp += strlen(what); + endp = strchr(tmp, '\n'); + if (!endp) + len = strlen(tmp); + else + len = endp - tmp; + + if (split_ident_line(ident, tmp, len)) { + error_out: + /* Ugh */ + tmp = "(unknown)"; + strbuf_addstr(name, tmp); + strbuf_addstr(mail, tmp); + return; + } + + namelen = ident->name_end - ident->name_begin; + namebuf = ident->name_begin; + + maillen = ident->mail_end - ident->mail_begin; + mailbuf = ident->mail_begin; + + strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf); + strbuf_add(name, namebuf, namelen); +} + +static void commit_info_init(struct commit_info *ci) +{ + + strbuf_init(&ci->author, 0); + strbuf_init(&ci->author_mail, 0); + strbuf_init(&ci->committer, 0); + strbuf_init(&ci->committer_mail, 0); + strbuf_init(&ci->summary, 0); +} + +static void commit_info_destroy(struct commit_info *ci) +{ + + strbuf_release(&ci->author); + strbuf_release(&ci->author_mail); + strbuf_release(&ci->committer); + strbuf_release(&ci->committer_mail); + strbuf_release(&ci->summary); +} + +static void get_commit_info(struct commit *commit, + struct commit_info *ret, + int detailed) +{ + int len; + const char *subject, *encoding; + const char *message; + + commit_info_init(ret); + + encoding = get_log_output_encoding(); + message = logmsg_reencode(commit, NULL, encoding); + get_ac_line(message, "\nauthor ", + &ret->author, &ret->author_mail, + &ret->author_ident); + + if (!detailed) { + unuse_commit_buffer(commit, message); + return; + } + + get_ac_line(message, "\ncommitter ", + &ret->committer, &ret->committer_mail, + &ret->committer_ident); + + len = find_commit_subject(message, &subject); + if (len) + strbuf_add(&ret->summary, subject, len); + else + strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid)); + + unuse_commit_buffer(commit, message); +} + +static char *emit_one_suspect_detail(struct blame_origin *suspect, const char *hex) +{ + struct commit_info ci; + struct strbuf detail = STRBUF_INIT; + + get_commit_info(suspect->commit, &ci, 1); + + strbuf_addf(&detail, "author %s", ci.author.buf); + if (!ctx.cfg.noplainemail) + strbuf_addf(&detail, " %s", ci.author_mail.buf); + strbuf_addf(&detail, " %s\n", + show_ident_date(&ci.author_ident, + cgit_date_mode(DATE_ISO8601))); + + strbuf_addf(&detail, "committer %s", ci.committer.buf); + if (!ctx.cfg.noplainemail) + strbuf_addf(&detail, " %s", ci.committer_mail.buf); + strbuf_addf(&detail, " %s\n\n", + show_ident_date(&ci.committer_ident, + cgit_date_mode(DATE_ISO8601))); + + strbuf_addbuf(&detail, &ci.summary); + + commit_info_destroy(&ci); + return strbuf_detach(&detail, NULL); +} + +static void emit_blame_entry(struct blame_scoreboard *sb, struct blame_entry *ent) +{ + struct blame_origin *suspect = ent->suspect; + char hex[GIT_SHA1_HEXSZ + 1]; + char *detail, *abbrev; + unsigned long lineno; + const char *numberfmt = "%1$d\n"; + const char *cp, *cpend; + + oid_to_hex_r(hex, &suspect->commit->object.oid); + detail = emit_one_suspect_detail(suspect, hex); + abbrev = xstrdup(find_unique_abbrev(suspect->commit->object.oid.hash, + DEFAULT_ABBREV)); + + html("
");
+	cgit_commit_link(abbrev, detail, NULL, ctx.qry.head, hex, suspect->path);
+	html("
\n"); + + free(detail); + free(abbrev); + + if (ctx.cfg.enable_tree_linenumbers) { + html("
");
+		lineno = ent->lno;
+		while (lineno < ent->lno + ent->num_lines)
+			htmlf(numberfmt, ++lineno);
+		html("
\n"); + } + + cp = blame_nth_line(sb, ent->lno); + cpend = blame_nth_line(sb, ent->lno + ent->num_lines); + + html("
");
+	html_ntxt(cpend - cp, cp);
+	html("
\n"); +} struct walk_tree_context { char *curr_rev; @@ -47,10 +224,16 @@ static void set_title_from_path(const char *path) strcat(new_title, ctx.page.title); ctx.page.title = new_title; } + static void print_object(const unsigned char *sha1, const char *path, const char *basename, const char *rev) { enum object_type type; unsigned long size; + struct argv_array rev_argv = ARGV_ARRAY_INIT; + struct rev_info revs; + struct blame_scoreboard sb; + struct blame_origin *o; + struct blame_entry *ent = NULL; type = sha1_object_info(sha1, &size); if (type == OBJ_BAD) { @@ -59,7 +242,22 @@ static void print_object(const unsigned char *sha1, const char *path, const char return; } - /* Read in applicable data here */ + 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); + init_scoreboard(&sb); + sb.revs = &revs; + setup_scoreboard(&sb, path, &o); + o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o); + prio_queue_put(&sb.commits, o->commit); + blame_origin_decref(o); + sb.ent = NULL; + sb.path = path; + assign_blame(&sb, 0); + blame_sort_final(&sb); + blame_coalesce(&sb); set_title_from_path(path); @@ -76,7 +274,15 @@ static void print_object(const unsigned char *sha1, const char *path, const char return; } - /* Output data here */ + html(""); + for (ent = sb.ent; ent; ) { + struct blame_entry *e = ent->next; + emit_blame_entry(&sb, ent); + free(ent); + ent = e; + } + html("
\n"); + free((void *)sb.final_buf); cgit_print_layout_end(); return; -- 2.9.4