diff options
Diffstat (limited to 'pretty.c')
-rw-r--r-- | pretty.c | 257 |
1 files changed, 186 insertions, 71 deletions
@@ -10,12 +10,14 @@ #include "color.h" #include "reflog-walk.h" #include "gpg-interface.h" +#include "trailer.h" static char *user_format; static struct cmt_fmt_map { const char *name; enum cmit_fmt format; int is_tformat; + int expand_tabs_in_log; int is_alias; const char *user_format; } *commit_formats; @@ -87,13 +89,14 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c static void setup_commit_formats(void) { struct cmt_fmt_map builtin_formats[] = { - { "raw", CMIT_FMT_RAW, 0 }, - { "medium", CMIT_FMT_MEDIUM, 0 }, - { "short", CMIT_FMT_SHORT, 0 }, - { "email", CMIT_FMT_EMAIL, 0 }, - { "fuller", CMIT_FMT_FULLER, 0 }, - { "full", CMIT_FMT_FULL, 0 }, - { "oneline", CMIT_FMT_ONELINE, 1 } + { "raw", CMIT_FMT_RAW, 0, 0 }, + { "medium", CMIT_FMT_MEDIUM, 0, 8 }, + { "short", CMIT_FMT_SHORT, 0, 0 }, + { "email", CMIT_FMT_EMAIL, 0, 0 }, + { "mboxrd", CMIT_FMT_MBOXRD, 0, 0 }, + { "fuller", CMIT_FMT_FULLER, 0, 8 }, + { "full", CMIT_FMT_FULL, 0, 8 }, + { "oneline", CMIT_FMT_ONELINE, 1, 0 } }; commit_formats_len = ARRAY_SIZE(builtin_formats); builtin_formats_len = commit_formats_len; @@ -172,6 +175,7 @@ void get_commit_format(const char *arg, struct rev_info *rev) rev->commit_format = commit_format->format; rev->use_terminator = commit_format->is_tformat; + rev->expand_tabs_in_log_default = commit_format->expand_tabs_in_log; if (commit_format->format == CMIT_FMT_USERFORMAT) { save_user_format(rev, commit_format->user_format, commit_format->is_tformat); @@ -399,13 +403,13 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len, } const char *show_ident_date(const struct ident_split *ident, - enum date_mode mode) + const struct date_mode *mode) { - unsigned long date = 0; + timestamp_t date = 0; long tz = 0; if (ident->date_begin && ident->date_end) - date = strtoul(ident->date_begin, NULL, 10); + date = parse_timestamp(ident->date_begin, NULL, 10); if (date_overflows(date)) date = 0; else { @@ -442,7 +446,7 @@ void pp_user_info(struct pretty_print_context *pp, if (pp->mailmap) map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen); - if (pp->fmt == CMIT_FMT_EMAIL) { + if (cmit_fmt_is_mail(pp->fmt)) { if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) { struct strbuf buf = STRBUF_INIT; @@ -489,15 +493,16 @@ void pp_user_info(struct pretty_print_context *pp, switch (pp->fmt) { case CMIT_FMT_MEDIUM: strbuf_addf(sb, "Date: %s\n", - show_ident_date(&ident, pp->date_mode)); + show_ident_date(&ident, &pp->date_mode)); break; case CMIT_FMT_EMAIL: + case CMIT_FMT_MBOXRD: strbuf_addf(sb, "Date: %s\n", - show_ident_date(&ident, DATE_RFC2822)); + show_ident_date(&ident, DATE_MODE(RFC2822))); break; case CMIT_FMT_FULLER: strbuf_addf(sb, "%sDate: %s\n", what, - show_ident_date(&ident, pp->date_mode)); + show_ident_date(&ident, &pp->date_mode)); break; default: /* notin' */ @@ -505,7 +510,7 @@ void pp_user_info(struct pretty_print_context *pp, } } -static int is_empty_line(const char *line, int *len_p) +static int is_blank_line(const char *line, int *len_p) { int len = *len_p; while (len && isspace(line[len - 1])) @@ -514,14 +519,14 @@ static int is_empty_line(const char *line, int *len_p) return !len; } -static const char *skip_empty_lines(const char *msg) +const char *skip_blank_lines(const char *msg) { for (;;) { int linelen = get_one_line(msg); int ll = linelen; if (!linelen) break; - if (!is_empty_line(msg, &ll)) + if (!is_blank_line(msg, &ll)) break; msg += linelen; } @@ -533,22 +538,20 @@ static void add_merge_info(const struct pretty_print_context *pp, { struct commit_list *parent = commit->parents; - if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) || + if ((pp->fmt == CMIT_FMT_ONELINE) || (cmit_fmt_is_mail(pp->fmt)) || !parent || !parent->next) return; strbuf_addstr(sb, "Merge:"); while (parent) { - struct commit *p = parent->item; - const char *hex = NULL; + struct object_id *oidp = &parent->item->object.oid; + strbuf_addch(sb, ' '); if (pp->abbrev) - hex = find_unique_abbrev(p->object.sha1, pp->abbrev); - if (!hex) - hex = sha1_to_hex(p->object.sha1); + strbuf_add_unique_abbrev(sb, oidp->hash, pp->abbrev); + else + strbuf_addstr(sb, oid_to_hex(oidp)); parent = parent->next; - - strbuf_addf(sb, " %s", hex); } strbuf_addch(sb, '\n'); } @@ -671,7 +674,8 @@ static int mailmap_name(const char **email, size_t *email_len, } static size_t format_person_part(struct strbuf *sb, char part, - const char *msg, int len, enum date_mode dmode) + const char *msg, int len, + const struct date_mode *dmode) { /* currently all placeholders have same length */ const int placeholder_len = 2; @@ -711,16 +715,16 @@ static size_t format_person_part(struct strbuf *sb, char part, strbuf_addstr(sb, show_ident_date(&s, dmode)); return placeholder_len; case 'D': /* date, RFC2822 style */ - strbuf_addstr(sb, show_ident_date(&s, DATE_RFC2822)); + strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(RFC2822))); return placeholder_len; case 'r': /* date, relative */ - strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE)); + strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(RELATIVE))); return placeholder_len; case 'i': /* date, ISO 8601-like */ - strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601)); + strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601))); return placeholder_len; case 'I': /* date, ISO 8601 strict */ - strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601_STRICT)); + strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601_STRICT))); return placeholder_len; } @@ -872,7 +876,7 @@ const char *format_subject(struct strbuf *sb, const char *msg, int linelen = get_one_line(line); msg += linelen; - if (!linelen || is_empty_line(line, &linelen)) + if (!linelen || is_blank_line(line, &linelen)) break; if (!sb) @@ -886,16 +890,26 @@ const char *format_subject(struct strbuf *sb, const char *msg, return msg; } +static void format_trailers(struct strbuf *sb, const char *msg) +{ + struct trailer_info info; + + trailer_info_get(&info, msg); + strbuf_add(sb, info.trailer_start, + info.trailer_end - info.trailer_start); + trailer_info_release(&info); +} + static void parse_commit_message(struct format_commit_context *c) { const char *msg = c->message + c->message_off; const char *start = c->message; - msg = skip_empty_lines(msg); + msg = skip_blank_lines(msg); c->subject_off = msg - start; msg = format_subject(NULL, msg, NULL); - msg = skip_empty_lines(msg); + msg = skip_blank_lines(msg); c->body_off = msg - start; c->commit_message_parsed = 1; @@ -933,7 +947,7 @@ static void rewrap_message_tail(struct strbuf *sb, static int format_reflog_person(struct strbuf *sb, char part, struct reflog_walk_info *log, - enum date_mode dmode) + const struct date_mode *dmode) { const char *ident; @@ -1019,9 +1033,15 @@ static size_t parse_padding_placeholder(struct strbuf *sb, int width; if (!end || end == start) return 0; - width = strtoul(start, &next, 10); + width = strtol(start, &next, 10); if (next == start || width == 0) return 0; + if (width < 0) { + if (to_column) + width += term_columns(); + if (width < 0) + return 0; + } c->padding = to_column ? -width : width; c->flush_type = flush_type; @@ -1054,13 +1074,15 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ const struct commit *commit = c->commit; const char *msg = c->message; struct commit_list *p; - int h1, h2; + int ch; /* these are independent of the commit */ switch (placeholder[0]) { case 'C': if (starts_with(placeholder + 1, "(auto)")) { - c->auto_color = 1; + c->auto_color = want_color(c->pretty_ctx->color); + if (c->auto_color && sb->len) + strbuf_addstr(sb, GIT_COLOR_RESET); return 7; /* consumed 7 bytes, "C(auto)" */ } else { int ret = parse_color(sb, placeholder, c); @@ -1078,14 +1100,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ return 1; case 'x': /* %x00 == NUL, %x0a == LF, etc. */ - if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) && - h1 <= 16 && - 0 <= (h2 = hexval_table[0xff & placeholder[2]]) && - h2 <= 16) { - strbuf_addch(sb, (h1<<4)|h2); - return 3; - } else + ch = hex2chr(placeholder + 1); + if (ch < 0) return 0; + strbuf_addch(sb, ch); + return 3; case 'w': if (placeholder[1] == '(') { unsigned long width = 0, indent1 = 0, indent2 = 0; @@ -1118,12 +1137,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ /* these depend on the commit */ if (!commit->object.parsed) - parse_object(commit->object.sha1); + parse_object(&commit->object.oid); switch (placeholder[0]) { case 'H': /* commit hash */ strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT)); - strbuf_addstr(sb, sha1_to_hex(commit->object.sha1)); + strbuf_addstr(sb, oid_to_hex(&commit->object.oid)); strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET)); return 1; case 'h': /* abbreviated commit hash */ @@ -1132,26 +1151,26 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET)); return 1; } - strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, - c->pretty_ctx->abbrev)); + strbuf_add_unique_abbrev(sb, commit->object.oid.hash, + c->pretty_ctx->abbrev); strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET)); c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off; return 1; case 'T': /* tree hash */ - strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1)); + strbuf_addstr(sb, oid_to_hex(&commit->tree->object.oid)); return 1; case 't': /* abbreviated tree hash */ if (add_again(sb, &c->abbrev_tree_hash)) return 1; - strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1, - c->pretty_ctx->abbrev)); + strbuf_add_unique_abbrev(sb, commit->tree->object.oid.hash, + c->pretty_ctx->abbrev); c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off; return 1; case 'P': /* parent hashes */ for (p = commit->parents; p; p = p->next) { if (p != commit->parents) strbuf_addch(sb, ' '); - strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1)); + strbuf_addstr(sb, oid_to_hex(&p->item->object.oid)); } return 1; case 'p': /* abbreviated parent hashes */ @@ -1160,9 +1179,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ for (p = commit->parents; p; p = p->next) { if (p != commit->parents) strbuf_addch(sb, ' '); - strbuf_addstr(sb, find_unique_abbrev( - p->item->object.sha1, - c->pretty_ctx->abbrev)); + strbuf_add_unique_abbrev(sb, p->item->object.oid.hash, + c->pretty_ctx->abbrev); } c->abbrev_parent_hashes.len = sb->len - c->abbrev_parent_hashes.off; @@ -1185,7 +1203,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ if (c->pretty_ctx->reflog_info) get_reflog_selector(sb, c->pretty_ctx->reflog_info, - c->pretty_ctx->date_mode, + &c->pretty_ctx->date_mode, c->pretty_ctx->date_mode_explicit, (placeholder[1] == 'd')); return 2; @@ -1200,7 +1218,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ return format_reflog_person(sb, placeholder[1], c->pretty_ctx->reflog_info, - c->pretty_ctx->date_mode); + &c->pretty_ctx->date_mode); } return 0; /* unknown %g placeholder */ case 'N': @@ -1223,8 +1241,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ switch (c->signature_check.result) { case 'G': case 'B': + case 'E': case 'U': case 'N': + case 'X': + case 'Y': + case 'R': strbuf_addch(sb, c->signature_check.result); } break; @@ -1251,11 +1273,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ case 'a': /* author ... */ return format_person_part(sb, placeholder[1], msg + c->author.off, c->author.len, - c->pretty_ctx->date_mode); + &c->pretty_ctx->date_mode); case 'c': /* committer ... */ return format_person_part(sb, placeholder[1], msg + c->committer.off, c->committer.len, - c->pretty_ctx->date_mode); + &c->pretty_ctx->date_mode); case 'e': /* encoding */ if (c->commit_encoding) strbuf_addstr(sb, c->commit_encoding); @@ -1281,6 +1303,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ strbuf_addstr(sb, msg + c->body_off); return 1; } + + if (starts_with(placeholder, "(trailers)")) { + format_trailers(sb, msg + c->subject_off); + return strlen("(trailers)"); + } + return 0; /* unknown placeholder */ } @@ -1296,6 +1324,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */ if (!start) start = sb->buf; occupied = utf8_strnwidth(start, -1, 1); + occupied += c->pretty_ctx->graph_width; padding = (-padding) - occupied; } while (1) { @@ -1578,8 +1607,9 @@ void pp_title_line(struct pretty_print_context *pp, pp->preserve_subject ? "\n" : " "); strbuf_grow(sb, title.len + 1024); - if (pp->subject) { - strbuf_addstr(sb, pp->subject); + if (pp->print_email_subject) { + if (pp->rev) + fmt_output_email_subject(sb, pp->rev); if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT)) add_rfc2047(sb, title.buf, title.len, encoding, RFC2047_SUBJECT); @@ -1611,7 +1641,7 @@ void pp_title_line(struct pretty_print_context *pp, if (pp->after_subject) { strbuf_addstr(sb, pp->after_subject); } - if (pp->fmt == CMIT_FMT_EMAIL) { + if (cmit_fmt_is_mail(pp->fmt)) { strbuf_addch(sb, '\n'); } @@ -1628,6 +1658,82 @@ void pp_title_line(struct pretty_print_context *pp, strbuf_release(&title); } +static int pp_utf8_width(const char *start, const char *end) +{ + int width = 0; + size_t remain = end - start; + + while (remain) { + int n = utf8_width(&start, &remain); + if (n < 0 || !start) + return -1; + width += n; + } + return width; +} + +static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth, + const char *line, int linelen) +{ + const char *tab; + + while ((tab = memchr(line, '\t', linelen)) != NULL) { + int width = pp_utf8_width(line, tab); + + /* + * If it wasn't well-formed utf8, or it + * had characters with badly defined + * width (control characters etc), just + * give up on trying to align things. + */ + if (width < 0) + break; + + /* Output the data .. */ + strbuf_add(sb, line, tab - line); + + /* .. and the de-tabified tab */ + strbuf_addchars(sb, ' ', tabwidth - (width % tabwidth)); + + /* Skip over the printed part .. */ + linelen -= tab + 1 - line; + line = tab + 1; + } + + /* + * Print out everything after the last tab without + * worrying about width - there's nothing more to + * align. + */ + strbuf_add(sb, line, linelen); +} + +/* + * pp_handle_indent() prints out the intendation, and + * the whole line (without the final newline), after + * de-tabifying. + */ +static void pp_handle_indent(struct pretty_print_context *pp, + struct strbuf *sb, int indent, + const char *line, int linelen) +{ + strbuf_addchars(sb, ' ', indent); + if (pp->expand_tabs_in_log) + strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, line, linelen); + else + strbuf_add(sb, line, linelen); +} + +static int is_mboxrd_from(const char *line, int len) +{ + /* + * a line matching /^From $/ here would only have len == 4 + * at this point because is_empty_line would've trimmed all + * trailing space + */ + return len > 4 && starts_with(line + strspn(line, ">"), "From "); +} + void pp_remainder(struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, @@ -1642,7 +1748,7 @@ void pp_remainder(struct pretty_print_context *pp, if (!linelen) break; - if (is_empty_line(line, &linelen)) { + if (is_blank_line(line, &linelen)) { if (first) continue; if (pp->fmt == CMIT_FMT_SHORT) @@ -1652,8 +1758,17 @@ void pp_remainder(struct pretty_print_context *pp, strbuf_grow(sb, linelen + indent + 20); if (indent) - strbuf_addchars(sb, ' ', indent); - strbuf_add(sb, line, linelen); + pp_handle_indent(pp, sb, indent, line, linelen); + else if (pp->expand_tabs_in_log) + strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, + line, linelen); + else { + if (pp->fmt == CMIT_FMT_MBOXRD && + is_mboxrd_from(line, linelen)) + strbuf_addch(sb, '>'); + + strbuf_add(sb, line, linelen); + } strbuf_addch(sb, '\n'); } } @@ -1677,14 +1792,14 @@ void pretty_print_commit(struct pretty_print_context *pp, encoding = get_log_output_encoding(); msg = reencoded = logmsg_reencode(commit, NULL, encoding); - if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL) + if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt)) indent = 0; /* * We need to check and emit Content-type: to mark it * as 8-bit if we haven't done so. */ - if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) { + if (cmit_fmt_is_mail(pp->fmt) && need_8bit_cte == 0) { int i, ch, in_body; for (in_body = i = 0; (ch = msg[i]); i++) { @@ -1704,15 +1819,15 @@ void pretty_print_commit(struct pretty_print_context *pp, } pp_header(pp, encoding, commit, &msg, sb); - if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) { + if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) { strbuf_addch(sb, '\n'); } /* Skip excess blank lines at the beginning of body, if any... */ - msg = skip_empty_lines(msg); + msg = skip_blank_lines(msg); /* These formats treat the title line specially. */ - if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL) + if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt)) pp_title_line(pp, &msg, sb, encoding, need_8bit_cte); beginning_of_body = sb->len; @@ -1729,7 +1844,7 @@ void pretty_print_commit(struct pretty_print_context *pp, * format. Make sure we did not strip the blank line * between the header and the body. */ - if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body) strbuf_addch(sb, '\n'); unuse_commit_buffer(commit, reencoded); |