pretty: %G[?GS] placeholders
authorJunio C Hamano <gitster@pobox.com>
Sat, 22 Oct 2011 04:06:02 +0000 (21:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 13 Nov 2011 06:27:38 +0000 (22:27 -0800)
Add new placeholders related to the GPG signature on signed commits.

 - %GG to show the raw verification message from GPG;
 - %G? to show either "G" for Good, "B" for Bad;
 - %GS to show the name of the signer.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
pretty.c

index f45eb54e4c99b8d67e4aa85f9a6218ea7a560592..392d6565955febf10a5f03863d71a976ca3fe8dc 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -9,6 +9,7 @@
 #include "notes.h"
 #include "color.h"
 #include "reflog-walk.h"
+#include "gpg-interface.h"
 
 static char *user_format;
 static struct cmt_fmt_map {
@@ -640,6 +641,12 @@ struct format_commit_context {
        const struct pretty_print_context *pretty_ctx;
        unsigned commit_header_parsed:1;
        unsigned commit_message_parsed:1;
+       unsigned commit_signature_parsed:1;
+       struct {
+               char *gpg_output;
+               char good_bad;
+               char *signer;
+       } signature;
        char *message;
        size_t width, indent1, indent2;
 
@@ -822,6 +829,59 @@ static void rewrap_message_tail(struct strbuf *sb,
        c->indent2 = new_indent2;
 }
 
+static struct {
+       char result;
+       const char *check;
+} signature_check[] = {
+       { 'G', ": Good signature from " },
+       { 'B', ": BAD signature from " },
+};
+
+static void parse_signature_lines(struct format_commit_context *ctx)
+{
+       const char *buf = ctx->signature.gpg_output;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(signature_check); i++) {
+               const char *found = strstr(buf, signature_check[i].check);
+               const char *next;
+               if (!found)
+                       continue;
+               ctx->signature.good_bad = signature_check[i].result;
+               found += strlen(signature_check[i].check);
+               next = strchrnul(found, '\n');
+               ctx->signature.signer = xmemdupz(found, next - found);
+               break;
+       }
+}
+
+static void parse_commit_signature(struct format_commit_context *ctx)
+{
+       struct strbuf payload = STRBUF_INIT;
+       struct strbuf signature = STRBUF_INIT;
+       struct strbuf gpg_output = STRBUF_INIT;
+       int status;
+
+       ctx->commit_signature_parsed = 1;
+
+       if (parse_signed_commit(ctx->commit->object.sha1,
+                               &payload, &signature) <= 0)
+               goto out;
+       status = verify_signed_buffer(payload.buf, payload.len,
+                                     signature.buf, signature.len,
+                                     &gpg_output);
+       if (status && !gpg_output.len)
+               goto out;
+       ctx->signature.gpg_output = strbuf_detach(&gpg_output, NULL);
+       parse_signature_lines(ctx);
+
+ out:
+       strbuf_release(&gpg_output);
+       strbuf_release(&payload);
+       strbuf_release(&signature);
+}
+
+
 static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                                void *context)
 {
@@ -974,6 +1034,30 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                return 0;
        }
 
+       if (placeholder[0] == 'G') {
+               if (!c->commit_signature_parsed)
+                       parse_commit_signature(c);
+               switch (placeholder[1]) {
+               case 'G':
+                       if (c->signature.gpg_output)
+                               strbuf_addstr(sb, c->signature.gpg_output);
+                       break;
+               case '?':
+                       switch (c->signature.good_bad) {
+                       case 'G':
+                       case 'B':
+                               strbuf_addch(sb, c->signature.good_bad);
+                       }
+                       break;
+               case 'S':
+                       if (c->signature.signer)
+                               strbuf_addstr(sb, c->signature.signer);
+                       break;
+               }
+               return 2;
+       }
+
+
        /* For the rest we have to parse the commit header. */
        if (!c->commit_header_parsed)
                parse_commit_header(c);
@@ -1114,6 +1198,8 @@ void format_commit_message(const struct commit *commit,
 
        if (context.message != commit->buffer)
                free(context.message);
+       free(context.signature.gpg_output);
+       free(context.signature.signer);
 }
 
 static void pp_header(const struct pretty_print_context *pp,