{"thread":{"id":"59055","subject":"[PATCH] ref-filter: add new atom \"signature\" atom","startedAt":"2023-01-09T09:09:17Z","lastAt":"2023-01-09T13:02:54Z","messageCount":3,"participants":["nsengaw4c via GitGitGadget","Christian Couder","NSENGIYUMVA WILBERFORCE"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"469955","messageId":"pull.1428.git.git.1673254961028.gitgitgadget@gmail.com","threadId":"59055","inReplyTo":null,"subject":"[PATCH] ref-filter: add new atom \"signature\" atom","fromName":"nsengaw4c via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2023-01-09T09:02:40Z","receivedAt":"2023-01-09T09:09:17Z","isPatch":true,"sender":{"key":"name:nsengaw4c","avatar":null},"body":"From: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n\nThis commit duplicates the code for `signature` atom from pretty.c\nto ref-filter.c. This feature will help to get rid of current duplicate\nimplementation of `signature` atom when unifying implemenations by\nusing ref-filter logic everywhere when ref-filter can do everything\npretty is doing.\n\nAdd \"signature\" atom with `grade`, `signer`, `key`,\n`fingerprint`, `primarykeyfingerprint`, `trustlevel` as arguments.\nThis code and its documentation are inspired by how the %GG, %G?,\n%GS, %GK, %GF, %GP, and %GT pretty formats were implemented.\n\nCo-authored-by: Hariom Verma <hariom18599@gmail.com>\nCo-authored-by: Jaydeep Das <jaydeepjd.8914@gmail.com>\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Hariom Verma <hariom18599@gmail.com>\nSigned-off-by: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n---\n    ref-filter: add new atom \"signature\" atom\n    \n    This commit duplicates the code for signature atom from pretty.c to\n    ref-filter.c. This feature will help to get rid of current duplicate\n    implementation of signature atom when unifying implemenations by using\n    ref-filter logic everywhere when ref-filter can do everything pretty is\n    doing.\n    \n    Add \"signature\" atom with grade, signer, key, fingerprint,\n    primarykeyfingerprint, trustlevel as arguments. This code and its\n    documentation are inspired by how the %GG, %G?, %GS, %GK, %GF, %GP, and\n    %GT pretty formats were implemented.\n    \n    Co-authored-by: Hariom Verma hariom18599@gmail.com Co-authored-by:\n    Jaydeep Das jaydeepjd.8914@gmail.com Mentored-by: Christian Couder\n    chriscool@tuxfamily.org Mentored-by: Hariom Verma hariom18599@gmail.com\n    Signed-off-by: Nsengiyumva Wilberforce nsengiyumvawilberforce@gmail.com\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1428%2Fnsengiyumva-wilberforce%2Fsignature10-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1428/nsengiyumva-wilberforce/signature10-v1\nPull-Request: https://github.com/git/git/pull/1428\n\n Documentation/git-for-each-ref.txt |  27 ++++++\n ref-filter.c                       | 101 +++++++++++++++++++++++\n t/t6300-for-each-ref.sh            | 127 +++++++++++++++++++++++++++++\n 3 files changed, 255 insertions(+)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 6da899c6296..9a0be85368b 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -212,6 +212,33 @@ symref::\n \t`:lstrip` and `:rstrip` options in the same way as `refname`\n \tabove.\n \n+signature::\n+\tThe GPG signature of a commit.\n+\n+signature:grade::\n+\tShow \"G\" for a good (valid) signature, \"B\" for a bad\n+\tsignature, \"U\" for a good signature with unknown validity, \"X\"\n+\tfor a good signature that has expired, \"Y\" for a good\n+\tsignature made by an expired key, \"R\" for a good signature\n+\tmade by a revoked key, \"E\" if the signature cannot be\n+\tchecked (e.g. missing key) and \"N\" for no signature.\n+\n+signature:signer::\n+\tThe signer of the GPG signature of a commit.\n+\n+signature:key::\n+\tThe key of the GPG signature of a commit.\n+\n+signature:fingerprint::\n+\tThe fingerprint of the GPG signature of a commit.\n+\n+signature:primarykeyfingerprint::\n+\tThe Primary Key fingerprint of the GPG signature of a commit.\n+\n+signature:trustlevel::\n+\tThe Trust level of the GPG signature of a commit. Possible\n+\toutputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`.\n+\n worktreepath::\n \tThe absolute path to the worktree in which the ref is checked\n \tout, if it is checked out in any linked worktree. Empty string\ndiff --git a/ref-filter.c b/ref-filter.c\nindex a24324123e7..0cba756b186 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -144,6 +144,7 @@ enum atom_type {\n \tATOM_BODY,\n \tATOM_TRAILERS,\n \tATOM_CONTENTS,\n+\tATOM_SIGNATURE,\n \tATOM_RAW,\n \tATOM_UPSTREAM,\n \tATOM_PUSH,\n@@ -208,6 +209,10 @@ static struct used_atom {\n \t\tstruct email_option {\n \t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n \t\t} email_option;\n+\t\tstruct {\n+\t\t\tenum { S_BARE, S_GRADE, S_SIGNER, S_KEY,\n+\t\t\t       S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL} option;\n+\t\t} signature;\n \t\tstruct refname_atom refname;\n \t\tchar *head;\n \t} u;\n@@ -394,6 +399,34 @@ static int subject_atom_parser(struct ref_format *format, struct used_atom *atom\n \treturn 0;\n }\n \n+static int parse_signature_option(const char *arg)\n+{\n+\tif (!arg)\n+\t\treturn S_BARE;\n+\telse if (!strcmp(arg, \"signer\"))\n+\t\treturn S_SIGNER;\n+\telse if (!strcmp(arg, \"grade\"))\n+\t\treturn S_GRADE;\n+\telse if (!strcmp(arg, \"key\"))\n+\t\treturn S_KEY;\n+\telse if (!strcmp(arg, \"fingerprint\"))\n+\t\treturn S_FINGERPRINT;\n+\telse if (!strcmp(arg, \"primarykeyfingerprint\"))\n+\t\treturn S_PRI_KEY_FP;\n+\telse if (!strcmp(arg, \"trustlevel\"))\n+\t\treturn S_TRUST_LEVEL;\n+\treturn -1;\n+}\n+\n+static int signature_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom,\n+\t\t\t       const char *arg, struct strbuf *err){\n+\tint opt = parse_signature_option(arg);\n+\tif (opt < 0)\n+\t\treturn err_bad_arg(err, \"signature\", arg);\n+\tatom->u.signature.option = opt;\n+\treturn 0;\n+}\n+\n static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom,\n \t\t\t\tconst char *arg, struct strbuf *err)\n {\n@@ -631,6 +664,7 @@ static struct {\n \t[ATOM_BODY] = { \"body\", SOURCE_OBJ, FIELD_STR, body_atom_parser },\n \t[ATOM_TRAILERS] = { \"trailers\", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },\n \t[ATOM_CONTENTS] = { \"contents\", SOURCE_OBJ, FIELD_STR, contents_atom_parser },\n+\t[ATOM_SIGNATURE] = { \"signature\", SOURCE_OBJ, FIELD_STR, signature_atom_parser },\n \t[ATOM_RAW] = { \"raw\", SOURCE_OBJ, FIELD_STR, raw_atom_parser },\n \t[ATOM_UPSTREAM] = { \"upstream\", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },\n \t[ATOM_PUSH] = { \"push\", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },\n@@ -1362,6 +1396,72 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t}\n }\n \n+static void grab_signature(struct atom_value *val, int deref, struct object *obj)\n+{\n+\tint i;\n+\tstruct commit *commit = (struct commit *) obj;\n+\tstruct signature_check sigc = { 0 };\n+\n+\tcheck_commit_signature(commit, &sigc);\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tstruct used_atom *atom = &used_atom[i];\n+\t\tconst char *name = atom->name;\n+\t\tstruct atom_value *v = &val[i];\n+\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\n+\t\tif (!skip_prefix(name, \"signature\", &name) || (*name &&\n+\t\t\t*name != ':'))\n+\t\t\tcontinue;\n+\t\tif (!*name)\n+\t\t\tname = NULL;\n+\t\telse\n+\t\t\tname++;\n+\t\tif (parse_signature_option(name) < 0)\n+\t\t\tcontinue;\n+\n+\t\tif (atom->u.signature.option == S_BARE)\n+\t\t\tv->s = xstrdup(sigc.output ? sigc.output: \"\");\n+\t\telse if (atom->u.signature.option == S_SIGNER)\n+\t\t\tv->s = xstrdup(sigc.signer ? sigc.signer : \"\");\n+\t\telse if (atom->u.signature.option == S_GRADE) {\n+\t\t\tswitch (sigc.result) {\n+\t\t\tcase 'G':\n+\t\t\t\tswitch (sigc.trust_level) {\n+\t\t\t\tcase TRUST_UNDEFINED:\n+\t\t\t\tcase TRUST_NEVER:\n+\t\t\t\t\tv->s = xstrfmt(\"%c\", (char)'U');\n+\t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\tv->s = xstrfmt(\"%c\", (char)'G');\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tbreak;\n+\t\t\tcase 'B':\n+\t\t\tcase 'E':\n+\t\t\tcase 'N':\n+\t\t\tcase 'X':\n+\t\t\tcase 'Y':\n+\t\t\tcase 'R':\n+\t\t\t\tv->s = xstrfmt(\"%c\", (char)sigc.result);\n+\t\t\t}\n+\t\t}\n+\t\telse if (atom->u.signature.option == S_KEY)\n+\t\t\tv->s = xstrdup(sigc.key ? sigc.key : \"\");\n+\t\telse if (atom->u.signature.option == S_FINGERPRINT)\n+\t\t\tv->s = xstrdup(sigc.fingerprint ? sigc.fingerprint : \"\");\n+\t\telse if (atom->u.signature.option == S_PRI_KEY_FP)\n+\t\t\tv->s = xstrdup(sigc.primary_key_fingerprint ? sigc.primary_key_fingerprint : \"\");\n+\t\telse if (atom->u.signature.option == S_TRUST_LEVEL)\n+\t\t\tv->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));\n+\t}\n+\tsignature_check_clear(&sigc);\n+}\n+\n static void find_subpos(const char *buf,\n \t\t\tconst char **sub, size_t *sublen,\n \t\t\tconst char **body, size_t *bodylen,\n@@ -1555,6 +1655,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s\n \t\tgrab_sub_body_contents(val, deref, data);\n \t\tgrab_person(\"author\", val, deref, buf);\n \t\tgrab_person(\"committer\", val, deref, buf);\n+\t\tgrab_signature(val, deref, obj);\n \t\tbreak;\n \tcase OBJ_TREE:\n \t\t/* grab_tree_values(val, deref, obj, buf, sz); */\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 2ae1fc721b1..a8efe6f58ec 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -6,6 +6,7 @@\n test_description='for-each-ref test'\n \n . ./test-lib.sh\n+GNUPGHOME_NOT_USED=$GNUPGHOME\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n . \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n@@ -1464,4 +1465,130 @@ sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n sig_crlf=${sig_crlf%dummy}\n test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n \n+GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+\n+test_expect_success GPG 'test bare signature atom' '\n+\tgit checkout -b signed &&\n+\techo 1 >file && git add file &&\n+\ttest_tick && git commit -S -m initial &&\n+\tgit verify-commit signed 2>out &&\n+\thead -3 out >expected &&\n+\ttail -1 out >>expected &&\n+\techo >>expected &&\n+\tgit for-each-ref refs/heads/signed --format=\"%(signature)\" >actual &&\n+\ttest_cmp actual expected\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\techo 2 >file && git add file &&\n+\ttest_tick && git commit -S -m initial &&\n+\tgit verify-commit signed 2>out &&\n+\tcat >expect <<-\\EOF &&\n+\tG\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\tgit for-each-ref refs/heads/signed --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'test signature atom with grade option and bad signature' '\n+\tgit config commit.gpgsign true &&\n+\techo 3 >file && test_tick && git commit -a -m \"third\" --no-gpg-sign &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick && git rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^third/3rd forged/\" raw >forged1 &&\n+\tFORGED1=$(git hash-object -w -t commit forged1) &&\n+\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n+\ttest_must_fail git verify-commit \"$FORGED1\" &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tB\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\n+\n+\tEOF\n+\tgit for-each-ref refs/tags/third-signed --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\n+\techo 4 >file && test_tick && git commit -a -m fourth -SB7227189 &&\n+\tgit tag signed-fourth &&\n+\tcat >expect <<-\\EOF &&\n+\tU\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\tgit for-each-ref refs/tags/signed-fourth --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\n+\techo 5 >file && test_tick && git commit -a -m fifth -SB7227189 &&\n+\tgit tag fifth-signed &&\n+\tcat >expect <<-\\EOF &&\n+\tundefined\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\tgit for-each-ref refs/tags/fifth-signed --format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n+\techo 7 >file && test_tick && git commit -a -m \"seventh\" --no-gpg-sign &&\n+\tgit tag seventh-unsigned &&\n+\n+\ttest_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&\n+\tgit tag seventh-signed &&\n+\tcat >expect <<-\\EOF &&\n+\tultimate\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\tgit for-each-ref refs/tags/seventh-signed --format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show unknown signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tE\n+\t65A0EEA02E30CAD7\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref refs/tags/fifth-signed --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show lack of signature with custom format' '\n+\techo 8 >file && test_tick && git commit -a -m \"eigth unsigned\" --no-gpg-sign &&\n+\tgit tag eigth-unsigned &&\n+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\tgit for-each-ref refs/tags/eigth-unsigned --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n\nbase-commit: 6bae53b138a1f38d8887f6b46d17661357a1468b\n-- \ngitgitgadget\n"},{"id":"469960","messageId":"CAP8UFD3cTnesN5SMcDjT3K2tpBOc82+aP=wr+DVzO3GomqwhRA@mail.gmail.com","threadId":"59055","inReplyTo":"pull.1428.git.git.1673254961028.gitgitgadget@gmail.com","subject":"Re: [PATCH] ref-filter: add new atom \"signature\" atom","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2023-01-09T09:45:29Z","receivedAt":"2023-01-09T09:47:42Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Mon, Jan 9, 2023 at 10:15 AM nsengaw4c via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n\nThis patch should be marked as a V2 in its subject:\n\n[PATCH v2] ref-filter: add new atom \"signature\" atom\n\nnot sure how to do that using GitGitGadget though.\n\nAlso \"atom\" appears twice in the subject, so the following would be even better:\n\n[PATCH v2] ref-filter: add new \"signature\" atom\n\nI am not sure how, but GitGitGadget should allow to set the\n\"In-Reply-To:\" to the message ID of your previous version of this\npatch, so that your email with the version 2 of the patch would appear\nin the same email thread as the email of your previous version of this\npatch on lore.kernel.org/git:\n\nhttps://lore.kernel.org/git/pull.1452.git.1672102523902.gitgitgadget@gmail.com/\n\n(Please don't resend this patch as a v2, but as a v3, if you make any change.)\n\n> This commit duplicates the code for `signature` atom from pretty.c\n> to ref-filter.c. This feature will help to get rid of current duplicate\n> implementation of `signature` atom when unifying implemenations by\n\ns/implemenations/implementations/\n\n> using ref-filter logic everywhere when ref-filter can do everything\n> pretty is doing.\n>\n> Add \"signature\" atom with `grade`, `signer`, `key`,\n> `fingerprint`, `primarykeyfingerprint`, `trustlevel` as arguments.\n> This code and its documentation are inspired by how the %GG, %G?,\n> %GS, %GK, %GF, %GP, and %GT pretty formats were implemented.\n>\n> Co-authored-by: Hariom Verma <hariom18599@gmail.com>\n> Co-authored-by: Jaydeep Das <jaydeepjd.8914@gmail.com>\n> Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n> Mentored-by: Hariom Verma <hariom18599@gmail.com>\n> Signed-off-by: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n> ---\n>     ref-filter: add new atom \"signature\" atom\n>\n>     This commit duplicates the code for signature atom from pretty.c to\n>     ref-filter.c. This feature will help to get rid of current duplicate\n>     implementation of signature atom when unifying implemenations by using\n\ns/implemenations/implementations/\n\n>     ref-filter logic everywhere when ref-filter can do everything pretty is\n>     doing.\n>\n>     Add \"signature\" atom with grade, signer, key, fingerprint,\n>     primarykeyfingerprint, trustlevel as arguments. This code and its\n>     documentation are inspired by how the %GG, %G?, %GS, %GK, %GF, %GP, and\n>     %GT pretty formats were implemented.\n>\n>     Co-authored-by: Hariom Verma hariom18599@gmail.com Co-authored-by:\n>     Jaydeep Das jaydeepjd.8914@gmail.com Mentored-by: Christian Couder\n>     chriscool@tuxfamily.org Mentored-by: Hariom Verma hariom18599@gmail.com\n>     Signed-off-by: Nsengiyumva Wilberforce nsengiyumvawilberforce@gmail.com\n\nNot sure you can do something about it, but the above lines aren't\nproperly wrapped.\n\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1428%2Fnsengiyumva-wilberforce%2Fsignature10-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1428/nsengiyumva-wilberforce/signature10-v1\n> Pull-Request: https://github.com/git/git/pull/1428\n>\n>  Documentation/git-for-each-ref.txt |  27 ++++++\n>  ref-filter.c                       | 101 +++++++++++++++++++++++\n>  t/t6300-for-each-ref.sh            | 127 +++++++++++++++++++++++++++++\n>  3 files changed, 255 insertions(+)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index 6da899c6296..9a0be85368b 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -212,6 +212,33 @@ symref::\n>         `:lstrip` and `:rstrip` options in the same way as `refname`\n>         above.\n>\n> +signature::\n> +       The GPG signature of a commit.\n> +\n> +signature:grade::\n> +       Show \"G\" for a good (valid) signature, \"B\" for a bad\n> +       signature, \"U\" for a good signature with unknown validity, \"X\"\n> +       for a good signature that has expired, \"Y\" for a good\n> +       signature made by an expired key, \"R\" for a good signature\n> +       made by a revoked key, \"E\" if the signature cannot be\n> +       checked (e.g. missing key) and \"N\" for no signature.\n> +\n> +signature:signer::\n> +       The signer of the GPG signature of a commit.\n> +\n> +signature:key::\n> +       The key of the GPG signature of a commit.\n> +\n> +signature:fingerprint::\n> +       The fingerprint of the GPG signature of a commit.\n> +\n> +signature:primarykeyfingerprint::\n> +       The Primary Key fingerprint of the GPG signature of a commit.\n> +\n> +signature:trustlevel::\n> +       The Trust level of the GPG signature of a commit. Possible\n> +       outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`.\n> +\n>  worktreepath::\n>         The absolute path to the worktree in which the ref is checked\n>         out, if it is checked out in any linked worktree. Empty string\n> diff --git a/ref-filter.c b/ref-filter.c\n> index a24324123e7..0cba756b186 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -144,6 +144,7 @@ enum atom_type {\n>         ATOM_BODY,\n>         ATOM_TRAILERS,\n>         ATOM_CONTENTS,\n> +       ATOM_SIGNATURE,\n>         ATOM_RAW,\n>         ATOM_UPSTREAM,\n>         ATOM_PUSH,\n> @@ -208,6 +209,10 @@ static struct used_atom {\n>                 struct email_option {\n>                         enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n>                 } email_option;\n> +               struct {\n> +                       enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,\n> +                              S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL} option;\n> +               } signature;\n>                 struct refname_atom refname;\n>                 char *head;\n>         } u;\n> @@ -394,6 +399,34 @@ static int subject_atom_parser(struct ref_format *format, struct used_atom *atom\n>         return 0;\n>  }\n>\n> +static int parse_signature_option(const char *arg)\n> +{\n> +       if (!arg)\n> +               return S_BARE;\n> +       else if (!strcmp(arg, \"signer\"))\n> +               return S_SIGNER;\n> +       else if (!strcmp(arg, \"grade\"))\n> +               return S_GRADE;\n> +       else if (!strcmp(arg, \"key\"))\n> +               return S_KEY;\n> +       else if (!strcmp(arg, \"fingerprint\"))\n> +               return S_FINGERPRINT;\n> +       else if (!strcmp(arg, \"primarykeyfingerprint\"))\n> +               return S_PRI_KEY_FP;\n> +       else if (!strcmp(arg, \"trustlevel\"))\n> +               return S_TRUST_LEVEL;\n> +       return -1;\n> +}\n> +\n> +static int signature_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom,\n> +                              const char *arg, struct strbuf *err){\n> +       int opt = parse_signature_option(arg);\n> +       if (opt < 0)\n> +               return err_bad_arg(err, \"signature\", arg);\n> +       atom->u.signature.option = opt;\n> +       return 0;\n> +}\n> +\n>  static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom,\n>                                 const char *arg, struct strbuf *err)\n>  {\n> @@ -631,6 +664,7 @@ static struct {\n>         [ATOM_BODY] = { \"body\", SOURCE_OBJ, FIELD_STR, body_atom_parser },\n>         [ATOM_TRAILERS] = { \"trailers\", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },\n>         [ATOM_CONTENTS] = { \"contents\", SOURCE_OBJ, FIELD_STR, contents_atom_parser },\n> +       [ATOM_SIGNATURE] = { \"signature\", SOURCE_OBJ, FIELD_STR, signature_atom_parser },\n>         [ATOM_RAW] = { \"raw\", SOURCE_OBJ, FIELD_STR, raw_atom_parser },\n>         [ATOM_UPSTREAM] = { \"upstream\", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },\n>         [ATOM_PUSH] = { \"push\", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },\n> @@ -1362,6 +1396,72 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n>         }\n>  }\n>\n> +static void grab_signature(struct atom_value *val, int deref, struct object *obj)\n> +{\n> +       int i;\n> +       struct commit *commit = (struct commit *) obj;\n> +       struct signature_check sigc = { 0 };\n> +\n> +       check_commit_signature(commit, &sigc);\n> +\n> +       for (i = 0; i < used_atom_cnt; i++) {\n> +               struct used_atom *atom = &used_atom[i];\n> +               const char *name = atom->name;\n> +               struct atom_value *v = &val[i];\n> +\n> +               if (!!deref != (*name == '*'))\n> +                       continue;\n> +               if (deref)\n> +                       name++;\n> +\n> +               if (!skip_prefix(name, \"signature\", &name) || (*name &&\n> +                       *name != ':'))\n> +                       continue;\n> +               if (!*name)\n> +                       name = NULL;\n> +               else\n> +                       name++;\n> +               if (parse_signature_option(name) < 0)\n> +                       continue;\n> +\n> +               if (atom->u.signature.option == S_BARE)\n> +                       v->s = xstrdup(sigc.output ? sigc.output: \"\");\n> +               else if (atom->u.signature.option == S_SIGNER)\n> +                       v->s = xstrdup(sigc.signer ? sigc.signer : \"\");\n> +               else if (atom->u.signature.option == S_GRADE) {\n> +                       switch (sigc.result) {\n> +                       case 'G':\n> +                               switch (sigc.trust_level) {\n> +                               case TRUST_UNDEFINED:\n> +                               case TRUST_NEVER:\n> +                                       v->s = xstrfmt(\"%c\", (char)'U');\n> +                                       break;\n> +                               default:\n> +                                       v->s = xstrfmt(\"%c\", (char)'G');\n> +                                       break;\n> +                               }\n> +                               break;\n> +                       case 'B':\n> +                       case 'E':\n> +                       case 'N':\n> +                       case 'X':\n> +                       case 'Y':\n> +                       case 'R':\n> +                               v->s = xstrfmt(\"%c\", (char)sigc.result);\n> +                       }\n> +               }\n> +               else if (atom->u.signature.option == S_KEY)\n> +                       v->s = xstrdup(sigc.key ? sigc.key : \"\");\n> +               else if (atom->u.signature.option == S_FINGERPRINT)\n> +                       v->s = xstrdup(sigc.fingerprint ? sigc.fingerprint : \"\");\n> +               else if (atom->u.signature.option == S_PRI_KEY_FP)\n> +                       v->s = xstrdup(sigc.primary_key_fingerprint ? sigc.primary_key_fingerprint : \"\");\n> +               else if (atom->u.signature.option == S_TRUST_LEVEL)\n> +                       v->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));\n> +       }\n> +       signature_check_clear(&sigc);\n> +}\n> +\n>  static void find_subpos(const char *buf,\n>                         const char **sub, size_t *sublen,\n>                         const char **body, size_t *bodylen,\n> @@ -1555,6 +1655,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s\n>                 grab_sub_body_contents(val, deref, data);\n>                 grab_person(\"author\", val, deref, buf);\n>                 grab_person(\"committer\", val, deref, buf);\n> +               grab_signature(val, deref, obj);\n>                 break;\n>         case OBJ_TREE:\n>                 /* grab_tree_values(val, deref, obj, buf, sz); */\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 2ae1fc721b1..a8efe6f58ec 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -6,6 +6,7 @@\n>  test_description='for-each-ref test'\n>\n>  . ./test-lib.sh\n> +GNUPGHOME_NOT_USED=$GNUPGHOME\n>  . \"$TEST_DIRECTORY\"/lib-gpg.sh\n>  . \"$TEST_DIRECTORY\"/lib-terminal.sh\n>\n> @@ -1464,4 +1465,130 @@ sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n>  sig_crlf=${sig_crlf%dummy}\n>  test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n>\n> +GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n> +TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n> +\n> +test_expect_success GPG 'test bare signature atom' '\n> +       git checkout -b signed &&\n> +       echo 1 >file && git add file &&\n> +       test_tick && git commit -S -m initial &&\n> +       git verify-commit signed 2>out &&\n> +       head -3 out >expected &&\n> +       tail -1 out >>expected &&\n> +       echo >>expected &&\n> +       git for-each-ref refs/heads/signed --format=\"%(signature)\" >actual &&\n> +       test_cmp actual expected\n> +'\n\n(I already commented about this test in a previous email related to\nhow it fails on GitHub CI.)\n"},{"id":"469969","messageId":"CA+PPyiEghM+eoA=oM9vJ+=Xyvou+ToV077rA+ty2k3dyUkhLZg@mail.gmail.com","threadId":"59055","inReplyTo":"CAP8UFD3cTnesN5SMcDjT3K2tpBOc82+aP=wr+DVzO3GomqwhRA@mail.gmail.com","subject":"Re: [PATCH] ref-filter: add new atom \"signature\" atom","fromName":"NSENGIYUMVA WILBERFORCE","fromEmail":"nsengiyumvawilberforce@gmail.com","sentAt":"2023-01-09T12:59:42Z","receivedAt":"2023-01-09T13:02:54Z","isPatch":true,"sender":{"key":"nsengiyumvawilberforce@gmail.com","avatar":"https://avatars.githubusercontent.com/u/65920105?v=4"},"body":"> <gitgitgadget@gmail.com> wrote:\n> >\n> > From: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n>\n> This patch should be marked as a V2 in its subject:\n>\n> [PATCH v2] ref-filter: add new atom \"signature\" atom\n>\n> not sure how to do that using GitGitGadget though.\n>\n> Also \"atom\" appears twice in the subject, so the following would be even better:\n>\n> [PATCH v2] ref-filter: add new \"signature\" atom\n>\n> I am not sure how, but GitGitGadget should allow to set the\n> \"In-Reply-To:\" to the message ID of your previous version of this\n> patch, so that your email with the version 2 of the patch would appear\n> in the same email thread as the email of your previous version of this\n> patch on lore.kernel.org/git:\n>\n> https://lore.kernel.org/git/pull.1452.git.1672102523902.gitgitgadget@gmail.com/\n>\n> (Please don't resend this patch as a v2, but as a v3, if you make any change.)\n\nI do not see any option for changing the ID, I think I need some\ndirections on how to change the patch version\n\n\n\nOn Mon, Jan 9, 2023 at 4:45 AM Christian Couder\n<christian.couder@gmail.com> wrote:\n>\n> On Mon, Jan 9, 2023 at 10:15 AM nsengaw4c via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> >\n> > From: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n>\n> This patch should be marked as a V2 in its subject:\n>\n> [PATCH v2] ref-filter: add new atom \"signature\" atom\n>\n> not sure how to do that using GitGitGadget though.\n>\n> Also \"atom\" appears twice in the subject, so the following would be even better:\n>\n> [PATCH v2] ref-filter: add new \"signature\" atom\n>\n> I am not sure how, but GitGitGadget should allow to set the\n> \"In-Reply-To:\" to the message ID of your previous version of this\n> patch, so that your email with the version 2 of the patch would appear\n> in the same email thread as the email of your previous version of this\n> patch on lore.kernel.org/git:\n>\n> https://lore.kernel.org/git/pull.1452.git.1672102523902.gitgitgadget@gmail.com/\n>\n> (Please don't resend this patch as a v2, but as a v3, if you make any change.)\n>\n> > This commit duplicates the code for `signature` atom from pretty.c\n> > to ref-filter.c. This feature will help to get rid of current duplicate\n> > implementation of `signature` atom when unifying implemenations by\n>\n> s/implemenations/implementations/\n>\n> > using ref-filter logic everywhere when ref-filter can do everything\n> > pretty is doing.\n> >\n> > Add \"signature\" atom with `grade`, `signer`, `key`,\n> > `fingerprint`, `primarykeyfingerprint`, `trustlevel` as arguments.\n> > This code and its documentation are inspired by how the %GG, %G?,\n> > %GS, %GK, %GF, %GP, and %GT pretty formats were implemented.\n> >\n> > Co-authored-by: Hariom Verma <hariom18599@gmail.com>\n> > Co-authored-by: Jaydeep Das <jaydeepjd.8914@gmail.com>\n> > Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n> > Mentored-by: Hariom Verma <hariom18599@gmail.com>\n> > Signed-off-by: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n> > ---\n> >     ref-filter: add new atom \"signature\" atom\n> >\n> >     This commit duplicates the code for signature atom from pretty.c to\n> >     ref-filter.c. This feature will help to get rid of current duplicate\n> >     implementation of signature atom when unifying implemenations by using\n>\n> s/implemenations/implementations/\n>\n> >     ref-filter logic everywhere when ref-filter can do everything pretty is\n> >     doing.\n> >\n> >     Add \"signature\" atom with grade, signer, key, fingerprint,\n> >     primarykeyfingerprint, trustlevel as arguments. This code and its\n> >     documentation are inspired by how the %GG, %G?, %GS, %GK, %GF, %GP, and\n> >     %GT pretty formats were implemented.\n> >\n> >     Co-authored-by: Hariom Verma hariom18599@gmail.com Co-authored-by:\n> >     Jaydeep Das jaydeepjd.8914@gmail.com Mentored-by: Christian Couder\n> >     chriscool@tuxfamily.org Mentored-by: Hariom Verma hariom18599@gmail.com\n> >     Signed-off-by: Nsengiyumva Wilberforce nsengiyumvawilberforce@gmail.com\n>\n> Not sure you can do something about it, but the above lines aren't\n> properly wrapped.\n>\n> > Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1428%2Fnsengiyumva-wilberforce%2Fsignature10-v1\n> > Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1428/nsengiyumva-wilberforce/signature10-v1\n> > Pull-Request: https://github.com/git/git/pull/1428\n> >\n> >  Documentation/git-for-each-ref.txt |  27 ++++++\n> >  ref-filter.c                       | 101 +++++++++++++++++++++++\n> >  t/t6300-for-each-ref.sh            | 127 +++++++++++++++++++++++++++++\n> >  3 files changed, 255 insertions(+)\n> >\n> > diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> > index 6da899c6296..9a0be85368b 100644\n> > --- a/Documentation/git-for-each-ref.txt\n> > +++ b/Documentation/git-for-each-ref.txt\n> > @@ -212,6 +212,33 @@ symref::\n> >         `:lstrip` and `:rstrip` options in the same way as `refname`\n> >         above.\n> >\n> > +signature::\n> > +       The GPG signature of a commit.\n> > +\n> > +signature:grade::\n> > +       Show \"G\" for a good (valid) signature, \"B\" for a bad\n> > +       signature, \"U\" for a good signature with unknown validity, \"X\"\n> > +       for a good signature that has expired, \"Y\" for a good\n> > +       signature made by an expired key, \"R\" for a good signature\n> > +       made by a revoked key, \"E\" if the signature cannot be\n> > +       checked (e.g. missing key) and \"N\" for no signature.\n> > +\n> > +signature:signer::\n> > +       The signer of the GPG signature of a commit.\n> > +\n> > +signature:key::\n> > +       The key of the GPG signature of a commit.\n> > +\n> > +signature:fingerprint::\n> > +       The fingerprint of the GPG signature of a commit.\n> > +\n> > +signature:primarykeyfingerprint::\n> > +       The Primary Key fingerprint of the GPG signature of a commit.\n> > +\n> > +signature:trustlevel::\n> > +       The Trust level of the GPG signature of a commit. Possible\n> > +       outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`.\n> > +\n> >  worktreepath::\n> >         The absolute path to the worktree in which the ref is checked\n> >         out, if it is checked out in any linked worktree. Empty string\n> > diff --git a/ref-filter.c b/ref-filter.c\n> > index a24324123e7..0cba756b186 100644\n> > --- a/ref-filter.c\n> > +++ b/ref-filter.c\n> > @@ -144,6 +144,7 @@ enum atom_type {\n> >         ATOM_BODY,\n> >         ATOM_TRAILERS,\n> >         ATOM_CONTENTS,\n> > +       ATOM_SIGNATURE,\n> >         ATOM_RAW,\n> >         ATOM_UPSTREAM,\n> >         ATOM_PUSH,\n> > @@ -208,6 +209,10 @@ static struct used_atom {\n> >                 struct email_option {\n> >                         enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n> >                 } email_option;\n> > +               struct {\n> > +                       enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,\n> > +                              S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL} option;\n> > +               } signature;\n> >                 struct refname_atom refname;\n> >                 char *head;\n> >         } u;\n> > @@ -394,6 +399,34 @@ static int subject_atom_parser(struct ref_format *format, struct used_atom *atom\n> >         return 0;\n> >  }\n> >\n> > +static int parse_signature_option(const char *arg)\n> > +{\n> > +       if (!arg)\n> > +               return S_BARE;\n> > +       else if (!strcmp(arg, \"signer\"))\n> > +               return S_SIGNER;\n> > +       else if (!strcmp(arg, \"grade\"))\n> > +               return S_GRADE;\n> > +       else if (!strcmp(arg, \"key\"))\n> > +               return S_KEY;\n> > +       else if (!strcmp(arg, \"fingerprint\"))\n> > +               return S_FINGERPRINT;\n> > +       else if (!strcmp(arg, \"primarykeyfingerprint\"))\n> > +               return S_PRI_KEY_FP;\n> > +       else if (!strcmp(arg, \"trustlevel\"))\n> > +               return S_TRUST_LEVEL;\n> > +       return -1;\n> > +}\n> > +\n> > +static int signature_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom,\n> > +                              const char *arg, struct strbuf *err){\n> > +       int opt = parse_signature_option(arg);\n> > +       if (opt < 0)\n> > +               return err_bad_arg(err, \"signature\", arg);\n> > +       atom->u.signature.option = opt;\n> > +       return 0;\n> > +}\n> > +\n> >  static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom,\n> >                                 const char *arg, struct strbuf *err)\n> >  {\n> > @@ -631,6 +664,7 @@ static struct {\n> >         [ATOM_BODY] = { \"body\", SOURCE_OBJ, FIELD_STR, body_atom_parser },\n> >         [ATOM_TRAILERS] = { \"trailers\", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },\n> >         [ATOM_CONTENTS] = { \"contents\", SOURCE_OBJ, FIELD_STR, contents_atom_parser },\n> > +       [ATOM_SIGNATURE] = { \"signature\", SOURCE_OBJ, FIELD_STR, signature_atom_parser },\n> >         [ATOM_RAW] = { \"raw\", SOURCE_OBJ, FIELD_STR, raw_atom_parser },\n> >         [ATOM_UPSTREAM] = { \"upstream\", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },\n> >         [ATOM_PUSH] = { \"push\", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },\n> > @@ -1362,6 +1396,72 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n> >         }\n> >  }\n> >\n> > +static void grab_signature(struct atom_value *val, int deref, struct object *obj)\n> > +{\n> > +       int i;\n> > +       struct commit *commit = (struct commit *) obj;\n> > +       struct signature_check sigc = { 0 };\n> > +\n> > +       check_commit_signature(commit, &sigc);\n> > +\n> > +       for (i = 0; i < used_atom_cnt; i++) {\n> > +               struct used_atom *atom = &used_atom[i];\n> > +               const char *name = atom->name;\n> > +               struct atom_value *v = &val[i];\n> > +\n> > +               if (!!deref != (*name == '*'))\n> > +                       continue;\n> > +               if (deref)\n> > +                       name++;\n> > +\n> > +               if (!skip_prefix(name, \"signature\", &name) || (*name &&\n> > +                       *name != ':'))\n> > +                       continue;\n> > +               if (!*name)\n> > +                       name = NULL;\n> > +               else\n> > +                       name++;\n> > +               if (parse_signature_option(name) < 0)\n> > +                       continue;\n> > +\n> > +               if (atom->u.signature.option == S_BARE)\n> > +                       v->s = xstrdup(sigc.output ? sigc.output: \"\");\n> > +               else if (atom->u.signature.option == S_SIGNER)\n> > +                       v->s = xstrdup(sigc.signer ? sigc.signer : \"\");\n> > +               else if (atom->u.signature.option == S_GRADE) {\n> > +                       switch (sigc.result) {\n> > +                       case 'G':\n> > +                               switch (sigc.trust_level) {\n> > +                               case TRUST_UNDEFINED:\n> > +                               case TRUST_NEVER:\n> > +                                       v->s = xstrfmt(\"%c\", (char)'U');\n> > +                                       break;\n> > +                               default:\n> > +                                       v->s = xstrfmt(\"%c\", (char)'G');\n> > +                                       break;\n> > +                               }\n> > +                               break;\n> > +                       case 'B':\n> > +                       case 'E':\n> > +                       case 'N':\n> > +                       case 'X':\n> > +                       case 'Y':\n> > +                       case 'R':\n> > +                               v->s = xstrfmt(\"%c\", (char)sigc.result);\n> > +                       }\n> > +               }\n> > +               else if (atom->u.signature.option == S_KEY)\n> > +                       v->s = xstrdup(sigc.key ? sigc.key : \"\");\n> > +               else if (atom->u.signature.option == S_FINGERPRINT)\n> > +                       v->s = xstrdup(sigc.fingerprint ? sigc.fingerprint : \"\");\n> > +               else if (atom->u.signature.option == S_PRI_KEY_FP)\n> > +                       v->s = xstrdup(sigc.primary_key_fingerprint ? sigc.primary_key_fingerprint : \"\");\n> > +               else if (atom->u.signature.option == S_TRUST_LEVEL)\n> > +                       v->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));\n> > +       }\n> > +       signature_check_clear(&sigc);\n> > +}\n> > +\n> >  static void find_subpos(const char *buf,\n> >                         const char **sub, size_t *sublen,\n> >                         const char **body, size_t *bodylen,\n> > @@ -1555,6 +1655,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s\n> >                 grab_sub_body_contents(val, deref, data);\n> >                 grab_person(\"author\", val, deref, buf);\n> >                 grab_person(\"committer\", val, deref, buf);\n> > +               grab_signature(val, deref, obj);\n> >                 break;\n> >         case OBJ_TREE:\n> >                 /* grab_tree_values(val, deref, obj, buf, sz); */\n> > diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> > index 2ae1fc721b1..a8efe6f58ec 100755\n> > --- a/t/t6300-for-each-ref.sh\n> > +++ b/t/t6300-for-each-ref.sh\n> > @@ -6,6 +6,7 @@\n> >  test_description='for-each-ref test'\n> >\n> >  . ./test-lib.sh\n> > +GNUPGHOME_NOT_USED=$GNUPGHOME\n> >  . \"$TEST_DIRECTORY\"/lib-gpg.sh\n> >  . \"$TEST_DIRECTORY\"/lib-terminal.sh\n> >\n> > @@ -1464,4 +1465,130 @@ sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n> >  sig_crlf=${sig_crlf%dummy}\n> >  test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n> >\n> > +GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n> > +TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n> > +\n> > +test_expect_success GPG 'test bare signature atom' '\n> > +       git checkout -b signed &&\n> > +       echo 1 >file && git add file &&\n> > +       test_tick && git commit -S -m initial &&\n> > +       git verify-commit signed 2>out &&\n> > +       head -3 out >expected &&\n> > +       tail -1 out >>expected &&\n> > +       echo >>expected &&\n> > +       git for-each-ref refs/heads/signed --format=\"%(signature)\" >actual &&\n> > +       test_cmp actual expected\n> > +'\n>\n> (I already commented about this test in a previous email related to\n> how it fails on GitHub CI.)\n"}]}