git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 15/17] log: --show-signature

From
Junio C Hamano <gitster@pobox.com>
Date
Nov 9, 2011, 01:02 UTC
Message-ID
<1320800523-5407-16-git-send-email-gitster@pobox.com>
In-Reply-To
<1320800523-5407-1-git-send-email-gitster@pobox.com>

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 <gitster@pobox.com>
---
 commit.c   |   44 ++++++++++++++++++++++++++++++++++++++++++++
 commit.h   |    2 ++
 log-tree.c |   39 +++++++++++++++++++++++++++++++++++++++
 revision.c |    2 ++
 revision.h |    1 +
 5 files changed, 88 insertions(+), 0 deletions(-)
diff --git a/commit.c b/commit.c
index a5c53b3..7fb1830 100644
--- a/commit.c
+++ b/commit.c
@@ -877,6 +877,50 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
 	return 0;
 }
 
+int parse_signed_commit(const unsigned char *sha1,
+			struct strbuf *payload, struct strbuf *signature)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buffer = read_sha1_file(sha1, &type, &size);
+	int in_signature, saw_signature = -1;
+	char *line, *tail;
+
+	if (!buffer || type != OBJ_COMMIT)
+		goto cleanup;
+
+	line = buffer;
+	tail = buffer + size;
+	in_signature = 0;
+	saw_signature = 0;
+	while (line < tail) {
+		const char *sig = NULL;
+		char *next = memchr(line, '\n', tail - line);
+
+		next = next ? next + 1 : tail;
+		if (in_signature && line[0] == ' ')
+			sig = line + 1;
+		else if (!prefixcmp(line, gpg_sig_header) &&
+			 line[gpg_sig_header_len] == ' ')
+			sig = line + gpg_sig_header_len + 1;
+		if (sig) {
+			strbuf_add(signature, sig, next - sig);
+			saw_signature = 1;
+			in_signature = 1;
+		} else {
+			if (*line == '\n')
+				/* dump the whole remainder of the buffer */
+				next = tail;
+			strbuf_add(payload, line, next - line);
+			in_signature = 0;
+		}
+		line = next;
+	}
+ cleanup:
+	free(buffer);
+	return saw_signature;
+}
+
 static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
 {
 	struct merge_remote_desc *desc;
diff --git a/commit.h b/commit.h
index d2c3e65..6107648 100644
--- a/commit.h
+++ b/commit.h
@@ -218,4 +218,6 @@ struct merge_remote_desc {
  */
 struct commit *get_merge_parent(const char *name);
 
+extern int parse_signed_commit(const unsigned char *sha1,
+			       struct strbuf *message, struct strbuf *signature);
 #endif /* COMMIT_H */
diff --git a/log-tree.c b/log-tree.c
index e7694a3..142ba51 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;
 
diff --git a/revision.c b/revision.c
index 8764dde..064e351 100644
--- a/revision.c
+++ b/revision.c
@@ -1469,6 +1469,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 		revs->show_notes = 1;
 		revs->show_notes_given = 1;
 		revs->notes_opt.use_default_notes = 1;
+	} else if (!strcmp(arg, "--show-signature")) {
+		revs->show_signature = 1;
 	} else if (!prefixcmp(arg, "--show-notes=") ||
 		   !prefixcmp(arg, "--notes=")) {
 		struct strbuf buf = STRBUF_INIT;
diff --git a/revision.h b/revision.h
index 6aa53d1..b8e9223 100644
--- a/revision.h
+++ b/revision.h
@@ -110,6 +110,7 @@ struct rev_info {
 			show_merge:1,
 			show_notes:1,
 			show_notes_given:1,
+			show_signature:1,
 			pretty_given:1,
 			abbrev_commit:1,
 			abbrev_commit_given:1,
-- 
1.7.8.rc1.82.g90e080
Previous: Junio C HamanoNext: Junio C Hamano
Message 16 of 21 in “Pulling signed tags”
  1. 00/17 Pulling signed tagsJunio C Hamano, Nov 9, 2011
  2. 01/17 Split GPG interface into its own helper libraryJunio C Hamano, Nov 9, 2011
  3. 02/17 fetch: do not store peeled tag object names in FETCH_HEADJunio C Hamano, Nov 9, 2011
  4. 03/17 merge: notice local merging of tags and keep it unwrappedJunio C Hamano, Nov 9, 2011
  5. 04/17 fetch: allow "git fetch $there v1.0" to fetch a tagJunio C Hamano, Nov 9, 2011
  6. 05/17 refs DWIMmery: use the same rule for both "git fetch" and othersJunio C Hamano, Nov 9, 2011
  7. 06/17 fmt-merge-msg: avoid early returnsJunio C Hamano, Nov 9, 2011
  8. 07/17 fmt-merge-msg: package options into a structureJunio C Hamano, Nov 9, 2011
  9. 08/17 fmt-merge-msg: Add contents of merged tag in the merge messageJunio C Hamano, Nov 9, 2011
  10. 09/17 merge: make usage of commit->util more extensibleJunio C Hamano, Nov 9, 2011
  11. 10/17 merge: record tag objects without peeling in MERGE_HEADJunio C Hamano, Nov 9, 2011
  12. 11/17 commit: copy merged signed tags to headers of merge commitJunio C Hamano, Nov 9, 2011
  13. 12/17 merge: force edit mode when merging a tag objectJunio C Hamano, Nov 9, 2011
  14. 13/17 commit: teach --amend to carry forward extra headersJunio C Hamano, Nov 9, 2011
  15. 14/17 commit: teach --gpg-sign optionJunio C Hamano, Nov 9, 2011
  16. 15/17 log: --show-signatureJunio C Hamano, Nov 9, 2011
  17. 16/17 test "commit -S" and "log --show-signature"Junio C Hamano, Nov 9, 2011
  18. 17/17 pretty: %G[?GS] placeholdersJunio C Hamano, Nov 9, 2011
  19. Robin H. JohnsonNov 9, 2011
  20. 18/17 request-pull: use the annotated tag contentsJunio C Hamano, Nov 9, 2011
  21. Junio C HamanoNov 9, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.