diff options
| author | Bryce Kwon <bryce@brycekwon.com> | |
|---|---|---|
| committer | Bryce Kwon <bryce@brycekwon.com> | |
| commit | ||
| parent | ||
| tree | ||
| download | ||
Split trailers out of the commit message
Diffstat (limited to '')
| -rw-r--r-- | source/ui-commit.c | 128 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 file changed, 125 insertions, 3 deletions
diff --git a/source/ui-commit.c b/source/ui-commit.c index 6055509..b67f5db 100644 --- a/source/ui-commit.c +++ b/source/ui-commit.c @@ -18,6 +18,9 @@ #include "ui-log.h" #include "ui-shared.h" +#include <list.h> +#include <trailer.h> + // The diff below the message is always taken against the first parent alone, // which says little about a merge of many branches, so a commit with this // many parents or more is shown without a diff at all. @@ -76,6 +79,127 @@ static void print_filtered_text(const char *text) cgit_close_filter(ctx.repo->commit_filter); } +/* + * A value shaped like an ident is written the way the author row is, through + * the email filter. Anything else goes through the trailer filter when one is + * set, and otherwise through the commit filter, so the links a site already + * draws in message text keep appearing on trailer lines. + */ +static void print_trailer_value(const char *key, const char *value) +{ + struct ident_split ident; + struct strbuf name = STRBUF_INIT, email = STRBUF_INIT; + + if (!split_ident_line(&ident, value, strlen(value)) && + memchr(ident.mail_begin, '@', ident.mail_end - ident.mail_begin)) { + strbuf_add(&name, ident.name_begin, ident.name_end - ident.name_begin); + strbuf_addch(&email, '<'); + strbuf_add(&email, ident.mail_begin, ident.mail_end - ident.mail_begin); + strbuf_addch(&email, '>'); + cgit_open_filter(ctx.repo->email_filter, email.buf, "commit"); + html_txt(name.buf); + if (ctx.cfg.enable_plain_email) { + html(" "); + html_txt(email.buf); + } + cgit_close_filter(ctx.repo->email_filter); + strbuf_release(&name); + strbuf_release(&email); + } else if (ctx.repo->trailer_filter) { + cgit_open_filter(ctx.repo->trailer_filter, key, "commit"); + html_txt(value); + cgit_close_filter(ctx.repo->trailer_filter); + } else + print_filtered_text(value); +} + +/* + * One row per trailer, except that a run of trailers sharing a key becomes a + * single row listing every value. A line git kept in the block without being + * able to split it, such as a cherry-pick note, gets a row of its own. + */ +static void print_trailers(const char *msg) +{ + struct trailer_iterator iter; + struct strbuf key = STRBUF_INIT; + int open = 0; + + html("<table class='commit-trailers'>\n"); + trailer_iterator_init(&iter, msg); + while (trailer_iterator_advance(&iter)) { + if (!iter.val.len) { + if (open) + html("</td></tr>\n"); + open = 0; + html("<tr><td colspan='2'>"); + print_filtered_text(iter.key.buf); + html("</td></tr>\n"); + continue; + } + if (open && !strcasecmp(key.buf, iter.key.buf)) + html("<br>"); + else { + if (open) + html("</td></tr>\n"); + strbuf_reset(&key); + strbuf_addbuf(&key, &iter.key); + html("<tr><th>"); + html_txt(key.buf); + html("</th><td>"); + open = 1; + } + print_trailer_value(key.buf, iter.val.buf); + } + if (open) + html("</td></tr>\n"); + html("</table>\n"); + trailer_iterator_release(&iter); + strbuf_release(&key); +} + +/* + * Git finds the trailer block by looking at the whole message, title included, + * so the subject goes back in front of the body before asking, and the offsets + * that come back are shifted by that prefix. The blank line that separated the + * body from its block is dropped along with the block. + */ +static void print_message(struct commitinfo *info) +{ + struct strbuf full = STRBUF_INIT; + struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; + struct trailer_block *block; + LIST_HEAD(items); + size_t prefix, start, end; + char *body = NULL; + + if (ctx.repo->enable_trailers) { + strbuf_addf(&full, "%s\n\n%s", info->subject, info->msg); + prefix = full.len - strlen(info->msg); + opts.no_divider = 1; + block = parse_trailers(&opts, full.buf, &items); + start = trailer_block_start(block); + end = trailer_block_end(block); + free_trailers(&items); + trailer_block_release(block); + if (start >= prefix && start < end) { + size_t len = start - prefix; + + while (len > 0 && info->msg[len - 1] == '\n') + len--; + body = xmemdupz(info->msg, len); + } + } + + html("<div class='commit-msg'>"); + print_filtered_text(body ? body : info->msg); + html("</div>\n"); + if (body) + print_trailers(full.buf); + + free(body); + strbuf_release(&full); +} + void cgit_print_commit(char *hex, const char *prefix) { struct commit *commit; @@ -145,9 +269,7 @@ void cgit_print_commit(char *hex, const char *prefix) print_filtered_text(info->subject); cgit_print_commit_decorations(commit); html("</div>\n"); - html("<div class='commit-msg'>"); - print_filtered_text(info->msg); - html("</div>\n"); + print_message(info); if (notes.len != 0) { html("<div class='notes-header'>Notes</div>\n"); html("<div class='notes'>"); |
