From 0c37f1fce6cb7997dbb2703d35eef0dfd86c5070 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 18 Oct 2011 15:53:23 -0700 Subject: log: --show-signature This teaches the "log" family of commands to pass the GPG signature in the commit objects to "gpg --verify" via the verify_signed_buffer() interface used to verify signed tag objects. E.g. $ git show --show-signature -s HEAD shows GPG output in the header part of the output. Signed-off-by: Junio C Hamano --- log-tree.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'log-tree.c') diff --git a/log-tree.c b/log-tree.c index e7694a3a4c..142ba51427 100644 --- a/log-tree.c +++ b/log-tree.c @@ -8,6 +8,7 @@ #include "refs.h" #include "string-list.h" #include "color.h" +#include "gpg-interface.h" struct decoration name_decoration = { "object names" }; @@ -403,6 +404,41 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, *extra_headers_p = extra_headers; } +static void show_signature(struct rev_info *opt, struct commit *commit) +{ + struct strbuf payload = STRBUF_INIT; + struct strbuf signature = STRBUF_INIT; + struct strbuf gpg_output = STRBUF_INIT; + int status; + const char *color, *reset, *bol, *eol; + + if (parse_signed_commit(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) + strbuf_addstr(&gpg_output, "No signature\n"); + + color = diff_get_color_opt(&opt->diffopt, + status ? DIFF_WHITESPACE : DIFF_FRAGINFO); + reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET); + + bol = gpg_output.buf; + while (*bol) { + eol = strchrnul(bol, '\n'); + printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset, + *eol ? "\n" : ""); + bol = (*eol) ? (eol + 1) : eol; + } + + out: + strbuf_release(&gpg_output); + strbuf_release(&payload); + strbuf_release(&signature); +} + void show_log(struct rev_info *opt) { struct strbuf msgbuf = STRBUF_INIT; @@ -514,6 +550,9 @@ void show_log(struct rev_info *opt) } } + if (opt->show_signature) + show_signature(opt, commit); + if (!commit->buffer) return; -- cgit v1.2.1 From c6b3ec41e2efdd9e05118c2e99ad70f0e0629873 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 4 Jan 2012 13:48:45 -0800 Subject: log-tree.c: small refactor in show_signature() The next patch needs to show the result of signature verification on a mergetag extended header in a way similar to how embedded signature for the commit object itself is shown. Separate out the logic to go through the message lines and show them in the "error" color (highlighted) or the "correct" color (dim). Signed-off-by: Junio C Hamano --- log-tree.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'log-tree.c') diff --git a/log-tree.c b/log-tree.c index 142ba51427..005c5a51c0 100644 --- a/log-tree.c +++ b/log-tree.c @@ -404,13 +404,27 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, *extra_headers_p = extra_headers; } +static void show_sig_lines(struct rev_info *opt, int status, const char *bol) +{ + const char *color, *reset, *eol; + + color = diff_get_color_opt(&opt->diffopt, + status ? DIFF_WHITESPACE : DIFF_FRAGINFO); + reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET); + while (*bol) { + eol = strchrnul(bol, '\n'); + printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset, + *eol ? "\n" : ""); + bol = (*eol) ? (eol + 1) : eol; + } +} + static void show_signature(struct rev_info *opt, struct commit *commit) { struct strbuf payload = STRBUF_INIT; struct strbuf signature = STRBUF_INIT; struct strbuf gpg_output = STRBUF_INIT; int status; - const char *color, *reset, *bol, *eol; if (parse_signed_commit(commit->object.sha1, &payload, &signature) <= 0) goto out; @@ -421,17 +435,7 @@ static void show_signature(struct rev_info *opt, struct commit *commit) if (status && !gpg_output.len) strbuf_addstr(&gpg_output, "No signature\n"); - color = diff_get_color_opt(&opt->diffopt, - status ? DIFF_WHITESPACE : DIFF_FRAGINFO); - reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET); - - bol = gpg_output.buf; - while (*bol) { - eol = strchrnul(bol, '\n'); - printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset, - *eol ? "\n" : ""); - bol = (*eol) ? (eol + 1) : eol; - } + show_sig_lines(opt, status, gpg_output.buf); out: strbuf_release(&gpg_output); -- cgit v1.2.1 From 824958e50b250e957998626a21763603ca30e832 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 4 Jan 2012 13:51:28 -0800 Subject: log-tree: show mergetag in log --show-signature output A commit object that merges a signed tag records the "mergetag" extended header. Check the validity of the GPG signature on it, and show it in a way similar to how "gpgsig" extended header is shown. Signed-off-by: Junio C Hamano --- log-tree.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) (limited to 'log-tree.c') diff --git a/log-tree.c b/log-tree.c index 005c5a51c0..61a12a7cb0 100644 --- a/log-tree.c +++ b/log-tree.c @@ -443,6 +443,78 @@ static void show_signature(struct rev_info *opt, struct commit *commit) strbuf_release(&signature); } +static int which_parent(const unsigned char *sha1, const struct commit *commit) +{ + int nth; + const struct commit_list *parent; + + for (nth = 0, parent = commit->parents; parent; parent = parent->next) { + if (!hashcmp(parent->item->object.sha1, sha1)) + return nth; + nth++; + } + return -1; +} + +static void show_one_mergetag(struct rev_info *opt, + struct commit_extra_header *extra, + struct commit *commit) +{ + unsigned char sha1[20]; + struct tag *tag; + struct strbuf verify_message; + int status, nth; + size_t payload_size, gpg_message_offset; + + hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), sha1); + tag = lookup_tag(sha1); + if (!tag) + return; /* error message already given */ + + strbuf_init(&verify_message, 256); + if (parse_tag_buffer(tag, extra->value, extra->len)) + strbuf_addstr(&verify_message, "malformed mergetag\n"); + else if ((nth = which_parent(tag->tagged->sha1, commit)) < 0) + strbuf_addf(&verify_message, "tag %s names a non-parent %s\n", + tag->tag, tag->tagged->sha1); + else + strbuf_addf(&verify_message, + "parent #%d, tagged '%s'\n", nth + 1, tag->tag); + 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; + + show_sig_lines(opt, status, verify_message.buf); + strbuf_release(&verify_message); +} + +static void show_mergetag(struct rev_info *opt, struct commit *commit) +{ + struct commit_extra_header *extra, *to_free; + + to_free = read_commit_extra_headers(commit, NULL); + for (extra = to_free; extra; extra = extra->next) { + if (strcmp(extra->key, "mergetag")) + continue; /* not a merge tag */ + show_one_mergetag(opt, extra, commit); + } + free_commit_extra_headers(to_free); +} + void show_log(struct rev_info *opt) { struct strbuf msgbuf = STRBUF_INIT; @@ -554,8 +626,10 @@ void show_log(struct rev_info *opt) } } - if (opt->show_signature) + if (opt->show_signature) { show_signature(opt, commit); + show_mergetag(opt, commit); + } if (!commit->buffer) return; -- cgit v1.2.1 From d041ffa55a69cb6cdc3b160dc181c7e59b3bd4bb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 4 Jan 2012 16:23:12 -0800 Subject: log --show-signature: reword the common two-head merge case While identifying the commit merged to our history as "parent #2" is technically correct, we will never say "parent #1" (as that is the tip of our history before the merge is made), and we rarely would say "parent #3" (which would mean the merge is an octopus), especially when responding to a request to pull a signed tag. Treat the most common case to merge a single commit specially, and just say "merged tag ''" instead. Signed-off-by: Junio C Hamano --- log-tree.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'log-tree.c') diff --git a/log-tree.c b/log-tree.c index 61a12a7cb0..3cf569ce7e 100644 --- a/log-tree.c +++ b/log-tree.c @@ -456,6 +456,13 @@ static int which_parent(const unsigned char *sha1, const struct commit *commit) return -1; } +static int is_common_merge(const struct commit *commit) +{ + return (commit->parents + && commit->parents->next + && !commit->parents->next->next); +} + static void show_one_mergetag(struct rev_info *opt, struct commit_extra_header *extra, struct commit *commit) @@ -474,6 +481,11 @@ static void show_one_mergetag(struct rev_info *opt, strbuf_init(&verify_message, 256); if (parse_tag_buffer(tag, extra->value, extra->len)) strbuf_addstr(&verify_message, "malformed mergetag\n"); + else if (is_common_merge(commit) && + !hashcmp(tag->tagged->sha1, + commit->parents->next->item->object.sha1)) + strbuf_addf(&verify_message, + "merged tag '%s'\n", tag->tag); else if ((nth = which_parent(tag->tagged->sha1, commit)) < 0) strbuf_addf(&verify_message, "tag %s names a non-parent %s\n", tag->tag, tag->tagged->sha1); -- cgit v1.2.1