diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 6109ef09aa..417b638cd8 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -153,6 +153,9 @@ endif::git-rev-list[] and "N" for no signature - '%GS': show the name of the signer for a signed commit - '%GK': show the key used to sign a signed commit +- '%GF': show the fingerprint of the key used to sign a signed commit +- '%GP': show the fingerprint of the primary key whose subkey was used + to sign a signed commit - '%gD': reflog selector, e.g., `refs/stash@{1}` or `refs/stash@{2 minutes ago`}; the format follows the rules described for the `-g` option. The portion before the `@` is the refname as diff --git a/gpg-interface.c b/gpg-interface.c index d72a43b774..8ed274533f 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -73,31 +73,43 @@ void signature_check_clear(struct signature_check *sigc) FREE_AND_NULL(sigc->gpg_status); FREE_AND_NULL(sigc->signer); FREE_AND_NULL(sigc->key); + FREE_AND_NULL(sigc->fingerprint); + FREE_AND_NULL(sigc->primary_key_fingerprint); } /* An exclusive status -- only one of them can appear in output */ #define GPG_STATUS_EXCLUSIVE (1<<0) +/* The status includes key identifier */ +#define GPG_STATUS_KEYID (1<<1) +/* The status includes user identifier */ +#define GPG_STATUS_UID (1<<2) +/* The status includes key fingerprints */ +#define GPG_STATUS_FINGERPRINT (1<<3) + +/* Short-hand for standard exclusive *SIG status with keyid & UID */ +#define GPG_STATUS_STDSIG (GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID) static struct { char result; const char *check; unsigned int flags; } sigcheck_gpg_status[] = { - { 'G', "GOODSIG ", GPG_STATUS_EXCLUSIVE }, - { 'B', "BADSIG ", GPG_STATUS_EXCLUSIVE }, + { 'G', "GOODSIG ", GPG_STATUS_STDSIG }, + { 'B', "BADSIG ", GPG_STATUS_STDSIG }, { 'U', "TRUST_NEVER", 0 }, { 'U', "TRUST_UNDEFINED", 0 }, - { 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE }, - { 'X', "EXPSIG ", GPG_STATUS_EXCLUSIVE }, - { 'Y', "EXPKEYSIG ", GPG_STATUS_EXCLUSIVE }, - { 'R', "REVKEYSIG ", GPG_STATUS_EXCLUSIVE }, + { 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID }, + { 'X', "EXPSIG ", GPG_STATUS_STDSIG }, + { 'Y', "EXPKEYSIG ", GPG_STATUS_STDSIG }, + { 'R', "REVKEYSIG ", GPG_STATUS_STDSIG }, + { 0, "VALIDSIG ", GPG_STATUS_FINGERPRINT }, }; static void parse_gpg_output(struct signature_check *sigc) { const char *buf = sigc->gpg_status; const char *line, *next; - int i; + int i, j; int seen_exclusive_status = 0; /* Iterate over all lines */ @@ -116,20 +128,39 @@ static void parse_gpg_output(struct signature_check *sigc) goto found_duplicate_status; } - sigc->result = sigcheck_gpg_status[i].result; - /* The trust messages are not followed by key/signer information */ - if (sigc->result != 'U') { + if (sigcheck_gpg_status[i].result) + sigc->result = sigcheck_gpg_status[i].result; + /* Do we have key information? */ + if (sigcheck_gpg_status[i].flags & GPG_STATUS_KEYID) { next = strchrnul(line, ' '); free(sigc->key); sigc->key = xmemdupz(line, next - line); - /* The ERRSIG message is not followed by signer information */ - if (*next && sigc->result != 'E') { + /* Do we have signer information? */ + if (*next && (sigcheck_gpg_status[i].flags & GPG_STATUS_UID)) { line = next + 1; next = strchrnul(line, '\n'); free(sigc->signer); sigc->signer = xmemdupz(line, next - line); } } + /* Do we have fingerprint? */ + if (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) { + next = strchrnul(line, ' '); + free(sigc->fingerprint); + sigc->fingerprint = xmemdupz(line, next - line); + + /* Skip interim fields */ + for (j = 9; j > 0; j--) { + if (!*next) + break; + line = next + 1; + next = strchrnul(line, ' '); + } + + next = strchrnul(line, '\n'); + free(sigc->primary_key_fingerprint); + sigc->primary_key_fingerprint = xmemdupz(line, next - line); + } break; } @@ -147,6 +178,8 @@ static void parse_gpg_output(struct signature_check *sigc) */ sigc->result = 'E'; /* Clear partial data to avoid confusion */ + FREE_AND_NULL(sigc->primary_key_fingerprint); + FREE_AND_NULL(sigc->fingerprint); FREE_AND_NULL(sigc->signer); FREE_AND_NULL(sigc->key); } diff --git a/gpg-interface.h b/gpg-interface.h index acf50c4610..3e624ec289 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -23,6 +23,8 @@ struct signature_check { char result; char *signer; char *key; + char *fingerprint; + char *primary_key_fingerprint; }; void signature_check_clear(struct signature_check *sigc); diff --git a/pretty.c b/pretty.c index 8ca29e9281..b83a3ecd23 100644 --- a/pretty.c +++ b/pretty.c @@ -1256,6 +1256,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ if (c->signature_check.key) strbuf_addstr(sb, c->signature_check.key); break; + case 'F': + if (c->signature_check.fingerprint) + strbuf_addstr(sb, c->signature_check.fingerprint); + break; + case 'P': + if (c->signature_check.primary_key_fingerprint) + strbuf_addstr(sb, c->signature_check.primary_key_fingerprint); + break; default: return 0; } diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 180f0be914..19ccae2869 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -175,8 +175,9 @@ test_expect_success GPG 'show good signature with custom format' ' G 13B6F51ECDDE430D C O Mitter + 73D758744BE721698EC54E8713B6F51ECDDE430D EOF - git log -1 --format="%G?%n%GK%n%GS" sixth-signed >actual && + git log -1 --format="%G?%n%GK%n%GS%n%GF" sixth-signed >actual && test_cmp expect actual ' @@ -185,8 +186,9 @@ test_expect_success GPG 'show bad signature with custom format' ' B 13B6F51ECDDE430D C O Mitter + EOF - git log -1 --format="%G?%n%GK%n%GS" $(cat forged1.commit) >actual && + git log -1 --format="%G?%n%GK%n%GS%n%GF" $(cat forged1.commit) >actual && test_cmp expect actual ' @@ -195,8 +197,9 @@ test_expect_success GPG 'show untrusted signature with custom format' ' U 61092E85B7227189 Eris Discordia + D4BE22311AD3131E5EDA29A461092E85B7227189 EOF - git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual && + git log -1 --format="%G?%n%GK%n%GS%n%GF" eighth-signed-alt >actual && test_cmp expect actual ' @@ -205,8 +208,9 @@ test_expect_success GPG 'show unknown signature with custom format' ' E 61092E85B7227189 + EOF - GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual && + GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS%n%GF" eighth-signed-alt >actual && test_cmp expect actual ' @@ -215,8 +219,9 @@ test_expect_success GPG 'show lack of signature with custom format' ' N + EOF - git log -1 --format="%G?%n%GK%n%GS" seventh-unsigned >actual && + git log -1 --format="%G?%n%GK%n%GS%n%GF" seventh-unsigned >actual && test_cmp expect actual ' @@ -255,8 +260,9 @@ test_expect_success GPG 'show double signature with custom format' ' E + EOF - git log -1 --format="%G?%n%GK%n%GS" $(cat double-commit.commit) >actual && + git log -1 --format="%G?%n%GK%n%GS%n%GF" $(cat double-commit.commit) >actual && test_cmp expect actual '