static void add_merge_info(const struct pretty_print_context *pp, struct strbuf *sb, const struct commit *commit) { struct commit_list *parent = commit->parents; if ((pp->fmt == CMIT_FMT_ONELINE) || (cmit_fmt_is_mail(pp->fmt)) || !parent || !parent->next) return; strbuf_addstr(sb, "Merge:"); while (parent) { struct object_id *oidp = &parent->item->object.oid; strbuf_addch(sb, ' '); if (pp->abbrev) strbuf_add_unique_abbrev(sb, oidp->hash, pp->abbrev); else strbuf_addstr(sb, oid_to_hex(oidp)); parent = parent->next; } strbuf_addch(sb, '\n'); }
static void make_cover_letter(struct rev_info *rev, int use_stdout, struct commit *origin, int nr, struct commit **list, const char *branch_name, int quiet) { const char *committer; const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n"; const char *msg; struct shortlog log; struct strbuf sb = STRBUF_INIT; int i; const char *encoding = "UTF-8"; struct diff_options opts; int need_8bit_cte = 0; struct pretty_print_context pp = {0}; struct commit *head = list[0]; if (!cmit_fmt_is_mail(rev->commit_format)) die(_("Cover letter needs email format")); committer = git_committer_info(0); if (!use_stdout && reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet)) return; log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, &need_8bit_cte); for (i = 0; !need_8bit_cte && i < nr; i++) { const char *buf = get_commit_buffer(list[i], NULL); if (has_non_ascii(buf)) need_8bit_cte = 1; unuse_commit_buffer(list[i], buf); } if (!branch_name) branch_name = find_branch_name(rev); msg = body; pp.fmt = CMIT_FMT_EMAIL; pp.date_mode.type = DATE_RFC2822; pp_user_info(&pp, NULL, &sb, committer, encoding); pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte); pp_remainder(&pp, &msg, &sb, 0); add_branch_description(&sb, branch_name); printf("%s\n", sb.buf); strbuf_release(&sb); shortlog_init(&log); log.wrap_lines = 1; log.wrap = 72; log.in1 = 2; log.in2 = 4; for (i = 0; i < nr; i++) shortlog_add_commit(&log, list[i]); shortlog_output(&log); /* * We can only do diffstat with a unique reference point */ if (!origin) return; memcpy(&opts, &rev->diffopt, sizeof(opts)); opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; diff_setup_done(&opts); diff_tree_sha1(origin->tree->object.oid.hash, head->tree->object.oid.hash, "", &opts); diffcore_std(&opts); diff_flush(&opts); printf("\n"); print_signature(); }
void show_log(struct rev_info *opt) { struct strbuf msgbuf = STRBUF_INIT; struct log_info *log = opt->loginfo; struct commit *commit = log->commit, *parent = log->parent; int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra_headers = opt->extra_headers; struct pretty_print_context ctx = {0}; opt->loginfo = NULL; if (!opt->verbose_header) { graph_show_commit(opt->graph); if (!opt->graph) put_revision_mark(opt, commit); fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), opt->diffopt.file); if (opt->print_parents) show_parents(commit, abbrev_commit, opt->diffopt.file); if (opt->children.name) show_children(opt, commit, abbrev_commit); show_decorations(opt, commit); if (opt->graph && !graph_is_commit_finished(opt->graph)) { putc('\n', opt->diffopt.file); graph_show_remainder(opt->graph); } putc(opt->diffopt.line_termination, opt->diffopt.file); return; } /* * If use_terminator is set, we already handled any record termination * at the end of the last record. * Otherwise, add a diffopt.line_termination character before all * entries but the first. (IOW, as a separator between entries) */ if (opt->shown_one && !opt->use_terminator) { /* * If entries are separated by a newline, the output * should look human-readable. If the last entry ended * with a newline, print the graph output before this * newline. Otherwise it will end up as a completely blank * line and will look like a gap in the graph. * * If the entry separator is not a newline, the output is * primarily intended for programmatic consumption, and we * never want the extra graph output before the entry * separator. */ if (opt->diffopt.line_termination == '\n' && !opt->missing_newline) graph_show_padding(opt->graph); putc(opt->diffopt.line_termination, opt->diffopt.file); } opt->shown_one = 1; /* * If the history graph was requested, * print the graph, up to this commit's line */ graph_show_commit(opt->graph); /* * Print header line of header.. */ if (cmit_fmt_is_mail(opt->commit_format)) { log_write_email_headers(opt, commit, &ctx.subject, &extra_headers, &ctx.need_8bit_cte); } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file); if (opt->commit_format != CMIT_FMT_ONELINE) fputs("commit ", opt->diffopt.file); if (!opt->graph) put_revision_mark(opt, commit); fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), opt->diffopt.file); if (opt->print_parents) show_parents(commit, abbrev_commit, opt->diffopt.file); if (opt->children.name) show_children(opt, commit, abbrev_commit); if (parent) fprintf(opt->diffopt.file, " (from %s)", find_unique_abbrev(parent->object.oid.hash, abbrev_commit)); fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file); show_decorations(opt, commit); if (opt->commit_format == CMIT_FMT_ONELINE) { putc(' ', opt->diffopt.file); } else { putc('\n', opt->diffopt.file); graph_show_oneline(opt->graph); } if (opt->reflog_info) { /* * setup_revisions() ensures that opt->reflog_info * and opt->graph cannot both be set, * so we don't need to worry about printing the * graph info here. */ show_reflog_message(opt->reflog_info, opt->commit_format == CMIT_FMT_ONELINE, &opt->date_mode, opt->date_mode_explicit); if (opt->commit_format == CMIT_FMT_ONELINE) return; } } if (opt->show_signature) { show_signature(opt, commit); show_mergetag(opt, commit); } if (!get_cached_commit_buffer(commit, NULL)) return; if (opt->show_notes) { int raw; struct strbuf notebuf = STRBUF_INIT; raw = (opt->commit_format == CMIT_FMT_USERFORMAT); format_display_notes(commit->object.oid.hash, ¬ebuf, get_log_output_encoding(), raw); ctx.notes_message = notebuf.len ? strbuf_detach(¬ebuf, NULL) : xcalloc(1, 1); } /* * And then the pretty-printed message itself */ if (ctx.need_8bit_cte >= 0 && opt->add_signoff) ctx.need_8bit_cte = has_non_ascii(fmt_name(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"))); ctx.date_mode = opt->date_mode; ctx.date_mode_explicit = opt->date_mode_explicit; ctx.abbrev = opt->diffopt.abbrev; ctx.after_subject = extra_headers; ctx.preserve_subject = opt->preserve_subject; ctx.reflog_info = opt->reflog_info; ctx.fmt = opt->commit_format; ctx.mailmap = opt->mailmap; ctx.color = opt->diffopt.use_color; ctx.expand_tabs_in_log = opt->expand_tabs_in_log; ctx.output_encoding = get_log_output_encoding(); if (opt->from_ident.mail_begin && opt->from_ident.name_begin) ctx.from_ident = &opt->from_ident; if (opt->graph) ctx.graph_width = graph_width(opt->graph); pretty_print_commit(&ctx, commit, &msgbuf); if (opt->add_signoff) append_signoff(&msgbuf, 0, APPEND_SIGNOFF_DEDUP); if ((ctx.fmt != CMIT_FMT_USERFORMAT) && ctx.notes_message && *ctx.notes_message) { if (cmit_fmt_is_mail(ctx.fmt)) { strbuf_addstr(&msgbuf, "---\n"); opt->shown_dashes = 1; } strbuf_addstr(&msgbuf, ctx.notes_message); } if (opt->show_log_size) { fprintf(opt->diffopt.file, "log size %i\n", (int)msgbuf.len); graph_show_oneline(opt->graph); } /* * Set opt->missing_newline if msgbuf doesn't * end in a newline (including if it is empty) */ if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n') opt->missing_newline = 1; else opt->missing_newline = 0; graph_show_commit_msg(opt->graph, opt->diffopt.file, &msgbuf); if (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) { if (!opt->missing_newline) graph_show_padding(opt->graph); putc(opt->diffopt.line_termination, opt->diffopt.file); } strbuf_release(&msgbuf); free(ctx.notes_message); }
void pp_user_info(struct pretty_print_context *pp, const char *what, struct strbuf *sb, const char *line, const char *encoding) { struct ident_split ident; char *line_end; const char *mailbuf, *namebuf; size_t namelen, maillen; int max_length = 78; /* per rfc2822 */ if (pp->fmt == CMIT_FMT_ONELINE) return; line_end = strchrnul(line, '\n'); if (split_ident_line(&ident, line, line_end - line)) return; mailbuf = ident.mail_begin; maillen = ident.mail_end - ident.mail_begin; namebuf = ident.name_begin; namelen = ident.name_end - ident.name_begin; if (pp->mailmap) map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen); if (cmit_fmt_is_mail(pp->fmt)) { if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) { struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, "From: "); strbuf_add(&buf, namebuf, namelen); strbuf_addstr(&buf, " <"); strbuf_add(&buf, mailbuf, maillen); strbuf_addstr(&buf, ">\n"); string_list_append(&pp->in_body_headers, strbuf_detach(&buf, NULL)); mailbuf = pp->from_ident->mail_begin; maillen = pp->from_ident->mail_end - mailbuf; namebuf = pp->from_ident->name_begin; namelen = pp->from_ident->name_end - namebuf; } strbuf_addstr(sb, "From: "); if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) { add_rfc2047(sb, namebuf, namelen, encoding, RFC2047_ADDRESS); max_length = 76; /* per rfc2047 */ } else if (needs_rfc822_quoting(namebuf, namelen)) { struct strbuf quoted = STRBUF_INIT; add_rfc822_quoted("ed, namebuf, namelen); strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len, -6, 1, max_length); strbuf_release("ed); } else { strbuf_add_wrapped_bytes(sb, namebuf, namelen, -6, 1, max_length); } if (max_length < last_line_length(sb) + strlen(" <") + maillen + strlen(">")) strbuf_addch(sb, '\n'); strbuf_addf(sb, " <%.*s>\n", (int)maillen, mailbuf); } else { strbuf_addf(sb, "%s: %.*s%.*s <%.*s>\n", what, (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0, " ", (int)namelen, namebuf, (int)maillen, mailbuf); } switch (pp->fmt) { case CMIT_FMT_MEDIUM: strbuf_addf(sb, "Date: %s\n", 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_MODE(RFC2822))); break; case CMIT_FMT_FULLER: strbuf_addf(sb, "%sDate: %s\n", what, show_ident_date(&ident, &pp->date_mode)); break; default: /* notin' */ break; } }
void pretty_print_commit(struct pretty_print_context *pp, const struct commit *commit, struct strbuf *sb) { unsigned long beginning_of_body; int indent = 4; const char *msg; const char *reencoded; const char *encoding; int need_8bit_cte = pp->need_8bit_cte; if (pp->fmt == CMIT_FMT_USERFORMAT) { format_commit_message(commit, user_format, sb, pp); return; } encoding = get_log_output_encoding(); msg = reencoded = logmsg_reencode(commit, NULL, encoding); 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 (cmit_fmt_is_mail(pp->fmt) && need_8bit_cte == 0) { int i, ch, in_body; for (in_body = i = 0; (ch = msg[i]); i++) { if (!in_body) { /* author could be non 7-bit ASCII but * the log may be so; skip over the * header part first. */ if (ch == '\n' && msg[i+1] == '\n') in_body = 1; } else if (non_ascii(ch)) { need_8bit_cte = 1; break; } } } pp_header(pp, encoding, commit, &msg, sb); 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_blank_lines(msg); /* These formats treat the title line specially. */ 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; if (pp->fmt != CMIT_FMT_ONELINE) pp_remainder(pp, &msg, sb, indent); strbuf_rtrim(sb); /* Make sure there is an EOLN for the non-oneline case */ if (pp->fmt != CMIT_FMT_ONELINE) strbuf_addch(sb, '\n'); /* * The caller may append additional body text in e-mail * format. Make sure we did not strip the blank line * between the header and the body. */ if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body) strbuf_addch(sb, '\n'); unuse_commit_buffer(commit, reencoded); }
void pp_title_line(struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, const char *encoding, int need_8bit_cte) { static const int max_length = 78; /* per rfc2047 */ struct strbuf title; strbuf_init(&title, 80); *msg_p = format_subject(&title, *msg_p, pp->preserve_subject ? "\n" : " "); strbuf_grow(sb, title.len + 1024); 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); else strbuf_add_wrapped_bytes(sb, title.buf, title.len, -last_line_length(sb), 1, max_length); } else { strbuf_addbuf(sb, &title); } strbuf_addch(sb, '\n'); if (need_8bit_cte == 0) { int i; for (i = 0; i < pp->in_body_headers.nr; i++) { if (has_non_ascii(pp->in_body_headers.items[i].string)) { need_8bit_cte = 1; break; } } } if (need_8bit_cte > 0) { const char *header_fmt = "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=%s\n" "Content-Transfer-Encoding: 8bit\n"; strbuf_addf(sb, header_fmt, encoding); } if (pp->after_subject) { strbuf_addstr(sb, pp->after_subject); } if (cmit_fmt_is_mail(pp->fmt)) { strbuf_addch(sb, '\n'); } if (pp->in_body_headers.nr) { int i; for (i = 0; i < pp->in_body_headers.nr; i++) { strbuf_addstr(sb, pp->in_body_headers.items[i].string); free(pp->in_body_headers.items[i].string); } string_list_clear(&pp->in_body_headers, 0); strbuf_addch(sb, '\n'); } strbuf_release(&title); }