Merge branch 'mg/verify-commit'
authorJunio C Hamano <gitster@pobox.com>
Thu, 10 Jul 2014 18:27:33 +0000 (11:27 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 10 Jul 2014 18:27:34 +0000 (11:27 -0700)
Add 'verify-commit' to be used in a way similar to 'verify-tag' is
used. Further work on verifying the mergetags might be needed.

* mg/verify-commit:
t7510: test verify-commit
t7510: exit for loop with test result
verify-commit: scriptable commit signature verification
gpg-interface: provide access to the payload
gpg-interface: provide clear helper for struct signature_check

1  2 
builtin/merge.c
commit.c
git.c
pretty.c
t/t7510-signed-commit.sh
diff --combined builtin/merge.c
index b49c310866f76434c3838484c0bf997a1230c7ed,e50323d423ed7a9dd7b41b7c007669da309b6e1d..86e9c61277990e2999d678af95824d5aec0b3df0
@@@ -852,8 -852,8 +852,8 @@@ static int merge_trivial(struct commit 
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
        prepare_to_commit(remoteheads);
 -      if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
 -                      sign_commit))
 +      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parent,
 +                      result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, result_commit, "In-index merge");
        drop_save();
@@@ -877,8 -877,8 +877,8 @@@ static int finish_automerge(struct comm
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
 -      if (commit_tree(&merge_msg, result_tree, parents, result_commit,
 -                      NULL, sign_commit))
 +      if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
 +                      result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, remoteheads, result_commit, buf.buf);
@@@ -1282,10 -1282,7 +1282,7 @@@ int cmd_merge(int argc, const char **ar
                                printf(_("Commit %s has a good GPG signature by %s\n"),
                                       hex, signature_check.signer);
  
-                       free(signature_check.gpg_output);
-                       free(signature_check.gpg_status);
-                       free(signature_check.signer);
-                       free(signature_check.key);
+                       signature_check_clear(&signature_check);
                }
        }
  
diff --combined commit.c
index fb7897c2a4cef3298c014cbac86081b1d6adc837,e9686b2df438bf9680e7ebcdd84c406b88e53ea6..acb74b55d4ee72ddf626754535a9febeb05ba945
+++ b/commit.c
@@@ -17,6 -17,7 +17,6 @@@ static struct commit_extra_header *read
  int save_commit_buffer = 1;
  
  const char *commit_type = "commit";
 -static int commit_count;
  
  static struct commit *check_commit(struct object *obj,
                                   const unsigned char *sha1,
@@@ -63,6 -64,7 +63,6 @@@ struct commit *lookup_commit(const unsi
        struct object *obj = lookup_object(sha1);
        if (!obj) {
                struct commit *c = alloc_commit_node();
 -              c->index = commit_count++;
                return create_object(sha1, OBJ_COMMIT, c);
        }
        if (!obj->type)
@@@ -245,76 -247,6 +245,76 @@@ int unregister_shallow(const unsigned c
        return 0;
  }
  
 +struct commit_buffer {
 +      void *buffer;
 +      unsigned long size;
 +};
 +define_commit_slab(buffer_slab, struct commit_buffer);
 +static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab);
 +
 +void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      v->buffer = buffer;
 +      v->size = size;
 +}
 +
 +const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      if (sizep)
 +              *sizep = v->size;
 +      return v->buffer;
 +}
 +
 +const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
 +{
 +      const void *ret = get_cached_commit_buffer(commit, sizep);
 +      if (!ret) {
 +              enum object_type type;
 +              unsigned long size;
 +              ret = read_sha1_file(commit->object.sha1, &type, &size);
 +              if (!ret)
 +                      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 (sizep)
 +                      *sizep = size;
 +      }
 +      return ret;
 +}
 +
 +void unuse_commit_buffer(const struct commit *commit, const void *buffer)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      if (v->buffer != buffer)
 +              free((void *)buffer);
 +}
 +
 +void free_commit_buffer(struct commit *commit)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      free(v->buffer);
 +      v->buffer = NULL;
 +      v->size = 0;
 +}
 +
 +const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      void *ret;
 +
 +      ret = v->buffer;
 +      if (sizep)
 +              *sizep = v->size;
 +
 +      v->buffer = NULL;
 +      v->size = 0;
 +      return ret;
 +}
 +
  int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
  {
        const char *tail = buffer;
@@@ -392,7 -324,7 +392,7 @@@ int parse_commit(struct commit *item
        }
        ret = parse_commit_buffer(item, buffer, size);
        if (save_commit_buffer && !ret) {
 -              item->buffer = buffer;
 +              set_commit_buffer(item, buffer, size);
                return 0;
        }
        free(buffer);
@@@ -607,14 -539,25 +607,14 @@@ static void record_author_date(struct a
                               struct commit *commit)
  {
        const char *buf, *line_end, *ident_line;
 -      char *buffer = NULL;
 +      const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
        char *date_end;
        unsigned long date;
  
 -      if (!commit->buffer) {
 -              unsigned long size;
 -              enum object_type type;
 -              buffer = read_sha1_file(commit->object.sha1, &type, &size);
 -              if (!buffer)
 -                      return;
 -      }
 -
 -      for (buf = commit->buffer ? commit->buffer : buffer;
 -           buf;
 -           buf = line_end + 1) {
 +      for (buf = buffer; buf; buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
 -              ident_line = skip_prefix(buf, "author ");
 -              if (!ident_line) {
 +              if (!skip_prefix(buf, "author ", &ident_line)) {
                        if (!line_end[0] || line_end[1] == '\n')
                                return; /* end of header */
                        continue;
        *(author_date_slab_at(author_date, commit)) = date;
  
  fail_exit:
 -      free(buffer);
 +      unuse_commit_buffer(commit, buffer);
  }
  
  static int compare_commits_by_author_date(const void *a_, const void *b_,
@@@ -1088,7 -1031,7 +1088,7 @@@ struct commit_list *reduce_heads(struc
                p->item->object.flags |= STALE;
                num_head++;
        }
 -      array = xcalloc(sizeof(*array), num_head);
 +      array = xcalloc(num_head, sizeof(*array));
        for (p = heads, i = 0; p; p = p->next) {
                if (p->item->object.flags & STALE) {
                        array[i++] = p->item;
@@@ -1137,14 -1080,17 +1137,14 @@@ static int do_sign_commit(struct strbu
        return 0;
  }
  
 -int parse_signed_commit(const unsigned char *sha1,
 +int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature)
  {
 +
        unsigned long size;
 -      enum object_type type;
 -      char *buffer = read_sha1_file(sha1, &type, &size);
 +      const char *buffer = get_commit_buffer(commit, &size);
        int in_signature, saw_signature = -1;
 -      char *line, *tail;
 -
 -      if (!buffer || type != OBJ_COMMIT)
 -              goto cleanup;
 +      const char *line, *tail;
  
        line = buffer;
        tail = buffer + size;
        saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
 -              char *next = memchr(line, '\n', tail - line);
 +              const char *next = memchr(line, '\n', tail - line);
  
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                }
                line = next;
        }
 - cleanup:
 -      free(buffer);
 +      unuse_commit_buffer(commit, buffer);
        return saw_signature;
  }
  
@@@ -1236,7 -1183,8 +1236,7 @@@ static void parse_gpg_output(struct sig
        for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                const char *found, *next;
  
 -              found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
 -              if (!found) {
 +              if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
                        found = strstr(buf, sigcheck_gpg_status[i].check);
                        if (!found)
                                continue;
@@@ -1263,13 -1211,15 +1263,14 @@@ void check_commit_signature(const struc
  
        sigc->result = 'N';
  
 -      if (parse_signed_commit(commit->object.sha1,
 -                              &payload, &signature) <= 0)
 +      if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
                                      &gpg_output, &gpg_status);
        if (status && !gpg_output.len)
                goto out;
+       sigc->payload = strbuf_detach(&payload, NULL);
        sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
        sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
        parse_gpg_output(sigc);
@@@ -1308,9 -1258,11 +1309,9 @@@ struct commit_extra_header *read_commit
  {
        struct commit_extra_header *extra = NULL;
        unsigned long size;
 -      enum object_type type;
 -      char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
 -      if (buffer && type == OBJ_COMMIT)
 -              extra = read_commit_extra_header_lines(buffer, size, exclude);
 -      free(buffer);
 +      const char *buffer = get_commit_buffer(commit, &size);
 +      extra = read_commit_extra_header_lines(buffer, size, exclude);
 +      unuse_commit_buffer(commit, buffer);
        return extra;
  }
  
@@@ -1393,8 -1345,7 +1394,8 @@@ void free_commit_extra_headers(struct c
        }
  }
  
 -int commit_tree(const struct strbuf *msg, const unsigned char *tree,
 +int commit_tree(const char *msg, size_t msg_len,
 +              const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
  {
        int result;
  
        append_merge_tag_headers(parents, &tail);
 -      result = commit_tree_extended(msg, tree, parents, ret,
 +      result = commit_tree_extended(msg, msg_len, tree, parents, ret,
                                      author, sign_commit, extra);
        free_commit_extra_headers(extra);
        return result;
@@@ -1523,8 -1474,7 +1524,8 @@@ static const char commit_utf8_warn[] 
  "You may want to amend it after fixing the message, or set the config\n"
  "variable i18n.commitencoding to the encoding your project uses.\n";
  
 -int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
 +int commit_tree_extended(const char *msg, size_t msg_len,
 +                       const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
  
        assert_sha1_type(tree, OBJ_TREE);
  
 -      if (memchr(msg->buf, '\0', msg->len))
 +      if (memchr(msg, '\0', msg_len))
                return error("a NUL byte in commit log message not allowed.");
  
        /* Not having i18n.commitencoding is the same as having utf-8 */
        strbuf_addch(&buffer, '\n');
  
        /* And add the comment */
 -      strbuf_addbuf(&buffer, msg);
 +      strbuf_add(&buffer, msg, msg_len);
  
        /* And check the encoding */
        if (encoding_is_utf8 && !verify_utf8(&buffer))
diff --combined git.c
index dd54f5734a246d65436c823dd65fb224b240009f,bbc329395bd3ba711fae51a1f929eb5c8a87ebed..5b6c7611be93c535491bbc9ba027e3acbad0db8f
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -20,43 -20,6 +20,43 @@@ const char git_more_info_string[] 
  
  static struct startup_info git_startup_info;
  static int use_pager = -1;
 +static char orig_cwd[PATH_MAX];
 +static const char *env_names[] = {
 +      GIT_DIR_ENVIRONMENT,
 +      GIT_WORK_TREE_ENVIRONMENT,
 +      GIT_IMPLICIT_WORK_TREE_ENVIRONMENT,
 +      GIT_PREFIX_ENVIRONMENT
 +};
 +static char *orig_env[4];
 +static int saved_environment;
 +
 +static void save_env(void)
 +{
 +      int i;
 +      if (saved_environment)
 +              return;
 +      saved_environment = 1;
 +      if (!getcwd(orig_cwd, sizeof(orig_cwd)))
 +              die_errno("cannot getcwd");
 +      for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 +              orig_env[i] = getenv(env_names[i]);
 +              if (orig_env[i])
 +                      orig_env[i] = xstrdup(orig_env[i]);
 +      }
 +}
 +
 +static void restore_env(void)
 +{
 +      int i;
 +      if (*orig_cwd && chdir(orig_cwd))
 +              die_errno("could not move to %s", orig_cwd);
 +      for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 +              if (orig_env[i])
 +                      setenv(env_names[i], orig_env[i], 1);
 +              else
 +                      unsetenv(env_names[i]);
 +      }
 +}
  
  static void commit_pager_choice(void) {
        switch (use_pager) {
@@@ -91,7 -54,8 +91,7 @@@ static int handle_options(const char **
                /*
                 * Check remaining flags.
                 */
 -              if (starts_with(cmd, "--exec-path")) {
 -                      cmd += 11;
 +              if (skip_prefix(cmd, "--exec-path", &cmd)) {
                        if (*cmd == '=')
                                git_set_argv_exec_path(cmd + 1);
                        else {
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
 -              } else if (starts_with(cmd, "--git-dir=")) {
 -                      setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
 +              } else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
 +                      setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--namespace")) {
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
 -              } else if (starts_with(cmd, "--namespace=")) {
 -                      setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
 +              } else if (skip_prefix(cmd, "--namespace=", &cmd)) {
 +                      setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
 -              } else if (starts_with(cmd, "--work-tree=")) {
 -                      setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
 +              } else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
 +                      setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--bare")) {
@@@ -308,7 -272,6 +308,7 @@@ static int handle_alias(int *argcp, con
   * RUN_SETUP for reading from the configuration file.
   */
  #define NEED_WORK_TREE                (1<<3)
 +#define NO_SETUP              (1<<4)
  
  struct cmd_struct {
        const char *cmd;
@@@ -389,7 -352,7 +389,7 @@@ static struct cmd_struct commands[] = 
        { "cherry", cmd_cherry, RUN_SETUP },
        { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
        { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
 -      { "clone", cmd_clone },
 +      { "clone", cmd_clone, NO_SETUP },
        { "column", cmd_column, RUN_SETUP_GENTLY },
        { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
        { "commit-tree", cmd_commit_tree, RUN_SETUP },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
 -      { "init", cmd_init_db },
 -      { "init-db", cmd_init_db },
 +      { "init", cmd_init_db, NO_SETUP },
 +      { "init-db", cmd_init_db, NO_SETUP },
        { "log", cmd_log, RUN_SETUP },
        { "ls-files", cmd_ls_files, RUN_SETUP },
        { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
        { "upload-archive", cmd_upload_archive },
        { "upload-archive--writer", cmd_upload_archive_writer },
        { "var", cmd_var, RUN_SETUP_GENTLY },
+       { "verify-commit", cmd_verify_commit, RUN_SETUP },
        { "verify-pack", cmd_verify_pack },
        { "verify-tag", cmd_verify_tag, RUN_SETUP },
        { "version", cmd_version },
@@@ -521,10 -485,6 +522,10 @@@ static void handle_builtin(int argc, co
                struct cmd_struct *p = commands+i;
                if (strcmp(p->cmd, cmd))
                        continue;
 +              if (saved_environment && (p->option & NO_SETUP)) {
 +                      restore_env();
 +                      break;
 +              }
                exit(run_builtin(p, argc, argv));
        }
  }
@@@ -580,10 -540,7 +581,10 @@@ static int run_argv(int *argcp, const c
                 * of overriding "git log" with "git show" by having
                 * alias.log = show
                 */
 -              if (done_alias || !handle_alias(argcp, argv))
 +              if (done_alias)
 +                      break;
 +              save_env();
 +              if (!handle_alias(argcp, argv))
                        break;
                done_alias = 1;
        }
@@@ -622,7 -579,8 +623,7 @@@ int main(int argc, char **av
         * So we just directly call the builtin handler, and die if
         * that one cannot handle it.
         */
 -      if (starts_with(cmd, "git-")) {
 -              cmd += 4;
 +      if (skip_prefix(cmd, "git-", &cmd)) {
                argv[0] = cmd;
                handle_builtin(argc, argv);
                die("cannot handle %s as a builtin", cmd);
        argc--;
        handle_options(&argv, &argc, NULL);
        if (argc > 0) {
 -              if (starts_with(argv[0], "--"))
 -                      argv[0] += 2;
 +              /* translate --help and --version into commands */
 +              skip_prefix(argv[0], "--", &argv[0]);
        } else {
                /* The user didn't specify a command; give them help */
                commit_pager_choice();
diff --combined pretty.c
index 8d201f6bda1bb9ccf97be4fa25660adbebcfe74f,f6a0023dd2f05e3f807e857f88b6767b028f553e..14357e233f3174963add310a29bb35c3de229121
+++ b/pretty.c
@@@ -40,9 -40,10 +40,9 @@@ static int git_pretty_formats_config(co
        const char *fmt;
        int i;
  
 -      if (!starts_with(var, "pretty."))
 +      if (!skip_prefix(var, "pretty.", &name))
                return 0;
  
 -      name = var + strlen("pretty.");
        for (i = 0; i < builtin_formats_len; i++) {
                if (!strcmp(commit_formats[i].name, name))
                        return 0;
@@@ -273,7 -274,7 +273,7 @@@ static void add_rfc822_quoted(struct st
  
  enum rfc2047_type {
        RFC2047_SUBJECT,
 -      RFC2047_ADDRESS,
 +      RFC2047_ADDRESS
  };
  
  static int is_rfc2047_special(char ch, enum rfc2047_type type)
@@@ -605,16 -606,29 +605,16 @@@ static char *replace_encoding_header(ch
        return strbuf_detach(&tmp, NULL);
  }
  
 -char *logmsg_reencode(const struct commit *commit,
 -                    char **commit_encoding,
 -                    const char *output_encoding)
 +const char *logmsg_reencode(const struct commit *commit,
 +                          char **commit_encoding,
 +                          const char *output_encoding)
  {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
 -      char *msg = commit->buffer;
 +      const char *msg = get_commit_buffer(commit, NULL);
        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) {
                if (commit_encoding)
                        *commit_encoding =
                 * 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.
 +               * the cached copy from get_commit_buffer, we need to duplicate it
 +               * to avoid munging the cached copy.
                 */
 -              out = msg;
 -              if (out == commit->buffer)
 -                      out = xstrdup(out);
 +              if (msg == get_cached_commit_buffer(commit, NULL))
 +                      out = xstrdup(msg);
 +              else
 +                      out = (char *)msg;
        }
        else {
                /*
                 * copy, we can free it.
                 */
                out = reencode_string(msg, output_encoding, use_encoding);
 -              if (out && msg != commit->buffer)
 -                      free(msg);
 +              if (out)
 +                      unuse_commit_buffer(commit, msg);
        }
  
        /*
        return out ? out : msg;
  }
  
 -void logmsg_free(char *msg, const struct commit *commit)
 -{
 -      if (msg != commit->buffer)
 -              free(msg);
 -}
 -
  static int mailmap_name(const char **email, size_t *email_len,
                        const char **name, size_t *name_len)
  {
@@@ -777,7 -796,7 +777,7 @@@ struct format_commit_context 
        struct signature_check signature_check;
        enum flush_type flush_type;
        enum trunc_type truncate;
 -      char *message;
 +      const char *message;
        char *commit_encoding;
        size_t width, indent1, indent2;
        int auto_color;
@@@ -1248,8 -1267,6 +1248,8 @@@ static size_t format_commit_one(struct 
                        if (c->signature_check.key)
                                strbuf_addstr(sb, c->signature_check.key);
                        break;
 +              default:
 +                      return 0;
                }
                return 2;
        }
@@@ -1489,18 -1506,13 +1489,18 @@@ void format_commit_message(const struc
        context.commit = commit;
        context.pretty_ctx = pretty_ctx;
        context.wrap_start = sb->len;
 +      /*
 +       * convert a commit message to UTF-8 first
 +       * as far as 'format_commit_item' assumes it in UTF-8
 +       */
        context.message = logmsg_reencode(commit,
                                          &context.commit_encoding,
 -                                        output_enc);
 +                                        utf8);
  
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
  
 +      /* then convert a commit message to an actual output encoding */
        if (output_enc) {
                if (same_encoding(utf8, output_enc))
                        output_enc = NULL;
        }
  
        free(context.commit_encoding);
 -      logmsg_free(context.message, commit);
 -      signature_check_clear(&context.signature_check);
 +      unuse_commit_buffer(commit, context.message);
-       free(context.signature_check.gpg_output);
-       free(context.signature_check.signer);
  }
  
  static void pp_header(struct pretty_print_context *pp,
@@@ -1688,7 -1699,7 +1686,7 @@@ void pretty_print_commit(struct pretty_
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg;
 -      char *reencoded;
 +      const char *reencoded;
        const char *encoding;
        int need_8bit_cte = pp->need_8bit_cte;
  
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
 -      logmsg_free(reencoded, commit);
 +      unuse_commit_buffer(commit, reencoded);
  }
  
  void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
diff --combined t/t7510-signed-commit.sh
index e97477a3b98a49c80ce01f06b966eacff52a3bd1,dd4b94823e182811b0ddf2a1e997e50ba7c815ad..474dab381aef027207026cb938df7a09cc7a9056
@@@ -43,57 -43,61 +43,74 @@@ test_expect_success GPG 'create signed 
  
        test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
        git tag seventh-signed
 +
 +      echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
 +      git tag eighth-signed-alt
  '
  
- test_expect_success GPG 'show signatures' '
+ test_expect_success GPG 'verify and show signatures' '
        (
 -              for commit in initial second merge fourth-signed fifth-signed sixth-signed master
 +              for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
                do
+                       git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
                        grep "Good signature from" actual &&
 -                      ! grep "BAD signature from" actual || exit 1
 -                      echo $commit OK
 +                      ! grep "BAD signature from" actual &&
 +                      echo $commit OK || exit 1
                done
        ) &&
        (
                for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
                do
+                       test_must_fail git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
                        ! grep "Good signature from" actual &&
 -                      ! grep "BAD signature from" actual || exit 1
 -                      echo $commit OK
 +                      ! grep "BAD signature from" actual &&
 +                      echo $commit OK || exit 1
 +              done
 +      ) &&
 +      (
 +              for commit in eighth-signed-alt
 +              do
 +                      git show --pretty=short --show-signature $commit >actual &&
 +                      grep "Good signature from" actual &&
 +                      ! grep "BAD signature from" actual &&
 +                      grep "not certified" actual &&
 +                      echo $commit OK || exit 1
                done
        )
  '
  
+ test_expect_success GPG 'show signed commit with signature' '
+       git show -s initial >commit &&
+       git show -s --show-signature initial >show &&
+       git verify-commit -v initial >verify.1 2>verify.2 &&
+       git cat-file commit initial >cat &&
+       grep -v "gpg: " show >show.commit &&
+       grep "gpg: " show >show.gpg &&
+       grep -v "^ " cat | grep -v "^gpgsig " >cat.commit &&
+       test_cmp show.commit commit &&
+       test_cmp show.gpg verify.2 &&
+       test_cmp cat.commit verify.1
+ '
  test_expect_success GPG 'detect fudged signature' '
 -      git cat-file commit master >raw &&
 +      git cat-file commit seventh-signed >raw &&
  
        sed -e "s/seventh/7th forged/" raw >forged1 &&
        git hash-object -w -t commit forged1 >forged1.commit &&
+       ! git verify-commit $(cat forged1.commit) &&
        git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
        grep "BAD signature from" actual1 &&
        ! grep "Good signature from" actual1
  '
  
  test_expect_success GPG 'detect fudged signature with NUL' '
 -      git cat-file commit master >raw &&
 +      git cat-file commit seventh-signed >raw &&
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
        git hash-object -w -t commit forged2 >forged2.commit &&
+       ! git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "BAD signature from" actual2 &&
        ! grep "Good signature from" actual2
  test_expect_success GPG 'amending already signed commit' '
        git checkout fourth-signed^0 &&
        git commit --amend -S --no-edit &&
+       git verify-commit HEAD &&
        git show -s --show-signature HEAD >actual &&
        grep "Good signature from" actual &&
        ! grep "BAD signature from" actual
  '
  
 +test_expect_success GPG 'show good signature with custom format' '
 +      cat >expect <<-\EOF &&
 +      G
 +      13B6F51ECDDE430D
 +      C O Mitter <committer@example.com>
 +      EOF
 +      git log -1 --format="%G?%n%GK%n%GS" sixth-signed >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success GPG 'show bad signature with custom format' '
 +      cat >expect <<-\EOF &&
 +      B
 +      13B6F51ECDDE430D
 +      C O Mitter <committer@example.com>
 +      EOF
 +      git log -1 --format="%G?%n%GK%n%GS" $(cat forged1.commit) >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success GPG 'show unknown signature with custom format' '
 +      cat >expect <<-\EOF &&
 +      U
 +      61092E85B7227189
 +      Eris Discordia <discord@example.net>
 +      EOF
 +      git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success GPG 'show lack of signature with custom format' '
 +      cat >expect <<-\EOF &&
 +      N
 +
 +
 +      EOF
 +      git log -1 --format="%G?%n%GK%n%GS" seventh-unsigned >actual &&
 +      test_cmp expect actual
 +'
 +
  test_done