Skip to content

Commit 81ebc54

Browse files
committed
Merge branch 'ks/ref-filter-signature'
The "git for-each-ref" family of commands learned placeholders related to GPG signature verification. * ks/ref-filter-signature: ref-filter: add new "signature" atom t/lib-gpg: introduce new prereq GPG2
2 parents aa9166b + 26c9c03 commit 81ebc54

File tree

5 files changed

+370
-2
lines changed

5 files changed

+370
-2
lines changed

Documentation/git-for-each-ref.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,33 @@ symref::
221221
`:lstrip` and `:rstrip` options in the same way as `refname`
222222
above.
223223

224+
signature::
225+
The GPG signature of a commit.
226+
227+
signature:grade::
228+
Show "G" for a good (valid) signature, "B" for a bad
229+
signature, "U" for a good signature with unknown validity, "X"
230+
for a good signature that has expired, "Y" for a good
231+
signature made by an expired key, "R" for a good signature
232+
made by a revoked key, "E" if the signature cannot be
233+
checked (e.g. missing key) and "N" for no signature.
234+
235+
signature:signer::
236+
The signer of the GPG signature of a commit.
237+
238+
signature:key::
239+
The key of the GPG signature of a commit.
240+
241+
signature:fingerprint::
242+
The fingerprint of the GPG signature of a commit.
243+
244+
signature:primarykeyfingerprint::
245+
The primary key fingerprint of the GPG signature of a commit.
246+
247+
signature:trustlevel::
248+
The trust level of the GPG signature of a commit. Possible
249+
outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`.
250+
224251
worktreepath::
225252
The absolute path to the worktree in which the ref is checked
226253
out, if it is checked out in any linked worktree. Empty string

ref-filter.c

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ enum atom_type {
150150
ATOM_BODY,
151151
ATOM_TRAILERS,
152152
ATOM_CONTENTS,
153+
ATOM_SIGNATURE,
153154
ATOM_RAW,
154155
ATOM_UPSTREAM,
155156
ATOM_PUSH,
@@ -215,6 +216,10 @@ static struct used_atom {
215216
struct email_option {
216217
enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;
217218
} email_option;
219+
struct {
220+
enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
221+
S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
222+
} signature;
218223
struct refname_atom refname;
219224
char *head;
220225
} u;
@@ -407,8 +412,37 @@ static int subject_atom_parser(struct ref_format *format UNUSED,
407412
return 0;
408413
}
409414

410-
static int trailers_atom_parser(struct ref_format *format UNUSED,
411-
struct used_atom *atom,
415+
static int parse_signature_option(const char *arg)
416+
{
417+
if (!arg)
418+
return S_BARE;
419+
else if (!strcmp(arg, "signer"))
420+
return S_SIGNER;
421+
else if (!strcmp(arg, "grade"))
422+
return S_GRADE;
423+
else if (!strcmp(arg, "key"))
424+
return S_KEY;
425+
else if (!strcmp(arg, "fingerprint"))
426+
return S_FINGERPRINT;
427+
else if (!strcmp(arg, "primarykeyfingerprint"))
428+
return S_PRI_KEY_FP;
429+
else if (!strcmp(arg, "trustlevel"))
430+
return S_TRUST_LEVEL;
431+
return -1;
432+
}
433+
434+
static int signature_atom_parser(struct ref_format *format UNUSED,
435+
struct used_atom *atom,
436+
const char *arg, struct strbuf *err)
437+
{
438+
int opt = parse_signature_option(arg);
439+
if (opt < 0)
440+
return err_bad_arg(err, "signature", arg);
441+
atom->u.signature.option = opt;
442+
return 0;
443+
}
444+
445+
static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom,
412446
const char *arg, struct strbuf *err)
413447
{
414448
atom->u.contents.trailer_opts.no_divider = 1;
@@ -668,6 +702,7 @@ static struct {
668702
[ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
669703
[ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
670704
[ATOM_CONTENTS] = { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
705+
[ATOM_SIGNATURE] = { "signature", SOURCE_OBJ, FIELD_STR, signature_atom_parser },
671706
[ATOM_RAW] = { "raw", SOURCE_OBJ, FIELD_STR, raw_atom_parser },
672707
[ATOM_UPSTREAM] = { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
673708
[ATOM_PUSH] = { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
@@ -1405,6 +1440,92 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
14051440
}
14061441
}
14071442

1443+
static void grab_signature(struct atom_value *val, int deref, struct object *obj)
1444+
{
1445+
int i;
1446+
struct commit *commit = (struct commit *) obj;
1447+
struct signature_check sigc = { 0 };
1448+
int signature_checked = 0;
1449+
1450+
for (i = 0; i < used_atom_cnt; i++) {
1451+
struct used_atom *atom = &used_atom[i];
1452+
const char *name = atom->name;
1453+
struct atom_value *v = &val[i];
1454+
int opt;
1455+
1456+
if (!!deref != (*name == '*'))
1457+
continue;
1458+
if (deref)
1459+
name++;
1460+
1461+
if (!skip_prefix(name, "signature", &name) ||
1462+
(*name && *name != ':'))
1463+
continue;
1464+
if (!*name)
1465+
name = NULL;
1466+
else
1467+
name++;
1468+
1469+
opt = parse_signature_option(name);
1470+
if (opt < 0)
1471+
continue;
1472+
1473+
if (!signature_checked) {
1474+
check_commit_signature(commit, &sigc);
1475+
signature_checked = 1;
1476+
}
1477+
1478+
switch (opt) {
1479+
case S_BARE:
1480+
v->s = xstrdup(sigc.output ? sigc.output: "");
1481+
break;
1482+
case S_SIGNER:
1483+
v->s = xstrdup(sigc.signer ? sigc.signer : "");
1484+
break;
1485+
case S_GRADE:
1486+
switch (sigc.result) {
1487+
case 'G':
1488+
switch (sigc.trust_level) {
1489+
case TRUST_UNDEFINED:
1490+
case TRUST_NEVER:
1491+
v->s = xstrfmt("%c", (char)'U');
1492+
break;
1493+
default:
1494+
v->s = xstrfmt("%c", (char)'G');
1495+
break;
1496+
}
1497+
break;
1498+
case 'B':
1499+
case 'E':
1500+
case 'N':
1501+
case 'X':
1502+
case 'Y':
1503+
case 'R':
1504+
v->s = xstrfmt("%c", (char)sigc.result);
1505+
break;
1506+
}
1507+
break;
1508+
case S_KEY:
1509+
v->s = xstrdup(sigc.key ? sigc.key : "");
1510+
break;
1511+
case S_FINGERPRINT:
1512+
v->s = xstrdup(sigc.fingerprint ?
1513+
sigc.fingerprint : "");
1514+
break;
1515+
case S_PRI_KEY_FP:
1516+
v->s = xstrdup(sigc.primary_key_fingerprint ?
1517+
sigc.primary_key_fingerprint : "");
1518+
break;
1519+
case S_TRUST_LEVEL:
1520+
v->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));
1521+
break;
1522+
}
1523+
}
1524+
1525+
if (signature_checked)
1526+
signature_check_clear(&sigc);
1527+
}
1528+
14081529
static void find_subpos(const char *buf,
14091530
const char **sub, size_t *sublen,
14101531
const char **body, size_t *bodylen,
@@ -1598,6 +1719,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
15981719
grab_sub_body_contents(val, deref, data);
15991720
grab_person("author", val, deref, buf);
16001721
grab_person("committer", val, deref, buf);
1722+
grab_signature(val, deref, obj);
16011723
break;
16021724
case OBJ_TREE:
16031725
/* grab_tree_values(val, deref, obj, buf, sz); */

t/lib-gpg.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,27 @@ test_lazy_prereq GPG '
5151
esac
5252
'
5353

54+
test_lazy_prereq GPG2 '
55+
gpg_version=$(gpg --version 2>&1)
56+
test $? != 127 || exit 1
57+
58+
case "$gpg_version" in
59+
"gpg (GnuPG) "[01].*)
60+
say "This test requires a GPG version >= v2.0.0"
61+
exit 1
62+
;;
63+
*)
64+
(gpgconf --kill all || : ) &&
65+
gpg --homedir "${GNUPGHOME}" --import \
66+
"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
67+
gpg --homedir "${GNUPGHOME}" --import-ownertrust \
68+
"$TEST_DIRECTORY"/lib-gpg/ownertrust &&
69+
gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \
70+
71+
;;
72+
esac
73+
'
74+
5475
test_lazy_prereq GPGSM '
5576
test_have_prereq GPG &&
5677
# Available key info:

0 commit comments

Comments
 (0)