6 #include "string-list.h"
12 static char *user_format;
14 static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
17 user_format = xstrdup(cp);
19 rev->use_terminator = 1;
20 rev->commit_format = CMIT_FMT_USERFORMAT;
23 void get_commit_format(const char *arg, struct rev_info *rev)
26 static struct cmt_fmt_map {
31 { "raw", 1, CMIT_FMT_RAW },
32 { "medium", 1, CMIT_FMT_MEDIUM },
33 { "short", 1, CMIT_FMT_SHORT },
34 { "email", 1, CMIT_FMT_EMAIL },
35 { "full", 5, CMIT_FMT_FULL },
36 { "fuller", 5, CMIT_FMT_FULLER },
37 { "oneline", 1, CMIT_FMT_ONELINE },
40 rev->use_terminator = 0;
42 rev->commit_format = CMIT_FMT_DEFAULT;
45 if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
46 save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
49 for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
50 if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
51 !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
52 if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
53 rev->use_terminator = 1;
54 rev->commit_format = cmt_fmts[i].v;
58 if (strchr(arg, '%')) {
59 save_user_format(rev, arg, 1);
63 die("invalid --pretty format: %s", arg);
67 * Generic support for pretty-printing the header
69 static int get_one_line(const char *msg)
84 /* High bit set, or ISO-2022-INT */
87 return !isascii(ch) || ch == '\033';
90 static int is_rfc2047_special(char ch)
92 return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
95 static void add_rfc2047(struct strbuf *sb, const char *line, int len,
100 for (i = 0; i < len; i++) {
104 if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
107 strbuf_add(sb, line, len);
111 strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
112 strbuf_addf(sb, "=?%s?q?", encoding);
113 for (i = last = 0; i < len; i++) {
114 unsigned ch = line[i] & 0xFF;
116 * We encode ' ' using '=20' even though rfc2047
117 * allows using '_' for readability. Unfortunately,
118 * many programs do not understand this and just
119 * leave the underscore in place.
121 if (is_rfc2047_special(ch) || ch == ' ') {
122 strbuf_add(sb, line + last, i - last);
123 strbuf_addf(sb, "=%02X", ch);
127 strbuf_add(sb, line + last, len - last);
128 strbuf_addstr(sb, "?=");
131 void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
132 const char *line, enum date_mode dmode,
133 const char *encoding)
140 if (fmt == CMIT_FMT_ONELINE)
142 date = strchr(line, '>');
145 namelen = ++date - line;
146 time = strtoul(date, &date, 10);
147 tz = strtol(date, NULL, 10);
149 if (fmt == CMIT_FMT_EMAIL) {
150 char *name_tail = strchr(line, '<');
151 int display_name_length;
154 while (line < name_tail && isspace(name_tail[-1]))
156 display_name_length = name_tail - line;
157 strbuf_addstr(sb, "From: ");
158 add_rfc2047(sb, line, display_name_length, encoding);
159 strbuf_add(sb, name_tail, namelen - display_name_length);
160 strbuf_addch(sb, '\n');
162 strbuf_addf(sb, "%s: %.*s%.*s\n", what,
163 (fmt == CMIT_FMT_FULLER) ? 4 : 0,
167 case CMIT_FMT_MEDIUM:
168 strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode));
171 strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
173 case CMIT_FMT_FULLER:
174 strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
182 static int is_empty_line(const char *line, int *len_p)
185 while (len && isspace(line[len-1]))
191 static const char *skip_empty_lines(const char *msg)
194 int linelen = get_one_line(msg);
198 if (!is_empty_line(msg, &ll))
205 static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
206 const struct commit *commit, int abbrev)
208 struct commit_list *parent = commit->parents;
210 if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
211 !parent || !parent->next)
214 strbuf_addstr(sb, "Merge:");
217 struct commit *p = parent->item;
218 const char *hex = NULL;
220 hex = find_unique_abbrev(p->object.sha1, abbrev);
222 hex = sha1_to_hex(p->object.sha1);
223 parent = parent->next;
225 strbuf_addf(sb, " %s", hex);
227 strbuf_addch(sb, '\n');
230 static char *get_header(const struct commit *commit, const char *key)
232 int key_len = strlen(key);
233 const char *line = commit->buffer;
236 const char *eol = strchr(line, '\n'), *next;
241 eol = line + strlen(line);
245 if (eol - line > key_len &&
246 !strncmp(line, key, key_len) &&
247 line[key_len] == ' ') {
248 return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
254 static char *replace_encoding_header(char *buf, const char *encoding)
256 struct strbuf tmp = STRBUF_INIT;
260 /* guess if there is an encoding header before a \n\n */
261 while (strncmp(cp, "encoding ", strlen("encoding "))) {
262 cp = strchr(cp, '\n');
263 if (!cp || *++cp == '\n')
267 cp = strchr(cp, '\n');
269 return buf; /* should not happen but be defensive */
270 len = cp + 1 - (buf + start);
272 strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
273 if (is_encoding_utf8(encoding)) {
274 /* we have re-coded to UTF-8; drop the header */
275 strbuf_remove(&tmp, start, len);
277 /* just replaces XXXX in 'encoding XXXX\n' */
278 strbuf_splice(&tmp, start + strlen("encoding "),
279 len - strlen("encoding \n"),
280 encoding, strlen(encoding));
282 return strbuf_detach(&tmp, NULL);
285 static char *logmsg_reencode(const struct commit *commit,
286 const char *output_encoding)
288 static const char *utf8 = "UTF-8";
289 const char *use_encoding;
293 if (!*output_encoding)
295 encoding = get_header(commit, "encoding");
296 use_encoding = encoding ? encoding : utf8;
297 if (!strcmp(use_encoding, output_encoding))
298 if (encoding) /* we'll strip encoding header later */
299 out = xstrdup(commit->buffer);
301 return NULL; /* nothing to do */
303 out = reencode_string(commit->buffer,
304 output_encoding, use_encoding);
306 out = replace_encoding_header(out, output_encoding);
312 static int mailmap_name(char *email, int email_len, char *name, int name_len)
314 static struct string_list *mail_map;
316 mail_map = xcalloc(1, sizeof(*mail_map));
317 read_mailmap(mail_map, NULL);
319 return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
322 static size_t format_person_part(struct strbuf *sb, char part,
323 const char *msg, int len, enum date_mode dmode)
325 /* currently all placeholders have same length */
326 const int placeholder_len = 2;
327 int start, end, tz = 0;
328 unsigned long date = 0;
330 const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
331 char person_name[1024];
332 char person_mail[1024];
334 /* advance 'end' to point to email start delimiter */
335 for (end = 0; end < len && msg[end] != '<'; end++)
339 * When end points at the '<' that we found, it should have
340 * matching '>' later, which means 'end' must be strictly
346 /* Seek for both name and email part */
349 while (name_end > name_start && isspace(*(name_end-1)))
351 mail_start = msg+end+1;
352 mail_end = mail_start;
353 while (mail_end < msg_end && *mail_end != '>')
355 if (mail_end == msg_end)
359 if (part == 'N' || part == 'E') { /* mailmap lookup */
360 strlcpy(person_name, name_start, name_end-name_start+1);
361 strlcpy(person_mail, mail_start, mail_end-mail_start+1);
362 mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
363 name_start = person_name;
364 name_end = name_start + strlen(person_name);
365 mail_start = person_mail;
366 mail_end = mail_start + strlen(person_mail);
368 if (part == 'n' || part == 'N') { /* name */
369 strbuf_add(sb, name_start, name_end-name_start);
370 return placeholder_len;
372 if (part == 'e' || part == 'E') { /* email */
373 strbuf_add(sb, mail_start, mail_end-mail_start);
374 return placeholder_len;
377 /* advance 'start' to point to date start delimiter */
378 for (start = end + 1; start < len && isspace(msg[start]); start++)
382 date = strtoul(msg + start, &ep, 10);
383 if (msg + start == ep)
386 if (part == 't') { /* date, UNIX timestamp */
387 strbuf_add(sb, msg + start, ep - (msg + start));
388 return placeholder_len;
392 for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
394 if (start + 1 < len) {
395 tz = strtoul(msg + start + 1, NULL, 10);
396 if (msg[start] == '-')
402 strbuf_addstr(sb, show_date(date, tz, dmode));
403 return placeholder_len;
404 case 'D': /* date, RFC2822 style */
405 strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
406 return placeholder_len;
407 case 'r': /* date, relative */
408 strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
409 return placeholder_len;
410 case 'i': /* date, ISO 8601 */
411 strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
412 return placeholder_len;
417 * bogus commit, 'sb' cannot be updated, but we still need to
418 * compute a valid return value.
420 if (part == 'n' || part == 'e' || part == 't' || part == 'd'
421 || part == 'D' || part == 'r' || part == 'i')
422 return placeholder_len;
424 return 0; /* unknown placeholder */
432 struct format_commit_context {
433 const struct commit *commit;
434 enum date_mode dmode;
435 unsigned commit_header_parsed:1;
436 unsigned commit_message_parsed:1;
438 /* These offsets are relative to the start of the commit message. */
440 struct chunk committer;
441 struct chunk encoding;
446 /* The following ones are relative to the result struct strbuf. */
447 struct chunk abbrev_commit_hash;
448 struct chunk abbrev_tree_hash;
449 struct chunk abbrev_parent_hashes;
452 static int add_again(struct strbuf *sb, struct chunk *chunk)
455 strbuf_adddup(sb, chunk->off, chunk->len);
460 * We haven't seen this chunk before. Our caller is surely
461 * going to add it the hard way now. Remember the most likely
462 * start of the to-be-added chunk: the current end of the
465 chunk->off = sb->len;
469 static void parse_commit_header(struct format_commit_context *context)
471 const char *msg = context->commit->buffer;
474 for (i = 0; msg[i]; i++) {
476 for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
481 } else if (!prefixcmp(msg + i, "author ")) {
482 context->author.off = i + 7;
483 context->author.len = eol - i - 7;
484 } else if (!prefixcmp(msg + i, "committer ")) {
485 context->committer.off = i + 10;
486 context->committer.len = eol - i - 10;
487 } else if (!prefixcmp(msg + i, "encoding ")) {
488 context->encoding.off = i + 9;
489 context->encoding.len = eol - i - 9;
493 context->message_off = i;
494 context->commit_header_parsed = 1;
497 static int istitlechar(char c)
499 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
500 (c >= '0' && c <= '9') || c == '.' || c == '_';
503 static void format_sanitized_subject(struct strbuf *sb, const char *msg)
506 size_t start_len = sb->len;
509 for (; *msg && *msg != '\n'; msg++) {
510 if (istitlechar(*msg)) {
512 strbuf_addch(sb, '-');
514 strbuf_addch(sb, *msg);
516 while (*(msg+1) == '.')
522 /* trim any trailing '.' or '-' characters */
524 while (sb->len - trimlen > start_len &&
525 (sb->buf[sb->len - 1 - trimlen] == '.'
526 || sb->buf[sb->len - 1 - trimlen] == '-'))
528 strbuf_remove(sb, sb->len - trimlen, trimlen);
531 const char *format_subject(struct strbuf *sb, const char *msg,
532 const char *line_separator)
537 const char *line = msg;
538 int linelen = get_one_line(line);
541 if (!linelen || is_empty_line(line, &linelen))
546 strbuf_grow(sb, linelen + 2);
548 strbuf_addstr(sb, line_separator);
549 strbuf_add(sb, line, linelen);
555 static void parse_commit_message(struct format_commit_context *c)
557 const char *msg = c->commit->buffer + c->message_off;
558 const char *start = c->commit->buffer;
560 msg = skip_empty_lines(msg);
561 c->subject_off = msg - start;
563 msg = format_subject(NULL, msg, NULL);
564 msg = skip_empty_lines(msg);
565 c->body_off = msg - start;
567 c->commit_message_parsed = 1;
570 static void format_decoration(struct strbuf *sb, const struct commit *commit)
572 struct name_decoration *d;
573 const char *prefix = " (";
575 load_ref_decorations();
576 d = lookup_decoration(&name_decoration, &commit->object);
578 strbuf_addstr(sb, prefix);
580 strbuf_addstr(sb, d->name);
583 if (prefix[0] == ',')
584 strbuf_addch(sb, ')');
587 static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
590 struct format_commit_context *c = context;
591 const struct commit *commit = c->commit;
592 const char *msg = commit->buffer;
593 struct commit_list *p;
596 /* these are independent of the commit */
597 switch (placeholder[0]) {
599 if (placeholder[1] == '(') {
600 const char *end = strchr(placeholder + 2, ')');
601 char color[COLOR_MAXLEN];
604 color_parse_mem(placeholder + 2,
605 end - (placeholder + 2),
606 "--pretty format", color);
607 strbuf_addstr(sb, color);
608 return end - placeholder + 1;
610 if (!prefixcmp(placeholder + 1, "red")) {
611 strbuf_addstr(sb, GIT_COLOR_RED);
613 } else if (!prefixcmp(placeholder + 1, "green")) {
614 strbuf_addstr(sb, GIT_COLOR_GREEN);
616 } else if (!prefixcmp(placeholder + 1, "blue")) {
617 strbuf_addstr(sb, GIT_COLOR_BLUE);
619 } else if (!prefixcmp(placeholder + 1, "reset")) {
620 strbuf_addstr(sb, GIT_COLOR_RESET);
624 case 'n': /* newline */
625 strbuf_addch(sb, '\n');
628 /* %x00 == NUL, %x0a == LF, etc. */
629 if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) &&
631 0 <= (h2 = hexval_table[0xff & placeholder[2]]) &&
633 strbuf_addch(sb, (h1<<4)|h2);
639 /* these depend on the commit */
640 if (!commit->object.parsed)
641 parse_object(commit->object.sha1);
643 switch (placeholder[0]) {
644 case 'H': /* commit hash */
645 strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
647 case 'h': /* abbreviated commit hash */
648 if (add_again(sb, &c->abbrev_commit_hash))
650 strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
652 c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
654 case 'T': /* tree hash */
655 strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
657 case 't': /* abbreviated tree hash */
658 if (add_again(sb, &c->abbrev_tree_hash))
660 strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
662 c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
664 case 'P': /* parent hashes */
665 for (p = commit->parents; p; p = p->next) {
666 if (p != commit->parents)
667 strbuf_addch(sb, ' ');
668 strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
671 case 'p': /* abbreviated parent hashes */
672 if (add_again(sb, &c->abbrev_parent_hashes))
674 for (p = commit->parents; p; p = p->next) {
675 if (p != commit->parents)
676 strbuf_addch(sb, ' ');
677 strbuf_addstr(sb, find_unique_abbrev(
678 p->item->object.sha1, DEFAULT_ABBREV));
680 c->abbrev_parent_hashes.len = sb->len -
681 c->abbrev_parent_hashes.off;
683 case 'm': /* left/right/bottom */
684 strbuf_addch(sb, (commit->object.flags & BOUNDARY)
686 : (commit->object.flags & SYMMETRIC_LEFT)
691 format_decoration(sb, commit);
694 get_commit_notes(commit, sb, git_log_output_encoding ?
695 git_log_output_encoding : git_commit_encoding, 0);
699 /* For the rest we have to parse the commit header. */
700 if (!c->commit_header_parsed)
701 parse_commit_header(c);
703 switch (placeholder[0]) {
704 case 'a': /* author ... */
705 return format_person_part(sb, placeholder[1],
706 msg + c->author.off, c->author.len,
708 case 'c': /* committer ... */
709 return format_person_part(sb, placeholder[1],
710 msg + c->committer.off, c->committer.len,
712 case 'e': /* encoding */
713 strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
717 /* Now we need to parse the commit message. */
718 if (!c->commit_message_parsed)
719 parse_commit_message(c);
721 switch (placeholder[0]) {
722 case 's': /* subject */
723 format_subject(sb, msg + c->subject_off, " ");
725 case 'f': /* sanitized subject */
726 format_sanitized_subject(sb, msg + c->subject_off);
729 strbuf_addstr(sb, msg + c->body_off);
732 return 0; /* unknown placeholder */
735 void format_commit_message(const struct commit *commit,
736 const void *format, struct strbuf *sb,
737 enum date_mode dmode)
739 struct format_commit_context context;
741 memset(&context, 0, sizeof(context));
742 context.commit = commit;
743 context.dmode = dmode;
744 strbuf_expand(sb, format, format_commit_item, &context);
747 static void pp_header(enum cmit_fmt fmt,
749 enum date_mode dmode,
750 const char *encoding,
751 const struct commit *commit,
755 int parents_shown = 0;
758 const char *line = *msg_p;
759 int linelen = get_one_line(*msg_p);
769 if (fmt == CMIT_FMT_RAW) {
770 strbuf_add(sb, line, linelen);
774 if (!memcmp(line, "parent ", 7)) {
776 die("bad parent line in commit");
780 if (!parents_shown) {
781 struct commit_list *parent;
783 for (parent = commit->parents, num = 0;
785 parent = parent->next, num++)
787 /* with enough slop */
788 strbuf_grow(sb, num * 50 + 20);
789 add_merge_info(fmt, sb, commit, abbrev);
794 * MEDIUM == DEFAULT shows only author with dates.
795 * FULL shows both authors but not dates.
796 * FULLER shows both authors and dates.
798 if (!memcmp(line, "author ", 7)) {
799 strbuf_grow(sb, linelen + 80);
800 pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
802 if (!memcmp(line, "committer ", 10) &&
803 (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
804 strbuf_grow(sb, linelen + 80);
805 pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
810 void pp_title_line(enum cmit_fmt fmt,
814 const char *after_subject,
815 const char *encoding,
818 const char *line_separator = (fmt == CMIT_FMT_EMAIL) ? "\n " : " ";
821 strbuf_init(&title, 80);
822 *msg_p = format_subject(&title, *msg_p, line_separator);
824 strbuf_grow(sb, title.len + 1024);
826 strbuf_addstr(sb, subject);
827 add_rfc2047(sb, title.buf, title.len, encoding);
829 strbuf_addbuf(sb, &title);
831 strbuf_addch(sb, '\n');
833 if (need_8bit_cte > 0) {
834 const char *header_fmt =
835 "MIME-Version: 1.0\n"
836 "Content-Type: text/plain; charset=%s\n"
837 "Content-Transfer-Encoding: 8bit\n";
838 strbuf_addf(sb, header_fmt, encoding);
841 strbuf_addstr(sb, after_subject);
843 if (fmt == CMIT_FMT_EMAIL) {
844 strbuf_addch(sb, '\n');
846 strbuf_release(&title);
849 void pp_remainder(enum cmit_fmt fmt,
856 const char *line = *msg_p;
857 int linelen = get_one_line(line);
863 if (is_empty_line(line, &linelen)) {
866 if (fmt == CMIT_FMT_SHORT)
871 strbuf_grow(sb, linelen + indent + 20);
873 memset(sb->buf + sb->len, ' ', indent);
874 strbuf_setlen(sb, sb->len + indent);
876 strbuf_add(sb, line, linelen);
877 strbuf_addch(sb, '\n');
881 char *reencode_commit_message(const struct commit *commit, const char **encoding_p)
883 const char *encoding;
885 encoding = (git_log_output_encoding
886 ? git_log_output_encoding
887 : git_commit_encoding);
891 *encoding_p = encoding;
892 return logmsg_reencode(commit, encoding);
895 void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
896 struct strbuf *sb, int abbrev,
897 const char *subject, const char *after_subject,
898 enum date_mode dmode, int need_8bit_cte)
900 unsigned long beginning_of_body;
902 const char *msg = commit->buffer;
904 const char *encoding;
906 if (fmt == CMIT_FMT_USERFORMAT) {
907 format_commit_message(commit, user_format, sb, dmode);
911 reencoded = reencode_commit_message(commit, &encoding);
916 if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
920 * We need to check and emit Content-type: to mark it
921 * as 8-bit if we haven't done so.
923 if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
926 for (in_body = i = 0; (ch = msg[i]); i++) {
928 /* author could be non 7-bit ASCII but
929 * the log may be so; skip over the
932 if (ch == '\n' && msg[i+1] == '\n')
935 else if (non_ascii(ch)) {
942 pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
943 if (fmt != CMIT_FMT_ONELINE && !subject) {
944 strbuf_addch(sb, '\n');
947 /* Skip excess blank lines at the beginning of body, if any... */
948 msg = skip_empty_lines(msg);
950 /* These formats treat the title line specially. */
951 if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
952 pp_title_line(fmt, &msg, sb, subject,
953 after_subject, encoding, need_8bit_cte);
955 beginning_of_body = sb->len;
956 if (fmt != CMIT_FMT_ONELINE)
957 pp_remainder(fmt, &msg, sb, indent);
960 /* Make sure there is an EOLN for the non-oneline case */
961 if (fmt != CMIT_FMT_ONELINE)
962 strbuf_addch(sb, '\n');
965 * The caller may append additional body text in e-mail
966 * format. Make sure we did not strip the blank line
967 * between the header and the body.
969 if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
970 strbuf_addch(sb, '\n');
972 if (fmt != CMIT_FMT_ONELINE)
973 get_commit_notes(commit, sb, encoding,
974 NOTES_SHOW_HEADER | NOTES_INDENT);