]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'mg/gpg-interface-using-status' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Apr 2013 16:26:27 +0000 (09:26 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Apr 2013 16:26:27 +0000 (09:26 -0700)
Verification of signed tags were not done correctly when not in C
or en/US locale.

* mg/gpg-interface-using-status:
  pretty: make %GK output the signing key for signed commits
  pretty: parse the gpg status lines rather than the output
  gpg_interface: allow to request status return
  log-tree: rely upon the check in the gpg_interface
  gpg-interface: check good signature in a reliable way

1  2 
Documentation/pretty-formats.txt
builtin/fmt-merge-msg.c
gpg-interface.c
log-tree.c
pretty.c

index 105f18a6f9fd9f4a5a388cb3ec8a0e6a99f7abe3,443381817a38525f78081a2a4c8cbb57ef0e082c..293965524e81b240996a795498ba127f723d18eb
@@@ -133,6 -133,7 +133,7 @@@ The placeholders are
  - '%GG': raw verification message from GPG for a signed commit
  - '%G?': show either "G" for Good or "B" for Bad for a signed commit
  - '%GS': show the name of the signer for a signed commit
+ - '%GK': show the key used to sign a signed commit
  - '%gD': reflog selector, e.g., `refs/stash@{1}`
  - '%gd': shortened reflog selector, e.g., `stash@{1}`
  - '%gn': reflog identity name
  - '%Cgreen': switch color to green
  - '%Cblue': switch color to blue
  - '%Creset': reset color
 -- '%C(...)': color specification, as described in color.branch.* config option
 +- '%C(...)': color specification, as described in color.branch.* config option;
 +  adding `auto,` at the beginning will emit color only when colors are
 +  enabled for log output (by `color.diff`, `color.ui`, or `--color`, and
 +  respecting the `auto` settings of the former if we are going to a
 +  terminal)
  - '%m': left, right or boundary mark
  - '%n': newline
  - '%%': a raw '%'
diff --combined builtin/fmt-merge-msg.c
index b49612f0ce02e0c8503bee77477800297e84065d,69bf15a98105588f83dc5a755231ec311bac6b12..265a9253bfd7cacd730789212fdb5284fcd270ac
@@@ -470,7 -470,7 +470,7 @@@ static void fmt_tag_signature(struct st
        strbuf_complete_line(tagbuf);
        if (sig->len) {
                strbuf_addch(tagbuf, '\n');
 -              strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
 +              strbuf_add_commented_lines(tagbuf, sig->buf, sig->len);
        }
  }
  
@@@ -492,7 -492,7 +492,7 @@@ static void fmt_merge_msg_sigs(struct s
  
                if (size == len)
                        ; /* merely annotated */
-               else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig)) {
+               else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig, NULL)) {
                        if (!sig.len)
                                strbuf_addstr(&sig, "gpg verification failed.\n");
                }
diff --combined gpg-interface.c
index 45590330aa3131da69174899783913cee9a44c31,ce07cd5cbbf9e0aa32bc79a9ce128369a0ce2603..8b0e87436b687ce26e3e4987129a2fc171069280
@@@ -96,35 -96,39 +96,39 @@@ int sign_buffer(struct strbuf *buffer, 
  /*
   * Run "gpg" to see if the payload matches the detached signature.
   * gpg_output, when set, receives the diagnostic output from GPG.
+  * gpg_status, when set, receives the status output from GPG.
   */
  int verify_signed_buffer(const char *payload, size_t payload_size,
                         const char *signature, size_t signature_size,
-                        struct strbuf *gpg_output)
+                        struct strbuf *gpg_output, struct strbuf *gpg_status)
  {
        struct child_process gpg;
-       const char *args_gpg[] = {NULL, "--verify", "FILE", "-", NULL};
+       const char *args_gpg[] = {NULL, "--status-fd=1", "--verify", "FILE", "-", NULL};
        char path[PATH_MAX];
        int fd, ret;
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf *pbuf = &buf;
  
        args_gpg[0] = gpg_program;
        fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
        if (fd < 0)
 -              return error("could not create temporary file '%s': %s",
 +              return error(_("could not create temporary file '%s': %s"),
                             path, strerror(errno));
        if (write_in_full(fd, signature, signature_size) < 0)
 -              return error("failed writing detached signature to '%s': %s",
 +              return error(_("failed writing detached signature to '%s': %s"),
                             path, strerror(errno));
        close(fd);
  
        memset(&gpg, 0, sizeof(gpg));
        gpg.argv = args_gpg;
        gpg.in = -1;
+       gpg.out = -1;
        if (gpg_output)
                gpg.err = -1;
-       args_gpg[2] = path;
+       args_gpg[3] = path;
        if (start_command(&gpg)) {
                unlink(path);
 -              return error("could not run gpg.");
 +              return error(_("could not run gpg."));
        }
  
        write_in_full(gpg.in, payload, payload_size);
                strbuf_read(gpg_output, gpg.err, 0);
                close(gpg.err);
        }
+       if (gpg_status)
+               pbuf = gpg_status;
+       strbuf_read(pbuf, gpg.out, 0);
+       close(gpg.out);
        ret = finish_command(&gpg);
  
        unlink_or_warn(path);
  
+       ret |= !strstr(pbuf->buf, "\n[GNUPG:] GOODSIG ");
+       strbuf_release(&buf); /* no matter it was used or not */
        return ret;
  }
diff --combined log-tree.c
index 5dc45c4812bdfd0d7a6b71d529eaa37df8178186,9cb78d195ec91b21230ca938b7ea5f95ecd9684d..3d888238711364338a83e7d81bf27f555352bdca
@@@ -299,34 -299,26 +299,34 @@@ static unsigned int digits_in_number(un
        return result;
  }
  
 -void get_patch_filename(struct commit *commit, const char *subject, int nr,
 -                      const char *suffix, struct strbuf *buf)
 +void fmt_output_subject(struct strbuf *filename,
 +                      const char *subject,
 +                      struct rev_info *info)
  {
 -      int suffix_len = strlen(suffix) + 1;
 -      int start_len = buf->len;
 -
 -      strbuf_addf(buf, commit || subject ? "%04d-" : "%d", nr);
 -      if (commit || subject) {
 -              int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
 -              struct pretty_print_context ctx = {0};
 -
 -              if (subject)
 -                      strbuf_addstr(buf, subject);
 -              else if (commit)
 -                      format_commit_message(commit, "%f", buf, &ctx);
 -
 -              if (max_len < buf->len)
 -                      strbuf_setlen(buf, max_len);
 -              strbuf_addstr(buf, suffix);
 -      }
 +      const char *suffix = info->patch_suffix;
 +      int nr = info->nr;
 +      int start_len = filename->len;
 +      int max_len = start_len + FORMAT_PATCH_NAME_MAX - (strlen(suffix) + 1);
 +
 +      if (0 < info->reroll_count)
 +              strbuf_addf(filename, "v%d-", info->reroll_count);
 +      strbuf_addf(filename, "%04d-%s", nr, subject);
 +
 +      if (max_len < filename->len)
 +              strbuf_setlen(filename, max_len);
 +      strbuf_addstr(filename, suffix);
 +}
 +
 +void fmt_output_commit(struct strbuf *filename,
 +                     struct commit *commit,
 +                     struct rev_info *info)
 +{
 +      struct pretty_print_context ctx = {0};
 +      struct strbuf subject = STRBUF_INIT;
 +
 +      format_commit_message(commit, "%f", &subject, &ctx);
 +      fmt_output_subject(filename, subject.buf, info);
 +      strbuf_release(&subject);
  }
  
  void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                         mime_boundary_leader, opt->mime_boundary);
                extra_headers = subject_buffer;
  
 -              get_patch_filename(opt->numbered_files ? NULL : commit, NULL,
 -                                 opt->nr, opt->patch_suffix, &filename);
 +              if (opt->numbered_files)
 +                      strbuf_addf(&filename, "%d", opt->nr);
 +              else
 +                      fmt_output_commit(&filename, commit, opt);
                snprintf(buffer, sizeof(buffer) - 1,
                         "\n--%s%s\n"
                         "Content-Type: text/x-patch;"
@@@ -444,7 -434,7 +444,7 @@@ static void show_signature(struct rev_i
  
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
-                                     &gpg_output);
+                                     &gpg_output, NULL);
        if (status && !gpg_output.len)
                strbuf_addstr(&gpg_output, "No signature\n");
  
@@@ -508,20 -498,17 +508,17 @@@ static void show_one_mergetag(struct re
        gpg_message_offset = verify_message.len;
  
        payload_size = parse_signature(extra->value, extra->len);
-       if ((extra->len <= payload_size) ||
-           (verify_signed_buffer(extra->value, payload_size,
-                                 extra->value + payload_size,
-                                 extra->len - payload_size,
-                                 &verify_message) &&
-            verify_message.len <= gpg_message_offset)) {
-               strbuf_addstr(&verify_message, "No signature\n");
-               status = -1;
-       }
-       else if (strstr(verify_message.buf + gpg_message_offset,
-                       ": Good signature from "))
-               status = 0;
-       else
-               status = -1;
+       status = -1;
+       if (extra->len > payload_size)
+               if (verify_signed_buffer(extra->value, payload_size,
+                                        extra->value + payload_size,
+                                        extra->len - payload_size,
+                                        &verify_message, NULL)) {
+                       if (verify_message.len <= gpg_message_offset)
+                               strbuf_addstr(&verify_message, "No signature\n");
+                       else
+                               status = 0;
+               }
  
        show_sig_lines(opt, status, verify_message.buf);
        strbuf_release(&verify_message);
@@@ -681,8 -668,6 +678,8 @@@ void show_log(struct rev_info *opt
        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;
        pretty_print_commit(&ctx, commit, &msgbuf);
  
        if (opt->add_signoff)
diff --combined pretty.c
index 41ffbc92404316027490cb36c6c9971fd4074494,9119fe7728b6d78ae6bd583c4f4efbf955761c77..41f04e669d3b9c38f49e0853053a6ba27b97357a
+++ b/pretty.c
@@@ -345,7 -345,7 +345,7 @@@ static int needs_rfc2047_encoding(cons
        return 0;
  }
  
 -static void add_rfc2047(struct strbuf *sb, const char *line, int len,
 +static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
                       const char *encoding, enum rfc2047_type type)
  {
        static const int max_encoded_length = 76; /* per rfc2047 */
        strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
        strbuf_addf(sb, "=?%s?q?", encoding);
        line_len += strlen(encoding) + 5; /* 5 for =??q? */
 -      for (i = 0; i < len; i++) {
 -              unsigned ch = line[i] & 0xFF;
 -              int is_special = is_rfc2047_special(ch, type);
 +
 +      while (len) {
 +              /*
 +               * RFC 2047, section 5 (3):
 +               *
 +               * Each 'encoded-word' MUST represent an integral number of
 +               * characters.  A multi-octet character may not be split across
 +               * adjacent 'encoded- word's.
 +               */
 +              const unsigned char *p = (const unsigned char *)line;
 +              int chrlen = mbs_chrlen(&line, &len, encoding);
 +              int is_special = (chrlen > 1) || is_rfc2047_special(*p, type);
 +
 +              /* "=%02X" * chrlen, or the byte itself */
 +              const char *encoded_fmt = is_special ? "=%02X"    : "%c";
 +              int         encoded_len = is_special ? 3 * chrlen : 1;
  
                /*
                 * According to RFC 2047, we could encode the special character
                 * causes ' ' to be encoded as '=20', avoiding this problem.
                 */
  
 -              if (line_len + 2 + (is_special ? 3 : 1) > max_encoded_length) {
 +              if (line_len + encoded_len + 2 > max_encoded_length) {
 +                      /* It won't fit with trailing "?=" --- break the line */
                        strbuf_addf(sb, "?=\n =?%s?q?", encoding);
                        line_len = strlen(encoding) + 5 + 1; /* =??q? plus SP */
                }
  
 -              if (is_special) {
 -                      strbuf_addf(sb, "=%02X", ch);
 -                      line_len += 3;
 -              } else {
 -                      strbuf_addch(sb, ch);
 -                      line_len++;
 -              }
 +              for (i = 0; i < chrlen; i++)
 +                      strbuf_addf(sb, encoded_fmt, p[i]);
 +              line_len += encoded_len;
        }
        strbuf_addstr(sb, "?=");
  }
@@@ -397,79 -387,56 +397,79 @@@ void pp_user_info(const struct pretty_p
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding)
  {
 +      struct strbuf name;
 +      struct strbuf mail;
 +      struct ident_split ident;
 +      int linelen;
 +      char *line_end, *date;
 +      const char *mailbuf, *namebuf;
 +      size_t namelen, maillen;
        int max_length = 78; /* per rfc2822 */
 -      char *date;
 -      int namelen;
        unsigned long time;
        int tz;
  
        if (pp->fmt == CMIT_FMT_ONELINE)
                return;
 -      date = strchr(line, '>');
 -      if (!date)
 +
 +      line_end = strchr(line, '\n');
 +      if (!line_end) {
 +              line_end = strchr(line, '\0');
 +              if (!line_end)
 +                      return;
 +      }
 +
 +      linelen = ++line_end - line;
 +      if (split_ident_line(&ident, line, linelen))
                return;
 -      namelen = ++date - line;
 -      time = strtoul(date, &date, 10);
 +
 +
 +      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);
 +
 +      strbuf_init(&mail, 0);
 +      strbuf_init(&name, 0);
 +
 +      strbuf_add(&mail, mailbuf, maillen);
 +      strbuf_add(&name, namebuf, namelen);
 +
 +      namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */
 +      time = strtoul(ident.date_begin, &date, 10);
        tz = strtol(date, NULL, 10);
  
        if (pp->fmt == CMIT_FMT_EMAIL) {
 -              char *name_tail = strchr(line, '<');
 -              int display_name_length;
 -              if (!name_tail)
 -                      return;
 -              while (line < name_tail && isspace(name_tail[-1]))
 -                      name_tail--;
 -              display_name_length = name_tail - line;
                strbuf_addstr(sb, "From: ");
 -              if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {
 -                      add_rfc2047(sb, line, display_name_length,
 -                                              encoding, RFC2047_ADDRESS);
 +              if (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) {
 +                      add_rfc2047(sb, name.buf, name.len,
 +                                  encoding, RFC2047_ADDRESS);
                        max_length = 76; /* per rfc2047 */
 -              } else if (needs_rfc822_quoting(line, display_name_length)) {
 +              } else if (needs_rfc822_quoting(name.buf, name.len)) {
                        struct strbuf quoted = STRBUF_INIT;
 -                      add_rfc822_quoted(&quoted, line, display_name_length);
 +                      add_rfc822_quoted(&quoted, name.buf, name.len);
                        strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
                                                        -6, 1, max_length);
                        strbuf_release(&quoted);
                } else {
 -                      strbuf_add_wrapped_bytes(sb, line, display_name_length,
 -                                                      -6, 1, max_length);
 +                      strbuf_add_wrapped_bytes(sb, name.buf, name.len,
 +                                               -6, 1, max_length);
                }
 -              if (namelen - display_name_length + last_line_length(sb) > max_length) {
 +              if (namelen - name.len + last_line_length(sb) > max_length)
                        strbuf_addch(sb, '\n');
 -                      if (!isspace(name_tail[0]))
 -                              strbuf_addch(sb, ' ');
 -              }
 -              strbuf_add(sb, name_tail, namelen - display_name_length);
 -              strbuf_addch(sb, '\n');
 +
 +              strbuf_addf(sb, " <%s>\n", mail.buf);
        } else {
 -              strbuf_addf(sb, "%s: %.*s%.*s\n", what,
 +              strbuf_addf(sb, "%s: %.*s%s <%s>\n", what,
                              (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
 -                            "    ", namelen, line);
 +                            "    ", name.buf, mail.buf);
        }
 +
 +      strbuf_release(&mail);
 +      strbuf_release(&name);
 +
        switch (pp->fmt) {
        case CMIT_FMT_MEDIUM:
                strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
@@@ -534,11 -501,10 +534,11 @@@ static void add_merge_info(const struc
        strbuf_addch(sb, '\n');
  }
  
 -static char *get_header(const struct commit *commit, const char *key)
 +static char *get_header(const struct commit *commit, const char *msg,
 +                      const char *key)
  {
        int key_len = strlen(key);
 -      const char *line = commit->buffer;
 +      const char *line = msg;
  
        while (line) {
                const char *eol = strchr(line, '\n'), *next;
@@@ -599,81 -565,28 +599,81 @@@ char *logmsg_reencode(const struct comm
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
 +      char *msg = commit->buffer;
        char *out;
  
 +      if (!msg) {
 +              enum object_type type;
 +              unsigned long size;
 +
 +              msg = read_sha1_file(commit->object.sha1, &type, &size);
 +              if (!msg)
 +                      die("Cannot read commit object %s",
 +                          sha1_to_hex(commit->object.sha1));
 +              if (type != OBJ_COMMIT)
 +                      die("Expected commit for '%s', got %s",
 +                          sha1_to_hex(commit->object.sha1), typename(type));
 +      }
 +
        if (!output_encoding || !*output_encoding)
 -              return NULL;
 -      encoding = get_header(commit, "encoding");
 +              return msg;
 +      encoding = get_header(commit, msg, "encoding");
        use_encoding = encoding ? encoding : utf8;
 -      if (same_encoding(use_encoding, output_encoding))
 -              if (encoding) /* we'll strip encoding header later */
 -                      out = xstrdup(commit->buffer);
 -              else
 -                      return NULL; /* nothing to do */
 -      else
 -              out = reencode_string(commit->buffer,
 -                                    output_encoding, use_encoding);
 +      if (same_encoding(use_encoding, output_encoding)) {
 +              /*
 +               * No encoding work to be done. If we have no encoding header
 +               * at all, then there's nothing to do, and we can return the
 +               * message verbatim (whether newly allocated or not).
 +               */
 +              if (!encoding)
 +                      return msg;
 +
 +              /*
 +               * Otherwise, we still want to munge the encoding header in the
 +               * result, which will be done by modifying the buffer. If we
 +               * are using a fresh copy, we can reuse it. But if we are using
 +               * the cached copy from commit->buffer, we need to duplicate it
 +               * to avoid munging commit->buffer.
 +               */
 +              out = msg;
 +              if (out == commit->buffer)
 +                      out = xstrdup(out);
 +      }
 +      else {
 +              /*
 +               * There's actual encoding work to do. Do the reencoding, which
 +               * still leaves the header to be replaced in the next step. At
 +               * this point, we are done with msg. If we allocated a fresh
 +               * copy, we can free it.
 +               */
 +              out = reencode_string(msg, output_encoding, use_encoding);
 +              if (out && msg != commit->buffer)
 +                      free(msg);
 +      }
 +
 +      /*
 +       * This replacement actually consumes the buffer we hand it, so we do
 +       * not have to worry about freeing the old "out" here.
 +       */
        if (out)
                out = replace_encoding_header(out, output_encoding);
  
        free(encoding);
 -      return out;
 +      /*
 +       * If the re-encoding failed, out might be NULL here; in that
 +       * case we just return the commit message verbatim.
 +       */
 +      return out ? out : msg;
 +}
 +
 +void logmsg_free(char *msg, const struct commit *commit)
 +{
 +      if (msg != commit->buffer)
 +              free(msg);
  }
  
 -static int mailmap_name(char *email, int email_len, char *name, int name_len)
 +static int mailmap_name(const char **email, size_t *email_len,
 +                      const char **name, size_t *name_len)
  {
        static struct string_list *mail_map;
        if (!mail_map) {
@@@ -690,26 -603,36 +690,26 @@@ static size_t format_person_part(struc
        const int placeholder_len = 2;
        int tz;
        unsigned long date = 0;
 -      char person_name[1024];
 -      char person_mail[1024];
        struct ident_split s;
 -      const char *name_start, *name_end, *mail_start, *mail_end;
 +      const char *name, *mail;
 +      size_t maillen, namelen;
  
        if (split_ident_line(&s, msg, len) < 0)
                goto skip;
  
 -      name_start = s.name_begin;
 -      name_end = s.name_end;
 -      mail_start = s.mail_begin;
 -      mail_end = s.mail_end;
 -
 -      if (part == 'N' || part == 'E') { /* mailmap lookup */
 -              snprintf(person_name, sizeof(person_name), "%.*s",
 -                       (int)(name_end - name_start), name_start);
 -              snprintf(person_mail, sizeof(person_mail), "%.*s",
 -                       (int)(mail_end - mail_start), mail_start);
 -              mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
 -              name_start = person_name;
 -              name_end = name_start + strlen(person_name);
 -              mail_start = person_mail;
 -              mail_end = mail_start +  strlen(person_mail);
 -      }
 +      name = s.name_begin;
 +      namelen = s.name_end - s.name_begin;
 +      mail = s.mail_begin;
 +      maillen = s.mail_end - s.mail_begin;
 +
 +      if (part == 'N' || part == 'E') /* mailmap lookup */
 +              mailmap_name(&mail, &maillen, &name, &namelen);
        if (part == 'n' || part == 'N') {       /* name */
 -              strbuf_add(sb, name_start, name_end-name_start);
 +              strbuf_add(sb, name, namelen);
                return placeholder_len;
        }
        if (part == 'e' || part == 'E') {       /* email */
 -              strbuf_add(sb, mail_start, mail_end-mail_start);
 +              strbuf_add(sb, mail, maillen);
                return placeholder_len;
        }
  
@@@ -769,8 -692,10 +769,10 @@@ struct format_commit_context 
        unsigned commit_signature_parsed:1;
        struct {
                char *gpg_output;
+               char *gpg_status;
                char good_bad;
                char *signer;
+               char *key;
        } signature;
        char *message;
        size_t width, indent1, indent2;
@@@ -958,13 -883,13 +960,13 @@@ static struct 
        char result;
        const char *check;
  } signature_check[] = {
-       { 'G', ": Good signature from " },
-       { 'B', ": BAD signature from " },
+       { 'G', "\n[GNUPG:] GOODSIG " },
+       { 'B', "\n[GNUPG:] BADSIG " },
  };
  
  static void parse_signature_lines(struct format_commit_context *ctx)
  {
-       const char *buf = ctx->signature.gpg_output;
+       const char *buf = ctx->signature.gpg_status;
        int i;
  
        for (i = 0; i < ARRAY_SIZE(signature_check); i++) {
                        continue;
                ctx->signature.good_bad = signature_check[i].result;
                found += strlen(signature_check[i].check);
+               ctx->signature.key = xmemdupz(found, 16);
+               found += 17;
                next = strchrnul(found, '\n');
                ctx->signature.signer = xmemdupz(found, next - found);
                break;
@@@ -985,6 -912,7 +989,7 @@@ static void parse_commit_signature(stru
        struct strbuf payload = STRBUF_INIT;
        struct strbuf signature = STRBUF_INIT;
        struct strbuf gpg_output = STRBUF_INIT;
+       struct strbuf gpg_status = STRBUF_INIT;
        int status;
  
        ctx->commit_signature_parsed = 1;
                goto out;
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
-                                     &gpg_output);
+                                     &gpg_output, &gpg_status);
        if (status && !gpg_output.len)
                goto out;
        ctx->signature.gpg_output = strbuf_detach(&gpg_output, NULL);
+       ctx->signature.gpg_status = strbuf_detach(&gpg_status, NULL);
        parse_signature_lines(ctx);
  
   out:
+       strbuf_release(&gpg_status);
        strbuf_release(&gpg_output);
        strbuf_release(&payload);
        strbuf_release(&signature);
@@@ -1037,19 -967,12 +1044,19 @@@ static size_t format_commit_one(struct 
        switch (placeholder[0]) {
        case 'C':
                if (placeholder[1] == '(') {
 -                      const char *end = strchr(placeholder + 2, ')');
 +                      const char *begin = placeholder + 2;
 +                      const char *end = strchr(begin, ')');
                        char color[COLOR_MAXLEN];
 +
                        if (!end)
                                return 0;
 -                      color_parse_mem(placeholder + 2,
 -                                      end - (placeholder + 2),
 +                      if (!prefixcmp(begin, "auto,")) {
 +                              if (!want_color(c->pretty_ctx->color))
 +                                      return end - placeholder + 1;
 +                              begin += 5;
 +                      }
 +                      color_parse_mem(begin,
 +                                      end - begin,
                                        "--pretty format", color);
                        strbuf_addstr(sb, color);
                        return end - placeholder + 1;
                        if (c->signature.signer)
                                strbuf_addstr(sb, c->signature.signer);
                        break;
+               case 'K':
+                       if (c->signature.key)
+                               strbuf_addstr(sb, c->signature.key);
+                       break;
                }
                return 2;
        }
@@@ -1341,11 -1268,14 +1352,11 @@@ void format_commit_message(const struc
        context.pretty_ctx = pretty_ctx;
        context.wrap_start = sb->len;
        context.message = logmsg_reencode(commit, output_enc);
 -      if (!context.message)
 -              context.message = commit->buffer;
  
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
  
 -      if (context.message != commit->buffer)
 -              free(context.message);
 +      logmsg_free(context.message, commit);
        free(context.signature.gpg_output);
        free(context.signature.signer);
  }
@@@ -1375,7 -1305,7 +1386,7 @@@ static void pp_header(const struct pret
                        continue;
                }
  
 -              if (!memcmp(line, "parent ", 7)) {
 +              if (!prefixcmp(line, "parent ")) {
                        if (linelen != 48)
                                die("bad parent line in commit");
                        continue;
                 * FULL shows both authors but not dates.
                 * FULLER shows both authors and dates.
                 */
 -              if (!memcmp(line, "author ", 7)) {
 +              if (!prefixcmp(line, "author ")) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Author", sb, line + 7, encoding);
                }
 -              if (!memcmp(line, "committer ", 10) &&
 +              if (!prefixcmp(line, "committer ") &&
                    (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Commit", sb, line + 10, encoding);
@@@ -1492,7 -1422,7 +1503,7 @@@ void pretty_print_commit(const struct p
  {
        unsigned long beginning_of_body;
        int indent = 4;
 -      const char *msg = commit->buffer;
 +      const char *msg;
        char *reencoded;
        const char *encoding;
        int need_8bit_cte = pp->need_8bit_cte;
        }
  
        encoding = get_log_output_encoding();
 -      reencoded = logmsg_reencode(commit, encoding);
 -      if (reencoded) {
 -              msg = reencoded;
 -      }
 +      msg = reencoded = logmsg_reencode(commit, encoding);
  
        if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
                indent = 0;
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
 -      free(reencoded);
 +      logmsg_free(reencoded, commit);
  }
  
  void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,