{"thread":{"id":"59804","subject":"[GSoC][PATCH 0/2] Add new \"signature\" atom","startedAt":"2023-05-29T19:23:20Z","lastAt":"2023-06-04T18:59:13Z","messageCount":19,"participants":["Kousik Sanagavarapu","Christian Couder","Junio C Hamano","Eric Sunshine","Oswald Buddenhagen"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"477786","messageId":"20230529192209.17747-1-five231003@gmail.com","threadId":"59804","inReplyTo":null,"subject":"[GSoC][PATCH 0/2] Add new \"signature\" atom","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-05-29T18:32:04Z","receivedAt":"2023-05-29T19:23:20Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"Hi,\nThis series duplicates the code for signature related formats from\npretty to ref-filter, which is a step in the process of duplicating all\nthe pretty formats which are not present in ref-filter, with the end\ngoal of a single formatting interface for git in mind.\n\nPATCH 1/2 introduces a new prereq GPG2 for the purpose of testing stuff\nthat breaks with GPG version <= v2. This is evident from the CI failure\nin [1] that was sent with the same subject as this series (more info\nbelow).\n\nPATCH 2/2 adds a new \"signature\" atom, which is a duplication of the %G*\nformats in pretty. This was sent before by Nsengiyumva Wilberforce and\nwas in \"seen\" until May and was removed because of CI failure. I have\nbuilt upon it and have done some minor changes to it. The CI jobs are\nnow successful which can be found at [2].\n\n[1]: Patch\n\n\thttps://lore.kernel.org/git/20230311210607.64927-1-nsengiyumvawilberforce@gmail.com/\n\n     Junio's email about CI failure\n\n\thttps://lore.kernel.org/git/xmqqpm9bosjw.fsf@gitster.g/\n\n[2]: https://github.com/five-sh/git/actions/runs/5114306975/workflow\n\nHere is the range-diff, compared to Nsengiyumva's final version posted\nto the mailing list\n\n-:  ---------- > 1:  5c97d11b79 t/lib-gpg: introduce new prereq GPG2\n1:  8a49102b1f ! 2:  e89f14283d ref-filter: add new \"signature\" atom\n    @@\n      ## Metadata ##\n    -Author: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\n    +Author: Kousik Sanagavarapu <five231003@gmail.com>\n     \n      ## Commit message ##\n         ref-filter: add new \"signature\" atom\n     \n    -    This commit duplicates the code for `signature` atom from\n         pretty.c\n    -    to ref-filter.c. This feature will help to get rid of current\n         duplicate\n    -    implementation of `signature` atom when unifying\n         implementations by\n    -    using ref-filter logic everywhere when ref-filter can do\n         everything\n    -    pretty is doing.\n    +    Duplicate the code for outputting the signature and it's other\n    +    parameters for commits and tags in ref-filter from pretty. In\nthe\n    +    future, this will help in getting rid of the current duplicate\n    +    implementations of such logic everywhere, when ref-filter can\ndo\n    +    everything that pretty is doing.\n     \n    -    Add \"signature\" atom with `grade`, `signer`, `key`,\n    -    `fingerprint`, `primarykeyfingerprint`, `trustlevel` as\n         arguments.\n    -    This code and its documentation are inspired by how the %GG,\n         %G?,\n    -    %GS, %GK, %GF, %GP, and %GT pretty formats were implemented.\n    +    The new atom \"signature\" and it's friends are equivalent to the\nexisting\n    +    pretty formats as follows:\n    +\n    +            %(signature) = %GG\n    +            %(signature:grade) = %G?\n    +            %(siganture:signer) = %GS\n    +            %(signature:key) = %GK\n    +            %(signature:fingerprint) = %GF\n    +            %(signature:primarykeyfingerprint) = %GP\n    +            %(signature:trustlevel) = %GT\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    +    Co-authored-by: Nsengiyumva Wilberforce\n<nsengiyumvawilberforce@gmail.com>\n    +    Mentored-by: Christian Couder <christian.couder@gmail.com>\n         Mentored-by: Hariom Verma <hariom18599@gmail.com>\n    -    Signed-off-by: Nsengiyumva Wilberforce\n         <nsengiyumvawilberforce@gmail.com>\n    +    Signed-off-by: Kousik Sanagavarapu <five231003@gmail.com>\n     \n      ## Documentation/git-for-each-ref.txt ##\n     @@ Documentation/git-for-each-ref.txt: symref::\n    @@ ref-filter.c: static int subject_atom_parser(struct ref_format\n*format UNUSED,\n     +  return -1;\n     +}\n     +\n    -+static int signature_atom_parser(struct ref_format *format UNUSED,\nstruct used_atom *atom,\n    -+                         const char *arg, struct strbuf *err){\n    ++static int signature_atom_parser(struct ref_format *format UNUSED,\n    ++                           struct used_atom *atom,\n    ++                           const char *arg, struct strbuf *err)\n    ++{\n     +  int opt = parse_signature_option(arg);\n     +  if (opt < 0)\n     +          return err_bad_arg(err, \"signature\", arg);\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\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    ++test_expect_success GPG 'setup: signature gpg' '\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_orig &&\n    -+  grep -v \"checking the trustdb\" out_orig >out &&\n    -+  head -3 out >expected &&\n    -+  tail -1 out >>expected &&\n    -+  echo >>expected &&\n    -+  git for-each-ref refs/heads/signed --format=\"%(signature)\"\n>actual &&\n    -+  test_cmp expected actual\n    ++\n    ++  test_when_finished \"test_unconfig commit.gpgSign\" &&\n    ++\n    ++  echo \"1\" >file &&\n    ++  git add file &&\n    ++  test_tick &&\n    ++  git commit -S -m \"file: 1\" &&\n    ++  git tag first-signed &&\n    ++\n    ++  echo \"2\" >file &&\n    ++  test_tick &&\n    ++  git commit -a -m \"file: 2\" &&\n    ++  git tag second-unsigned &&\n    ++\n    ++  git config commit.gpgSign 1 &&\n    ++  echo \"3\" >file &&\n    ++  test_tick &&\n    ++  git commit -a --no-gpg-sign -m \"file: 3\" &&\n    ++  git tag third-unsigned &&\n    ++\n    ++  test_tick &&\n    ++  git rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n    ++  git tag third-signed &&\n    ++\n    ++  echo \"4\" >file &&\n    ++  test_tick &&\n    ++  git commit -a -SB7227189 -m \"file: 4\" &&\n    ++  git tag fourth-signed &&\n    ++\n    ++  echo \"5\" >file &&\n    ++  test_tick &&\n    ++  git commit -a --no-gpg-sign -m \"file: 5\" &&\n    ++  git tag fifth-unsigned &&\n    ++\n    ++  echo \"6\" >file &&\n    ++  test_tick &&\n    ++  git commit -a --no-gpg-sign -m \"file: 6\" &&\n    ++\n    ++  test_tick &&\n    ++  git rebase -f HEAD^^ &&\n    ++  git tag fifth-signed HEAD^ &&\n    ++  git tag sixth-signed &&\n    ++\n    ++  echo \"7\" >file &&\n    ++  test_tick &&\n    ++  git commit -a --no-gpg-sign -m \"file: 7\" &&\n    ++  git tag seventh-unsigned\n    ++'\n    ++\n    ++test_expect_success GPGSSH 'setup: signature ssh' '\n    ++  test_config gpg.format ssh &&\n    ++  test_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n    ++  echo \"8\" >file &&\n    ++  test_tick &&\n    ++  git commit -a -S -m \"file: 8\" &&\n    ++  git tag eighth-signed-ssh\n    ++'\n    ++\n    ++test_expect_success GPG2 'bare signature atom' '\n    ++  git verify-commit first-signed 2>out.raw &&\n    ++  grep -Ev \"checking the trustdb|PGP trust model\" out.raw >out &&\n    ++  head -3 out >expect &&\n    ++  tail -1 out >>expect &&\n    ++  echo \"\" >>expect &&\n    ++  git for-each-ref refs/tags/first-signed \\\n    ++          --format=\"%(signature)\" >actual &&\n    ++  test_cmp expect actual\n     +'\n     +\n     +test_expect_success GPG 'show good signature with custom format' '\n    -+  echo 2 >file && git add file &&\n    -+  test_tick && git commit -S -m initial &&\n    -+  git verify-commit signed 2>out &&\n    ++  git verify-commit first-signed &&\n     +  cat >expect <<-\\EOF &&\n     +  G\n     +  13B6F51ECDDE430D\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +  73D758744BE721698EC54E8713B6F51ECDDE430D\n     +  73D758744BE721698EC54E8713B6F51ECDDE430D\n     +  EOF\n    -+  git for-each-ref refs/heads/signed --format=\"$GRADE_FORMAT\"\n>actual &&\n    ++  git for-each-ref refs/tags/first-signed \\\n    ++          --format=\"$GRADE_FORMAT\" >actual &&\n     +  test_cmp expect actual\n     +'\n    ++test_expect_success GPGSSH 'show good signature with custom format\n    ++                      with ssh' '\n    ++  test_config gpg.ssh.allowedSignersFile\n\"${GPGSSH_ALLOWED_SIGNERS}\" &&\n    ++  FINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk\n\"{print \\$2;}\") &&\n    ++  cat >expect.tmpl <<-\\EOF &&\n    ++  G\n    ++  FINGERPRINT\n    ++  principal with number 1\n    ++  FINGERPRINT\n     +\n    -+test_expect_success GPG 'test signature atom with grade option and\nbad signature' '\n    -+  git config commit.gpgsign true &&\n    -+  echo 3 >file && test_tick && git commit -a -m \"third\"\n--no-gpg-sign &&\n    -+  git tag third-unsigned &&\n    -+\n    -+  test_tick && git rebase -f HEAD^^ && git tag second-signed HEAD^\n&&\n    -+  git tag third-signed &&\n    ++  EOF\n    ++  sed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n    ++  git for-each-ref refs/tags/eighth-signed-ssh \\\n    ++          --format=\"$GRADE_FORMAT\" >actual &&\n    ++  test_cmp expect actual\n    ++'\n     +\n    ++test_expect_success GPG 'signature atom with grade option and bad\nsignature' '\n     +  git cat-file commit third-signed >raw &&\n    -+  sed -e \"s/^third/3rd forged/\" raw >forged1 &&\n    ++  sed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n     +  FORGED1=$(git hash-object -w -t commit forged1) &&\n     +  git update-ref refs/tags/third-signed \"$FORGED1\" &&\n     +  test_must_fail git verify-commit \"$FORGED1\" &&\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +\n     +\n     +  EOF\n    -+  git for-each-ref refs/tags/third-signed --format=\"$GRADE_FORMAT\"\n>actual &&\n    ++  git for-each-ref refs/tags/third-signed \\\n    ++          --format=\"$GRADE_FORMAT\" >actual &&\n     +  test_cmp expect actual\n     +'\n     +\n     +test_expect_success GPG 'show untrusted signature with custom\nformat' '\n    -+  echo 4 >file && test_tick && git commit -a -m fourth -SB7227189\n&&\n    -+  git tag signed-fourth &&\n     +  cat >expect <<-\\EOF &&\n     +  U\n     +  65A0EEA02E30CAD7\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +  F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n     +  D4BE22311AD3131E5EDA29A461092E85B7227189\n     +  EOF\n    -+  git for-each-ref refs/tags/signed-fourth\n--format=\"$GRADE_FORMAT\" >actual &&\n    ++  git for-each-ref refs/tags/fourth-signed \\\n    ++          --format=\"$GRADE_FORMAT\" >actual &&\n     +  test_cmp expect actual\n     +'\n     +\n     +test_expect_success GPG 'show untrusted signature with undefined\ntrust level' '\n    -+  echo 5 >file && test_tick && git commit -a -m fifth -SB7227189\n&&\n    -+  git tag fifth-signed &&\n     +  cat >expect <<-\\EOF &&\n     +  undefined\n     +  65A0EEA02E30CAD7\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +  F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n     +  D4BE22311AD3131E5EDA29A461092E85B7227189\n     +  EOF\n    -+  git for-each-ref refs/tags/fifth-signed\n--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n    ++  git for-each-ref refs/tags/fourth-signed \\\n    ++          --format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n     +  test_cmp expect actual\n     +'\n     +\n     +test_expect_success GPG 'show untrusted signature with ultimate\ntrust level' '\n    -+  echo 7 >file && test_tick && git commit -a -m \"seventh\"\n--no-gpg-sign &&\n    -+  git tag seventh-unsigned &&\n    -+\n    -+  test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^\n&&\n    -+  git tag seventh-signed &&\n     +  cat >expect <<-\\EOF &&\n     +  ultimate\n     +  13B6F51ECDDE430D\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +  73D758744BE721698EC54E8713B6F51ECDDE430D\n     +  73D758744BE721698EC54E8713B6F51ECDDE430D\n     +  EOF\n    -+  git for-each-ref refs/tags/seventh-signed\n--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n    ++  git for-each-ref refs/tags/sixth-signed \\\n    ++          --format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n     +  test_cmp expect actual\n     +'\n     +\n     +test_expect_success GPG 'show unknown signature with custom\nformat' '\n     +  cat >expect <<-\\EOF &&\n     +  E\n    -+  65A0EEA02E30CAD7\n    ++  13B6F51ECDDE430D\n     +\n     +\n     +\n     +  EOF\n    -+  GNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref\nrefs/tags/fifth-signed --format=\"$GRADE_FORMAT\" >actual &&\n    ++  GNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n    ++          refs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual\n&&\n     +  test_cmp expect actual\n     +'\n     +\n     +test_expect_success GPG 'show lack of signature with custom\nformat' '\n    -+  echo 8 >file && test_tick && git commit -a -m \"eigth unsigned\"\n--no-gpg-sign &&\n    -+  git tag eigth-unsigned &&\n     +  cat >expect <<-\\EOF &&\n     +  N\n     +\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +\n     +\n     +  EOF\n    -+  git for-each-ref refs/tags/eigth-unsigned\n--format=\"$GRADE_FORMAT\" >actual &&\n    ++  git for-each-ref refs/tags/seventh-unsigned \\\n    ++          --format=\"$GRADE_FORMAT\" >actual &&\n     +  test_cmp expect actual\n     +'\n     + \n\nKousik Sanagavarapu (2):\n  t/lib-gpg: introduce new prereq GPG2\n  ref-filter: add new \"signature\" atom\n\n Documentation/git-for-each-ref.txt |  27 +++++\n ref-filter.c                       | 111 ++++++++++++++++-\n t/lib-gpg.sh                       |  21 ++++\n t/t6300-for-each-ref.sh            | 189 +++++++++++++++++++++++++++++\n t/t7510-signed-commit.sh           |   7 ++\n 5 files changed, 353 insertions(+), 2 deletions(-)\n\n-- \n2.41.0.rc0\n\n"},{"id":"477787","messageId":"20230529192209.17747-2-five231003@gmail.com","threadId":"59804","inReplyTo":"20230529192209.17747-1-five231003@gmail.com","subject":"[PATCH 1/2] t/lib-gpg: introduce new prereq GPG2","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-05-29T18:32:05Z","receivedAt":"2023-05-29T19:23:35Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"GnuPG v2.0.0 released in 2006, which according to its release notes\n\n\thttps://gnupg.org/download/release_notes.html\n\nis the \"First stable version of GnuPG integrating OpenPGP and S/MIME\".\n\nUse this version or it's successors for tests that will fail for\nversions less than v2.0.0 because of the difference in the output on\nstderr between the versions (v2.* vs v0.* or v2.* vs v1.*). Skip if\nthe GPG version detected is less than v2.0.0.\n\nDo not, however, remove the existing prereq GPG yet since a lot of tests\nstill work with the prereq GPG (that is even with versions v0.* or v1.*)\nand some systems still use these versions.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Hariom Verma <hariom18599@gmail.com>\nSigned-off-by: Kousik Sanagavarapu <five231003@gmail.com>\n---\n t/lib-gpg.sh             | 21 +++++++++++++++++++++\n t/t7510-signed-commit.sh |  7 +++++++\n 2 files changed, 28 insertions(+)\n\ndiff --git a/t/lib-gpg.sh b/t/lib-gpg.sh\nindex 114785586a..4287ea8621 100644\n--- a/t/lib-gpg.sh\n+++ b/t/lib-gpg.sh\n@@ -51,6 +51,27 @@ test_lazy_prereq GPG '\n \tesac\n '\n \n+test_lazy_prereq GPG2 '\n+\tgpg_version=$(gpg --version 2>&1)\n+\ttest $? != 127 || exit 1\n+\n+\tcase \"$gpg_version\" in\n+\t!\"gpg (GnuPG) 2.\"*)\n+\t\tsay \"This test requires a GPG version >= v2.0.0\"\n+\t\texit 1\n+\t\t;;\n+\t*)\n+\t\t(gpgconf --kill all || : ) &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" --import \\\n+\t\t\t\"$TEST_DIRECTORY\"/lib-gpg/keyring.gpg &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" --import-ownertrust \\\n+\t\t\t\"$TEST_DIRECTORY\"/lib-gpg/ownertrust &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" </dev/null >/dev/null \\\n+\t\t\t--sign -u committer@example.com\n+\t\t;;\n+\tesac\n+'\n+\n test_lazy_prereq GPGSM '\n \ttest_have_prereq GPG &&\n \t# Available key info:\ndiff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\nindex ccbc416402..96b316ae01 100755\n--- a/t/t7510-signed-commit.sh\n+++ b/t/t7510-signed-commit.sh\n@@ -218,6 +218,13 @@ test_expect_success GPG 'amending already signed commit' '\n \t! grep \"BAD signature from\" actual\n '\n \n+test_expect_success GPG2 'bare signature' '\n+\tgit verify-commit fifth-signed 2>expect &&\n+\techo \"\" >>expect &&\n+\tgit log -1 --format=\"%GG\" fifth-signed >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success GPG 'show good signature with custom format' '\n \tcat >expect <<-\\EOF &&\n \tG\n-- \n2.41.0.rc0\n\n"},{"id":"477788","messageId":"20230529192209.17747-3-five231003@gmail.com","threadId":"59804","inReplyTo":"20230529192209.17747-1-five231003@gmail.com","subject":"[PATCH 2/2] ref-filter: add new \"signature\" atom","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-05-29T18:32:06Z","receivedAt":"2023-05-29T19:23:51Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"Duplicate the code for outputting the signature and it's other\nparameters for commits and tags in ref-filter from pretty. In the\nfuture, this will help in getting rid of the current duplicate\nimplementations of such logic everywhere, when ref-filter can do\neverything that pretty is doing.\n\nThe new atom \"signature\" and it's friends are equivalent to the existing\npretty formats as follows:\n\n\t%(signature) = %GG\n\t%(signature:grade) = %G?\n\t%(siganture:signer) = %GS\n\t%(signature:key) = %GK\n\t%(signature:fingerprint) = %GF\n\t%(signature:primarykeyfingerprint) = %GP\n\t%(signature:trustlevel) = %GT\n\nCo-authored-by: Hariom Verma <hariom18599@gmail.com>\nCo-authored-by: Jaydeep Das <jaydeepjd.8914@gmail.com>\nCo-authored-by: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Hariom Verma <hariom18599@gmail.com>\nSigned-off-by: Kousik Sanagavarapu <five231003@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  27 +++++\n ref-filter.c                       | 111 ++++++++++++++++-\n t/t6300-for-each-ref.sh            | 189 +++++++++++++++++++++++++++++\n 3 files changed, 325 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 1e215d4e73..2dbb95252f 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -221,6 +221,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 4991cd4f7a..bbab2d9528 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -150,6 +150,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@@ -215,6 +216,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@@ -407,8 +412,37 @@ static int subject_atom_parser(struct ref_format *format UNUSED,\n \treturn 0;\n }\n \n-static int trailers_atom_parser(struct ref_format *format UNUSED,\n-\t\t\t\tstruct used_atom *atom,\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,\n+\t\t\t\t struct used_atom *atom,\n+\t\t\t\t const char *arg, struct strbuf *err)\n+{\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 \tatom->u.contents.trailer_opts.no_divider = 1;\n@@ -668,6 +702,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@@ -1405,6 +1440,77 @@ 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+\tint signature_checked = 0;\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 (!signature_checked) {\n+\t\t\tcheck_commit_signature(commit, &sigc);\n+\t\t\tsignature_checked = 1;\n+\t\t}\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+\tif (signature_checked)\n+\t\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@@ -1598,6 +1704,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 5c00607608..98237beac7 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@@ -1522,4 +1523,192 @@ test_expect_success 'git for-each-ref with non-existing refs' '\n \ttest_must_be_empty actual\n '\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 'setup: signature gpg' '\n+\tgit checkout -b signed &&\n+\n+\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n+\n+\techo \"1\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -S -m \"file: 1\" &&\n+\tgit tag first-signed &&\n+\n+\techo \"2\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -m \"file: 2\" &&\n+\tgit tag second-unsigned &&\n+\n+\tgit config commit.gpgSign 1 &&\n+\techo \"3\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\techo \"4\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -SB7227189 -m \"file: 4\" &&\n+\tgit tag fourth-signed &&\n+\n+\techo \"5\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\techo \"6\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ &&\n+\tgit tag fifth-signed HEAD^ &&\n+\tgit tag sixth-signed &&\n+\n+\techo \"7\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n+\tgit tag seventh-unsigned\n+'\n+\n+test_expect_success GPGSSH 'setup: signature ssh' '\n+\ttest_config gpg.format ssh &&\n+\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n+\techo \"8\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -S -m \"file: 8\" &&\n+\tgit tag eighth-signed-ssh\n+'\n+\n+test_expect_success GPG2 'bare signature atom' '\n+\tgit verify-commit first-signed 2>out.raw &&\n+\tgrep -Ev \"checking the trustdb|PGP trust model\" out.raw >out &&\n+\thead -3 out >expect &&\n+\ttail -1 out >>expect &&\n+\techo \"\" >>expect &&\n+\tgit for-each-ref refs/tags/first-signed \\\n+\t\t--format=\"%(signature)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\tgit verify-commit first-signed &&\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/tags/first-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+test_expect_success GPGSSH 'show good signature with custom format\n+\t\t\t    with ssh' '\n+\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n+\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n+\tcat >expect.tmpl <<-\\EOF &&\n+\tG\n+\tFINGERPRINT\n+\tprincipal with number 1\n+\tFINGERPRINT\n+\n+\tEOF\n+\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n+\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'signature atom with grade option and bad signature' '\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^file: 3/file: 3 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 \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\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/fourth-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\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/fourth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\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/sixth-signed \\\n+\t\t--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+\t13B6F51ECDDE430D\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n+\t\trefs/tags/sixth-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+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\tgit for-each-ref refs/tags/seventh-unsigned \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.41.0.rc0\n\n"},{"id":"477861","messageId":"CAP8UFD1FkUJ0t0OosendGBQWZJC6AbntN4r7GmaVNVKHwHEc=w@mail.gmail.com","threadId":"59804","inReplyTo":"20230529192209.17747-2-five231003@gmail.com","subject":"Re: [PATCH 1/2] t/lib-gpg: introduce new prereq GPG2","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2023-06-01T08:39:16Z","receivedAt":"2023-06-01T08:40:29Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Mon, May 29, 2023 at 9:23 PM Kousik Sanagavarapu\n<five231003@gmail.com> wrote:\n>\n> GnuPG v2.0.0 released in 2006, which according to its release notes\n>\n>         https://gnupg.org/download/release_notes.html\n>\n> is the \"First stable version of GnuPG integrating OpenPGP and S/MIME\".\n>\n> Use this version or it's successors for tests that will fail for\n> versions less than v2.0.0 because of the difference in the output on\n> stderr between the versions (v2.* vs v0.* or v2.* vs v1.*). Skip if\n> the GPG version detected is less than v2.0.0.\n\nYeah, I think it's reasonable to stop worrying about the output of GPG\nversions that are 17 year old.\n\n> +test_lazy_prereq GPG2 '\n> +       gpg_version=$(gpg --version 2>&1)\n> +       test $? != 127 || exit 1\n> +\n> +       case \"$gpg_version\" in\n> +       !\"gpg (GnuPG) 2.\"*)\n\nMaybe something like `\"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.\"*)` would be\nbetter, as it would allow versions 3.X, 4.X, etc if they are ever\nreleased.\n\n> +               say \"This test requires a GPG version >= v2.0.0\"\n> +               exit 1\n> +               ;;\n\n> diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\n> index ccbc416402..96b316ae01 100755\n> --- a/t/t7510-signed-commit.sh\n> +++ b/t/t7510-signed-commit.sh\n> @@ -218,6 +218,13 @@ test_expect_success GPG 'amending already signed commit' '\n>         ! grep \"BAD signature from\" actual\n>  '\n>\n> +test_expect_success GPG2 'bare signature' '\n> +       git verify-commit fifth-signed 2>expect &&\n> +       echo \"\" >>expect &&\n\nWe sometimes use `echo \"\" >` to add a new line, but we much more often\nuse just `echo >` for that purpose:\n\n$ git grep 'echo >' | wc -l\n339\n$ git grep 'echo \"\" >' | wc -l\n16\n\n> +       git log -1 --format=\"%GG\" fifth-signed >actual &&\n> +       test_cmp expect actual\n> +'\n\nThanks!\n"},{"id":"477862","messageId":"CAP8UFD0zQoc9zgMLdxNavMmPyjk5Orp1PDx5gpDEyuv5sv7h8w@mail.gmail.com","threadId":"59804","inReplyTo":"20230529192209.17747-3-five231003@gmail.com","subject":"Re: [PATCH 2/2] ref-filter: add new \"signature\" atom","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2023-06-01T08:58:53Z","receivedAt":"2023-06-01T08:59:23Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Mon, May 29, 2023 at 9:23 PM Kousik Sanagavarapu\n<five231003@gmail.com> wrote:\n\n> +test_expect_success GPG 'setup: signature gpg' '\n\nMaybe something like \"setup: sign some commits using gpg\" or \"setup\nfor signature atom using gpg\" would be a bit clearer.\n\n> +test_expect_success GPGSSH 'setup: signature ssh' '\n\nHere also something like \"setup: sign some commits using ssh\" or\n\"setup for signature atom using ssh\" would be a bit clearer.\n\n> +       test_config gpg.format ssh &&\n> +       test_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n\nI wonder if this test should try to clean up the config a bit after\nitself. The previous test used:\n\n`test_when_finished \"test_unconfig commit.gpgSign\"`\n\nand maybe this one could do something similar.\n\n> +       echo \"8\" >file &&\n> +       test_tick &&\n> +       git commit -a -S -m \"file: 8\" &&\n> +       git tag eighth-signed-ssh\n> +'\n> +\n> +test_expect_success GPG2 'bare signature atom' '\n> +       git verify-commit first-signed 2>out.raw &&\n> +       grep -Ev \"checking the trustdb|PGP trust model\" out.raw >out &&\n> +       head -3 out >expect &&\n> +       tail -1 out >>expect &&\n> +       echo \"\" >>expect &&\n\n`echo >>expect` is more common to add a new line.\n\n> +       git for-each-ref refs/tags/first-signed \\\n> +               --format=\"%(signature)\" >actual &&\n> +       test_cmp expect actual\n> +'\n"},{"id":"477863","messageId":"CAP8UFD1JDQ=A8T_MYCG2yRDvZBjN0iDSLE69mxb7deGRupbpKg@mail.gmail.com","threadId":"59804","inReplyTo":"20230529192209.17747-1-five231003@gmail.com","subject":"Re: [GSoC][PATCH 0/2] Add new \"signature\" atom","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2023-06-01T09:11:14Z","receivedAt":"2023-06-01T09:11:31Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Mon, May 29, 2023 at 9:50 PM Kousik Sanagavarapu\n<five231003@gmail.com> wrote:\n\n> PATCH 1/2 introduces a new prereq GPG2 for the purpose of testing stuff\n> that breaks with GPG version <= v2. This is evident from the CI failure\n> in [1] that was sent with the same subject as this series (more info\n> below).\n>\n> PATCH 2/2 adds a new \"signature\" atom, which is a duplication of the %G*\n> formats in pretty. This was sent before by Nsengiyumva Wilberforce and\n> was in \"seen\" until May and was removed because of CI failure. I have\n> built upon it and have done some minor changes to it. The CI jobs are\n> now successful which can be found at [2].\n>\n> [1]: Patch\n>\n>         https://lore.kernel.org/git/20230311210607.64927-1-nsengiyumvawilberforce@gmail.com/\n\nI don't think it's a big issue, but, as there are only minor change,\nit might have been easier to refer to the previous work by just\ncalling this a v6 of the same patch series and using the\n--in-reply-to=<message id> option of git format-patch to send it so it\nwould be in the same email thread as the previous work.\n\nNow that a new series was started though, I think it makes more sense\nfor any improvement to this series to just be called v2 and be sent\nusing --in-reply-to=<message id> so that it is in the same thread as\nthis series.\n\nThanks!\n"},{"id":"477916","messageId":"20230602023105.17979-1-five231003@gmail.com","threadId":"59804","inReplyTo":"20230529192209.17747-1-five231003@gmail.com","subject":"[PATCH v2 0/2] Add new \"signature\" atom","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-02T02:11:53Z","receivedAt":"2023-06-02T02:31:26Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"Hi,\n\nThanks for the review.\n\nChanges since v1:\n\n    PATCH 1/2 -\n\tChanged the condition so that prereq GPG2 will only fail\n\tif we have GPG v0.* or v1.* instead of failing when we\n\tdon't have v2.* (this will have an effect if in the future\n\tGPG v3.*, v4.* were introduced).\n\n    PATCH 2/2 -\n\tRenamed the setup tests to be more clear about their purpose.\n\n    Common to both the patches is the change where we introduce a\n    newline to a file. Use \"echo >\" instead of \"echo \"\" >\".\n\nI have also rebased this to be on top of v2.41.0, the previous version\nwas on top of v2.41.0-rc0.\n\nRange-diff against v1:\n\n1:  5c97d11b79 ! 1:  87465ef1a8 t/lib-gpg: introduce new prereq GPG2\n    @@ t/lib-gpg.sh: test_lazy_prereq GPG '\n     +  test $? != 127 || exit 1\n     +\n     +  case \"$gpg_version\" in\n    -+  !\"gpg (GnuPG) 2.\"*)\n    ++  \"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n     +          say \"This test requires a GPG version >= v2.0.0\"\n     +          exit 1\n     +          ;;\n    @@ t/t7510-signed-commit.sh: test_expect_success GPG 'amending\nalready signed commi\n      \n     +test_expect_success GPG2 'bare signature' '\n     +  git verify-commit fifth-signed 2>expect &&\n    -+  echo \"\" >>expect &&\n    ++  echo >>expect &&\n     +  git log -1 --format=\"%GG\" fifth-signed >actual &&\n     +  test_cmp expect actual\n     +'\n2:  e89f14283d ! 2:  690869aa47 ref-filter: add new \"signature\" atom\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\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 'setup: signature gpg' '\n    ++test_expect_success GPG 'setup for signature atom using gpg' '\n     +  git checkout -b signed &&\n     +\n     +  test_when_finished \"test_unconfig commit.gpgSign\" &&\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +  git tag seventh-unsigned\n     +'\n     +\n    -+test_expect_success GPGSSH 'setup: signature ssh' '\n    ++test_expect_success GPGSSH 'setup for signature atom using ssh' '\n    ++  test_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n    ++\n     +  test_config gpg.format ssh &&\n     +  test_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n     +  echo \"8\" >file &&\n    @@ t/t6300-for-each-ref.sh: test_expect_success 'git for-each-ref\nwith non-existing\n     +  grep -Ev \"checking the trustdb|PGP trust model\" out.raw >out &&\n     +  head -3 out >expect &&\n     +  tail -1 out >>expect &&\n    -+  echo \"\" >>expect &&\n    ++  echo  >>expect &&\n     +  git for-each-ref refs/tags/first-signed \\\n     +          --format=\"%(signature)\" >actual &&\n     +  test_cmp expect actual\n\nKousik Sanagavarapu (2):\n  t/lib-gpg: introduce new prereq GPG2\n  ref-filter: add new \"signature\" atom\n\n Documentation/git-for-each-ref.txt |  27 ++++\n ref-filter.c                       | 111 ++++++++++++++++-\n t/lib-gpg.sh                       |  21 ++++\n t/t6300-for-each-ref.sh            | 191 +++++++++++++++++++++++++++++\n t/t7510-signed-commit.sh           |   7 ++\n 5 files changed, 355 insertions(+), 2 deletions(-)\n\n-- \n2.41.0\n\n"},{"id":"477917","messageId":"20230602023105.17979-2-five231003@gmail.com","threadId":"59804","inReplyTo":"20230602023105.17979-1-five231003@gmail.com","subject":"[PATCH v2 1/2] t/lib-gpg: introduce new prereq GPG2","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-02T02:11:54Z","receivedAt":"2023-06-02T02:32:02Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"GnuPG v2.0.0 released in 2006, which according to its release notes\n\n\thttps://gnupg.org/download/release_notes.html\n\nis the \"First stable version of GnuPG integrating OpenPGP and S/MIME\".\n\nUse this version or it's successors for tests that will fail for\nversions less than v2.0.0 because of the difference in the output on\nstderr between the versions (v2.* vs v0.* or v2.* vs v1.*). Skip if\nthe GPG version detected is less than v2.0.0.\n\nDo not, however, remove the existing prereq GPG yet since a lot of tests\nstill work with the prereq GPG (that is even with versions v0.* or v1.*)\nand some systems still use these versions.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Hariom Verma <hariom18599@gmail.com>\nSigned-off-by: Kousik Sanagavarapu <five231003@gmail.com>\n---\n t/lib-gpg.sh             | 21 +++++++++++++++++++++\n t/t7510-signed-commit.sh |  7 +++++++\n 2 files changed, 28 insertions(+)\n\ndiff --git a/t/lib-gpg.sh b/t/lib-gpg.sh\nindex 114785586a..aba8f861ed 100644\n--- a/t/lib-gpg.sh\n+++ b/t/lib-gpg.sh\n@@ -51,6 +51,27 @@ test_lazy_prereq GPG '\n \tesac\n '\n \n+test_lazy_prereq GPG2 '\n+\tgpg_version=$(gpg --version 2>&1)\n+\ttest $? != 127 || exit 1\n+\n+\tcase \"$gpg_version\" in\n+\t\"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n+\t\tsay \"This test requires a GPG version >= v2.0.0\"\n+\t\texit 1\n+\t\t;;\n+\t*)\n+\t\t(gpgconf --kill all || : ) &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" --import \\\n+\t\t\t\"$TEST_DIRECTORY\"/lib-gpg/keyring.gpg &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" --import-ownertrust \\\n+\t\t\t\"$TEST_DIRECTORY\"/lib-gpg/ownertrust &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" </dev/null >/dev/null \\\n+\t\t\t--sign -u committer@example.com\n+\t\t;;\n+\tesac\n+'\n+\n test_lazy_prereq GPGSM '\n \ttest_have_prereq GPG &&\n \t# Available key info:\ndiff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\nindex ccbc416402..0d2dd29fe6 100755\n--- a/t/t7510-signed-commit.sh\n+++ b/t/t7510-signed-commit.sh\n@@ -218,6 +218,13 @@ test_expect_success GPG 'amending already signed commit' '\n \t! grep \"BAD signature from\" actual\n '\n \n+test_expect_success GPG2 'bare signature' '\n+\tgit verify-commit fifth-signed 2>expect &&\n+\techo >>expect &&\n+\tgit log -1 --format=\"%GG\" fifth-signed >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success GPG 'show good signature with custom format' '\n \tcat >expect <<-\\EOF &&\n \tG\n-- \n2.41.0\n\n"},{"id":"477918","messageId":"20230602023105.17979-3-five231003@gmail.com","threadId":"59804","inReplyTo":"20230602023105.17979-1-five231003@gmail.com","subject":"[PATCH v2 2/2] ref-filter: add new \"signature\" atom","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-02T02:11:55Z","receivedAt":"2023-06-02T02:32:19Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"Duplicate the code for outputting the signature and it's other\nparameters for commits and tags in ref-filter from pretty. In the\nfuture, this will help in getting rid of the current duplicate\nimplementations of such logic everywhere, when ref-filter can do\neverything that pretty is doing.\n\nThe new atom \"signature\" and it's friends are equivalent to the existing\npretty formats as follows:\n\n\t%(signature) = %GG\n\t%(signature:grade) = %G?\n\t%(siganture:signer) = %GS\n\t%(signature:key) = %GK\n\t%(signature:fingerprint) = %GF\n\t%(signature:primarykeyfingerprint) = %GP\n\t%(signature:trustlevel) = %GT\n\nCo-authored-by: Hariom Verma <hariom18599@gmail.com>\nCo-authored-by: Jaydeep Das <jaydeepjd.8914@gmail.com>\nCo-authored-by: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Hariom Verma <hariom18599@gmail.com>\nSigned-off-by: Kousik Sanagavarapu <five231003@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  27 ++++\n ref-filter.c                       | 111 ++++++++++++++++-\n t/t6300-for-each-ref.sh            | 191 +++++++++++++++++++++++++++++\n 3 files changed, 327 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 1e215d4e73..2dbb95252f 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -221,6 +221,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 4991cd4f7a..bbab2d9528 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -150,6 +150,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@@ -215,6 +216,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@@ -407,8 +412,37 @@ static int subject_atom_parser(struct ref_format *format UNUSED,\n \treturn 0;\n }\n \n-static int trailers_atom_parser(struct ref_format *format UNUSED,\n-\t\t\t\tstruct used_atom *atom,\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,\n+\t\t\t\t struct used_atom *atom,\n+\t\t\t\t const char *arg, struct strbuf *err)\n+{\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 \tatom->u.contents.trailer_opts.no_divider = 1;\n@@ -668,6 +702,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@@ -1405,6 +1440,77 @@ 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+\tint signature_checked = 0;\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 (!signature_checked) {\n+\t\t\tcheck_commit_signature(commit, &sigc);\n+\t\t\tsignature_checked = 1;\n+\t\t}\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+\tif (signature_checked)\n+\t\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@@ -1598,6 +1704,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 5c00607608..6e6ec852b5 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@@ -1522,4 +1523,194 @@ test_expect_success 'git for-each-ref with non-existing refs' '\n \ttest_must_be_empty actual\n '\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 'setup for signature atom using gpg' '\n+\tgit checkout -b signed &&\n+\n+\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n+\n+\techo \"1\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -S -m \"file: 1\" &&\n+\tgit tag first-signed &&\n+\n+\techo \"2\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -m \"file: 2\" &&\n+\tgit tag second-unsigned &&\n+\n+\tgit config commit.gpgSign 1 &&\n+\techo \"3\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\techo \"4\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -SB7227189 -m \"file: 4\" &&\n+\tgit tag fourth-signed &&\n+\n+\techo \"5\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\techo \"6\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ &&\n+\tgit tag fifth-signed HEAD^ &&\n+\tgit tag sixth-signed &&\n+\n+\techo \"7\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n+\tgit tag seventh-unsigned\n+'\n+\n+test_expect_success GPGSSH 'setup for signature atom using ssh' '\n+\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n+\n+\ttest_config gpg.format ssh &&\n+\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n+\techo \"8\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -S -m \"file: 8\" &&\n+\tgit tag eighth-signed-ssh\n+'\n+\n+test_expect_success GPG2 'bare signature atom' '\n+\tgit verify-commit first-signed 2>out.raw &&\n+\tgrep -Ev \"checking the trustdb|PGP trust model\" out.raw >out &&\n+\thead -3 out >expect &&\n+\ttail -1 out >>expect &&\n+\techo  >>expect &&\n+\tgit for-each-ref refs/tags/first-signed \\\n+\t\t--format=\"%(signature)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\tgit verify-commit first-signed &&\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/tags/first-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+test_expect_success GPGSSH 'show good signature with custom format\n+\t\t\t    with ssh' '\n+\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n+\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n+\tcat >expect.tmpl <<-\\EOF &&\n+\tG\n+\tFINGERPRINT\n+\tprincipal with number 1\n+\tFINGERPRINT\n+\n+\tEOF\n+\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n+\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'signature atom with grade option and bad signature' '\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^file: 3/file: 3 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 \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\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/fourth-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\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/fourth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\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/sixth-signed \\\n+\t\t--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+\t13B6F51ECDDE430D\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n+\t\trefs/tags/sixth-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+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\tgit for-each-ref refs/tags/seventh-unsigned \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.41.0\n\n"},{"id":"477920","messageId":"CAP8UFD2wcnNaihGv=SQ_77OLQ5PN3DG73rnh2F_C_j+BFTcCyw@mail.gmail.com","threadId":"59804","inReplyTo":"20230602023105.17979-2-five231003@gmail.com","subject":"Re: [PATCH v2 1/2] t/lib-gpg: introduce new prereq GPG2","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2023-06-02T06:50:18Z","receivedAt":"2023-06-02T06:51:05Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Fri, Jun 2, 2023 at 4:31 AM Kousik Sanagavarapu <five231003@gmail.com> wrote:\n\n> +test_lazy_prereq GPG2 '\n> +       gpg_version=$(gpg --version 2>&1)\n> +       test $? != 127 || exit 1\n> +\n> +       case \"$gpg_version\" in\n> +       \"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n\ns/\"gpg (GnuPG) 1.*\"/\"gpg (GnuPG) 1.\"*/\n\nI am not sure if it changes anything, but for testing if we have v0\nhere and v1.0.6 in the \"test_lazy_prereq PGP\", we put the '*'\ncharacter outside the double quoted string.\n\n> +               say \"This test requires a GPG version >= v2.0.0\"\n> +               exit 1\n> +               ;;\n> +       *)\n> +               (gpgconf --kill all || : ) &&\n> +               gpg --homedir \"${GNUPGHOME}\" --import \\\n> +                       \"$TEST_DIRECTORY\"/lib-gpg/keyring.gpg &&\n> +               gpg --homedir \"${GNUPGHOME}\" --import-ownertrust \\\n> +                       \"$TEST_DIRECTORY\"/lib-gpg/ownertrust &&\n> +               gpg --homedir \"${GNUPGHOME}\" </dev/null >/dev/null \\\n> +                       --sign -u committer@example.com\n> +               ;;\n> +       esac\n> +'\n"},{"id":"477922","messageId":"xmqqzg5i8gpt.fsf@gitster.g","threadId":"59804","inReplyTo":"20230602023105.17979-1-five231003@gmail.com","subject":"Re: [PATCH v2 0/2] Add new \"signature\" atom","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-06-02T07:29:34Z","receivedAt":"2023-06-02T07:30:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kousik Sanagavarapu <five231003@gmail.com> writes:\n\n> I have also rebased this to be on top of v2.41.0, the previous version\n> was on top of v2.41.0-rc0.\n\nI am still feverish and feeling weak so no real review from me yet,\nbut there is one thing that immediately jumped at me.\n\n> Range-diff against v1:\n>\n> 1:  5c97d11b79 ! 1:  87465ef1a8 t/lib-gpg: introduce new prereq GPG2\n>     @@ t/lib-gpg.sh: test_lazy_prereq GPG '\n>      +  test $? != 127 || exit 1\n>      +\n>      +  case \"$gpg_version\" in\n>     -+  !\"gpg (GnuPG) 2.\"*)\n>     ++  \"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n\nThe last '*' being inside double-quote would not be what you\nintended, I suspect?\n"},{"id":"477923","messageId":"CAPig+cRmY3oDJLVKsBgKVZc-bJ7hArzk2eib9Ra5timC=Xz_kA@mail.gmail.com","threadId":"59804","inReplyTo":"xmqqzg5i8gpt.fsf@gitster.g","subject":"Re: [PATCH v2 0/2] Add new \"signature\" atom","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2023-06-02T07:51:01Z","receivedAt":"2023-06-02T07:51:16Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Jun 2, 2023 at 3:33 AM Junio C Hamano <gitster@pobox.com> wrote:\n> Kousik Sanagavarapu <five231003@gmail.com> writes:\n> > I have also rebased this to be on top of v2.41.0, the previous version\n> > was on top of v2.41.0-rc0.\n>\n> I am still feverish and feeling weak so no real review from me yet,\n> but there is one thing that immediately jumped at me.\n>\n> > 1:  5c97d11b79 ! 1:  87465ef1a8 t/lib-gpg: introduce new prereq GPG2\n> >     @@ t/lib-gpg.sh: test_lazy_prereq GPG '\n> >      +  test $? != 127 || exit 1\n> >      +\n> >      +  case \"$gpg_version\" in\n> >     -+  !\"gpg (GnuPG) 2.\"*)\n> >     ++  \"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n>\n> The last '*' being inside double-quote would not be what you\n> intended, I suspect?\n\nI noticed that, as well, when running my eye over the range-diff.\nMoreover, I wondered if using `[01]` to avoid the repetition would be\nworthwhile:\n\n    case \"$gpg_version\" in\n    \"gpg (GnuPG) \"[01].*)\n\nthough, of course, it's subjective whether that is clearer.\n"},{"id":"477924","messageId":"ZHmnFiykAixYUCgm@ugly","threadId":"59804","inReplyTo":"20230602023105.17979-3-five231003@gmail.com","subject":"Re: [PATCH v2 2/2] ref-filter: add new \"signature\" atom","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2023-06-02T08:23:50Z","receivedAt":"2023-06-02T08:23:56Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"just some nitpicks from me:\n\nOn Fri, Jun 02, 2023 at 07:41:55AM +0530, Kousik Sanagavarapu wrote:\n>Duplicate the code for outputting the signature and it's other\n>\n\"its\", not \"it's\". this repeats all over the place, so please look up \nthe rules.\n\n>+static void grab_signature(struct atom_value *val, int deref, struct \n>object *obj)\n>+{\n\n>+\t\tif (!skip_prefix(name, \"signature\", &name) || (*name &&\n>+\t\t\t*name != ':'))\n>+\t\t\tcontinue;\n>\nthis looks confusing.\n\nbut there appears to be no exception to the \"no excess braces\" rule for \nmulti-line conditions, unlike for bodies - maybe there should be?\n\nalso, i would wrap logically, not right at the margin, so\n\n\tif (!skip_prefix(name, \"signature\", &name) ||\n\t    (*name && *name != ':'))\n\n>+\t\telse if (atom->u.signature.option == S_GRADE) {\n>\nbrace symmetry rule violated.\n\n>+\t\t\tcase 'R':\n>+\t\t\t\tv->s = xstrfmt(\"%c\", (char)sigc.result);\n>+\t\t\t}\n>\nomitting the final 'break' looks like bad style to me.\n\nregards,\nossi\n"},{"id":"477948","messageId":"ZHnnZ_plMZ-wdO4M@fivlite-virtual-machine","threadId":"59804","inReplyTo":"CAP8UFD2wcnNaihGv=SQ_77OLQ5PN3DG73rnh2F_C_j+BFTcCyw@mail.gmail.com","subject":"Re: [PATCH v2 1/2] t/lib-gpg: introduce new prereq GPG2","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-02T12:58:15Z","receivedAt":"2023-06-02T12:58:33Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"On Fri, Jun 02, 2023 at 08:50:18AM +0200, Christian Couder wrote:\n> On Fri, Jun 2, 2023 at 4:31 AM Kousik Sanagavarapu <five231003@gmail.com> wrote:\n> \n> > +test_lazy_prereq GPG2 '\n> > +       gpg_version=$(gpg --version 2>&1)\n> > +       test $? != 127 || exit 1\n> > +\n> > +       case \"$gpg_version\" in\n> > +       \"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n> \n> s/\"gpg (GnuPG) 1.*\"/\"gpg (GnuPG) 1.\"*/\n> \n> I am not sure if it changes anything, but for testing if we have v0\n> here and v1.0.6 in the \"test_lazy_prereq PGP\", we put the '*'\n> character outside the double quoted string.\n\nIt does seem that it changes things, thanks for catching. I'll reroll\nwith the necessary changes (there are also some style issues that\nOswald pointed out).\n\nThanks\n"},{"id":"477955","messageId":"ZHnq-e0YAJSlAi9k@five231003","threadId":"59804","inReplyTo":"xmqqzg5i8gpt.fsf@gitster.g","subject":"Re: [PATCH v2 0/2] Add new \"signature\" atom","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-02T13:13:29Z","receivedAt":"2023-06-02T13:15:03Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"On Fri, Jun 02, 2023 at 04:29:34PM +0900, Junio C Hamano wrote:\n> Kousik Sanagavarapu <five231003@gmail.com> writes:\n> \n> > I have also rebased this to be on top of v2.41.0, the previous version\n> > was on top of v2.41.0-rc0.\n> \n> I am still feverish and feeling weak so no real review from me yet,\n\nPlease take care and get well soon.\n\n> but there is one thing that immediately jumped at me.\n>\n> > Range-diff against v1:\n> >\n> > 1:  5c97d11b79 ! 1:  87465ef1a8 t/lib-gpg: introduce new prereq GPG2\n> >     @@ t/lib-gpg.sh: test_lazy_prereq GPG '\n> >      +  test $? != 127 || exit 1\n> >      +\n> >      +  case \"$gpg_version\" in\n> >     -+  !\"gpg (GnuPG) 2.\"*)\n> >     ++  \"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n> \n> The last '*' being inside double-quote would not be what you\n> intended, I suspect?\n\nYeah, that was kind of a typo, thanks for catching it.\n\nThanks\n"},{"id":"477994","messageId":"xmqqilc58kn9.fsf@gitster.g","threadId":"59804","inReplyTo":"CAPig+cRmY3oDJLVKsBgKVZc-bJ7hArzk2eib9Ra5timC=Xz_kA@mail.gmail.com","subject":"Re: [PATCH v2 0/2] Add new \"signature\" atom","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-06-03T00:16:58Z","receivedAt":"2023-06-03T00:17:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> Moreover, I wondered if using `[01]` to avoid the repetition would be\n> worthwhile:\n>\n>     case \"$gpg_version\" in\n>     \"gpg (GnuPG) \"[01].*)\n>\n> though, of course, it's subjective whether that is clearer.\n\nExcellent.\n\nI'd say that the value of your version is primarily that it is much\nless error prone than repeating the constant string part that can be\nmisspelt.  The glob limiting \"begins with either '0' or '1' followed\nby a dot\" might be slightly less easier to understand for less trained\neyes, but eyes will not remain untrained forever, so it is OK.\n"},{"id":"478032","messageId":"20230604185815.15761-1-five231003@gmail.com","threadId":"59804","inReplyTo":"20230602023105.17979-1-five231003@gmail.com","subject":"[PATCH v3 0/2] Add new \"signature\" atom","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-04T18:22:45Z","receivedAt":"2023-06-04T18:58:52Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"Hi,\n\nThanks for the reviews.\n\nChanges since v2:\n\n  PATCH 1/2 -\n    Changed \"it's\" to \"its\" in the commit message.\n\n    Changed the `case` statement in prereq GPG2 to use the glob pattern\n    instead. This has the advantage of being precise and less typo-prone.\n\n  PATCH 2/2 -\n    Changed \"it's\" to \"its\" in the commit message.\n\n    Changed the `if else` to `switch` when handling options in\n    grab_siganture(). This increases the readability of code unlike the\n    previous `if else` checking for the type of option, which also\n    didn't comply with the style.\n\n    The same kind of refactoring of can be done in other parts of\n    ref-filter as well. ZheNing Hu has done some work on it [1], but it\n    looks like they were generated by some kind of a script and there\n    are unnecessary braces around.\n\n[1]: https://lore.kernel.org/git/2321b873d0c0223e553492d80ced2a51d8ce7281.1629189701.git.gitgitgadget@gmail.com/ \nRange-diff against v2:\n\n1:  87465ef1a8 ! 1:  a7ed6628e0 t/lib-gpg: introduce new prereq GPG2\n    @@ Commit message\n     \n         is the \"First stable version of GnuPG integrating OpenPGP and\nS/MIME\".\n     \n    -    Use this version or it's successors for tests that will fail\n         for\n    +    Use this version or its successors for tests that will fail for\n         versions less than v2.0.0 because of the difference in the\noutput on\n         stderr between the versions (v2.* vs v0.* or v2.* vs v1.*).\nSkip if\n         the GPG version detected is less than v2.0.0.\n    @@ t/lib-gpg.sh: test_lazy_prereq GPG '\n     +  test $? != 127 || exit 1\n     +\n     +  case \"$gpg_version\" in\n    -+  \"gpg (GnuPG) 0.\"* | \"gpg (GnuPG) 1.*\")\n    ++  \"gpg (GnuPG) \"[01].*)\n     +          say \"This test requires a GPG version >= v2.0.0\"\n     +          exit 1\n     +          ;;\n2:  690869aa47 ! 2:  b6da96dab2 ref-filter: add new \"signature\" atom\n    @@ Metadata\n      ## Commit message ##\n         ref-filter: add new \"signature\" atom\n     \n    -    Duplicate the code for outputting the signature and it's other\n    +    Duplicate the code for outputting the signature and its other\n         parameters for commits and tags in ref-filter from pretty. In\nthe\n         future, this will help in getting rid of the current duplicate\n         implementations of such logic everywhere, when ref-filter can\ndo\n         everything that pretty is doing.\n     \n    -    The new atom \"signature\" and it's friends are equivalent to the\n         existing\n    +    The new atom \"signature\" and its friends are equivalent to the\nexisting\n         pretty formats as follows:\n     \n                 %(signature) = %GG\n    @@ Documentation/git-for-each-ref.txt: symref::\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    ++  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    ++  The trust level of the GPG signature of a commit. Possible\n     +  outputs are `ultimate`, `fully`, `marginal`, `never` and\n`undefined`.\n     +\n      worktreepath::\n    @@ ref-filter.c: static struct used_atom {\n                } email_option;\n     +          struct {\n     +                  enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,\n    -+                         S_FINGERPRINT, S_PRI_KEY_FP,\nS_TRUST_LEVEL} option;\n    ++                         S_FINGERPRINT, S_PRI_KEY_FP,\nS_TRUST_LEVEL } option;\n     +          } signature;\n                struct refname_atom refname;\n                char *head;\n    @@ ref-filter.c: static void grab_person(const char *who, struct\natom_value *val, i\n     +          struct used_atom *atom = &used_atom[i];\n     +          const char *name = atom->name;\n     +          struct atom_value *v = &val[i];\n    ++          int opt;\n     +\n     +          if (!!deref != (*name == '*'))\n     +                  continue;\n     +          if (deref)\n     +                  name++;\n     +\n    -+          if (!skip_prefix(name, \"signature\", &name) || (*name &&\n    -+                  *name != ':'))\n    ++          if (!skip_prefix(name, \"signature\", &name) ||\n    ++              (*name && *name != ':'))\n     +                  continue;\n     +          if (!*name)\n     +                  name = NULL;\n     +          else\n     +                  name++;\n    -+          if (parse_signature_option(name) < 0)\n    ++\n    ++          opt = parse_signature_option(name);\n    ++          if (opt < 0)\n     +                  continue;\n     +\n     +          if (!signature_checked) {\n    @@ ref-filter.c: static void grab_person(const char *who, struct\natom_value *val, i\n     +                  signature_checked = 1;\n     +          }\n     +\n    -+          if (atom->u.signature.option == S_BARE)\n    ++          switch (opt) {\n    ++          case S_BARE:\n     +                  v->s = xstrdup(sigc.output ? sigc.output: \"\");\n    -+          else if (atom->u.signature.option == S_SIGNER)\n    ++                  break;\n    ++          case S_SIGNER:\n     +                  v->s = xstrdup(sigc.signer ? sigc.signer : \"\");\n    -+          else if (atom->u.signature.option == S_GRADE) {\n    ++                  break;\n    ++          case S_GRADE:\n     +                  switch (sigc.result) {\n     +                  case 'G':\n     +                          switch (sigc.trust_level) {\n    @@ ref-filter.c: static void grab_person(const char *who, struct\natom_value *val, i\n     +                  case 'Y':\n     +                  case 'R':\n     +                          v->s = xstrfmt(\"%c\", (char)sigc.result);\n    ++                          break;\n     +                  }\n    -+          }\n    -+          else if (atom->u.signature.option == S_KEY)\n    ++                  break;\n    ++          case 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 ?\nsigc.fingerprint : \"\");\n    -+          else if (atom->u.signature.option == S_PRI_KEY_FP)\n    -+                  v->s = xstrdup(sigc.primary_key_fingerprint ?\nsigc.primary_key_fingerprint : \"\");\n    -+          else if (atom->u.signature.option == S_TRUST_LEVEL)\n    ++                  break;\n    ++          case S_FINGERPRINT:\n    ++                  v->s = xstrdup(sigc.fingerprint ?\n    ++                                 sigc.fingerprint : \"\");\n    ++                  break;\n    ++          case S_PRI_KEY_FP:\n    ++                  v->s = xstrdup(sigc.primary_key_fingerprint ?\n    ++                                 sigc.primary_key_fingerprint :\n\"\");\n    ++                  break;\n    ++          case S_TRUST_LEVEL:\n     +                  v->s =\nxstrdup(gpg_trust_level_to_str(sigc.trust_level));\n    ++                  break;\n    ++          }\n     +  }\n    ++\n     +  if (signature_checked)\n     +          signature_check_clear(&sigc);\n     +}\n\nKousik Sanagavarapu (2):\n  t/lib-gpg: introduce new prereq GPG2\n  ref-filter: add new \"signature\" atom\n\n Documentation/git-for-each-ref.txt |  27 ++++\n ref-filter.c                       | 126 ++++++++++++++++++-\n t/lib-gpg.sh                       |  21 ++++\n t/t6300-for-each-ref.sh            | 191 +++++++++++++++++++++++++++++\n t/t7510-signed-commit.sh           |   7 ++\n 5 files changed, 370 insertions(+), 2 deletions(-)\n\n-- \n2.41.0\n\n"},{"id":"478033","messageId":"20230604185815.15761-2-five231003@gmail.com","threadId":"59804","inReplyTo":"20230604185815.15761-1-five231003@gmail.com","subject":"[PATCH v3 1/2] t/lib-gpg: introduce new prereq GPG2","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-04T18:22:46Z","receivedAt":"2023-06-04T18:59:05Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"GnuPG v2.0.0 released in 2006, which according to its release notes\n\n\thttps://gnupg.org/download/release_notes.html\n\nis the \"First stable version of GnuPG integrating OpenPGP and S/MIME\".\n\nUse this version or its successors for tests that will fail for\nversions less than v2.0.0 because of the difference in the output on\nstderr between the versions (v2.* vs v0.* or v2.* vs v1.*). Skip if\nthe GPG version detected is less than v2.0.0.\n\nDo not, however, remove the existing prereq GPG yet since a lot of tests\nstill work with the prereq GPG (that is even with versions v0.* or v1.*)\nand some systems still use these versions.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Hariom Verma <hariom18599@gmail.com>\nSigned-off-by: Kousik Sanagavarapu <five231003@gmail.com>\n---\n t/lib-gpg.sh             | 21 +++++++++++++++++++++\n t/t7510-signed-commit.sh |  7 +++++++\n 2 files changed, 28 insertions(+)\n\ndiff --git a/t/lib-gpg.sh b/t/lib-gpg.sh\nindex 114785586a..d219a09362 100644\n--- a/t/lib-gpg.sh\n+++ b/t/lib-gpg.sh\n@@ -51,6 +51,27 @@ test_lazy_prereq GPG '\n \tesac\n '\n \n+test_lazy_prereq GPG2 '\n+\tgpg_version=$(gpg --version 2>&1)\n+\ttest $? != 127 || exit 1\n+\n+\tcase \"$gpg_version\" in\n+\t\"gpg (GnuPG) \"[01].*)\n+\t\tsay \"This test requires a GPG version >= v2.0.0\"\n+\t\texit 1\n+\t\t;;\n+\t*)\n+\t\t(gpgconf --kill all || : ) &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" --import \\\n+\t\t\t\"$TEST_DIRECTORY\"/lib-gpg/keyring.gpg &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" --import-ownertrust \\\n+\t\t\t\"$TEST_DIRECTORY\"/lib-gpg/ownertrust &&\n+\t\tgpg --homedir \"${GNUPGHOME}\" </dev/null >/dev/null \\\n+\t\t\t--sign -u committer@example.com\n+\t\t;;\n+\tesac\n+'\n+\n test_lazy_prereq GPGSM '\n \ttest_have_prereq GPG &&\n \t# Available key info:\ndiff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\nindex ccbc416402..0d2dd29fe6 100755\n--- a/t/t7510-signed-commit.sh\n+++ b/t/t7510-signed-commit.sh\n@@ -218,6 +218,13 @@ test_expect_success GPG 'amending already signed commit' '\n \t! grep \"BAD signature from\" actual\n '\n \n+test_expect_success GPG2 'bare signature' '\n+\tgit verify-commit fifth-signed 2>expect &&\n+\techo >>expect &&\n+\tgit log -1 --format=\"%GG\" fifth-signed >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success GPG 'show good signature with custom format' '\n \tcat >expect <<-\\EOF &&\n \tG\n-- \n2.41.0\n\n"},{"id":"478034","messageId":"20230604185815.15761-3-five231003@gmail.com","threadId":"59804","inReplyTo":"20230604185815.15761-1-five231003@gmail.com","subject":"[PATCH v3 2/2] ref-filter: add new \"signature\" atom","fromName":"Kousik Sanagavarapu","fromEmail":"five231003@gmail.com","sentAt":"2023-06-04T18:22:47Z","receivedAt":"2023-06-04T18:59:13Z","isPatch":true,"sender":{"key":"five231003@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75560439?v=4"},"body":"Duplicate the code for outputting the signature and its other\nparameters for commits and tags in ref-filter from pretty. In the\nfuture, this will help in getting rid of the current duplicate\nimplementations of such logic everywhere, when ref-filter can do\neverything that pretty is doing.\n\nThe new atom \"signature\" and its friends are equivalent to the existing\npretty formats as follows:\n\n\t%(signature) = %GG\n\t%(signature:grade) = %G?\n\t%(siganture:signer) = %GS\n\t%(signature:key) = %GK\n\t%(signature:fingerprint) = %GF\n\t%(signature:primarykeyfingerprint) = %GP\n\t%(signature:trustlevel) = %GT\n\nCo-authored-by: Hariom Verma <hariom18599@gmail.com>\nCo-authored-by: Jaydeep Das <jaydeepjd.8914@gmail.com>\nCo-authored-by: Nsengiyumva Wilberforce <nsengiyumvawilberforce@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Hariom Verma <hariom18599@gmail.com>\nSigned-off-by: Kousik Sanagavarapu <five231003@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  27 ++++\n ref-filter.c                       | 126 ++++++++++++++++++-\n t/t6300-for-each-ref.sh            | 191 +++++++++++++++++++++++++++++\n 3 files changed, 342 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 1e215d4e73..2e0318770b 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -221,6 +221,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 4991cd4f7a..5c6924b367 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -150,6 +150,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@@ -215,6 +216,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@@ -407,8 +412,37 @@ static int subject_atom_parser(struct ref_format *format UNUSED,\n \treturn 0;\n }\n \n-static int trailers_atom_parser(struct ref_format *format UNUSED,\n-\t\t\t\tstruct used_atom *atom,\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,\n+\t\t\t\t struct used_atom *atom,\n+\t\t\t\t const char *arg, struct strbuf *err)\n+{\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 \tatom->u.contents.trailer_opts.no_divider = 1;\n@@ -668,6 +702,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@@ -1405,6 +1440,92 @@ 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+\tint signature_checked = 0;\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+\t\tint opt;\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) ||\n+\t\t    (*name && *name != ':'))\n+\t\t\tcontinue;\n+\t\tif (!*name)\n+\t\t\tname = NULL;\n+\t\telse\n+\t\t\tname++;\n+\n+\t\topt = parse_signature_option(name);\n+\t\tif (opt < 0)\n+\t\t\tcontinue;\n+\n+\t\tif (!signature_checked) {\n+\t\t\tcheck_commit_signature(commit, &sigc);\n+\t\t\tsignature_checked = 1;\n+\t\t}\n+\n+\t\tswitch (opt) {\n+\t\tcase S_BARE:\n+\t\t\tv->s = xstrdup(sigc.output ? sigc.output: \"\");\n+\t\t\tbreak;\n+\t\tcase S_SIGNER:\n+\t\t\tv->s = xstrdup(sigc.signer ? sigc.signer : \"\");\n+\t\t\tbreak;\n+\t\tcase 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\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase S_KEY:\n+\t\t\tv->s = xstrdup(sigc.key ? sigc.key : \"\");\n+\t\t\tbreak;\n+\t\tcase S_FINGERPRINT:\n+\t\t\tv->s = xstrdup(sigc.fingerprint ?\n+\t\t\t\t       sigc.fingerprint : \"\");\n+\t\t\tbreak;\n+\t\tcase S_PRI_KEY_FP:\n+\t\t\tv->s = xstrdup(sigc.primary_key_fingerprint ?\n+\t\t\t\t       sigc.primary_key_fingerprint : \"\");\n+\t\t\tbreak;\n+\t\tcase S_TRUST_LEVEL:\n+\t\t\tv->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (signature_checked)\n+\t\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@@ -1598,6 +1719,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 5c00607608..6e6ec852b5 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@@ -1522,4 +1523,194 @@ test_expect_success 'git for-each-ref with non-existing refs' '\n \ttest_must_be_empty actual\n '\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 'setup for signature atom using gpg' '\n+\tgit checkout -b signed &&\n+\n+\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n+\n+\techo \"1\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -S -m \"file: 1\" &&\n+\tgit tag first-signed &&\n+\n+\techo \"2\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -m \"file: 2\" &&\n+\tgit tag second-unsigned &&\n+\n+\tgit config commit.gpgSign 1 &&\n+\techo \"3\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\techo \"4\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -SB7227189 -m \"file: 4\" &&\n+\tgit tag fourth-signed &&\n+\n+\techo \"5\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\techo \"6\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ &&\n+\tgit tag fifth-signed HEAD^ &&\n+\tgit tag sixth-signed &&\n+\n+\techo \"7\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n+\tgit tag seventh-unsigned\n+'\n+\n+test_expect_success GPGSSH 'setup for signature atom using ssh' '\n+\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n+\n+\ttest_config gpg.format ssh &&\n+\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n+\techo \"8\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -S -m \"file: 8\" &&\n+\tgit tag eighth-signed-ssh\n+'\n+\n+test_expect_success GPG2 'bare signature atom' '\n+\tgit verify-commit first-signed 2>out.raw &&\n+\tgrep -Ev \"checking the trustdb|PGP trust model\" out.raw >out &&\n+\thead -3 out >expect &&\n+\ttail -1 out >>expect &&\n+\techo  >>expect &&\n+\tgit for-each-ref refs/tags/first-signed \\\n+\t\t--format=\"%(signature)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\tgit verify-commit first-signed &&\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/tags/first-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+test_expect_success GPGSSH 'show good signature with custom format\n+\t\t\t    with ssh' '\n+\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n+\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n+\tcat >expect.tmpl <<-\\EOF &&\n+\tG\n+\tFINGERPRINT\n+\tprincipal with number 1\n+\tFINGERPRINT\n+\n+\tEOF\n+\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n+\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'signature atom with grade option and bad signature' '\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^file: 3/file: 3 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 \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\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/fourth-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\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/fourth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\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/sixth-signed \\\n+\t\t--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+\t13B6F51ECDDE430D\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n+\t\trefs/tags/sixth-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+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\tgit for-each-ref refs/tags/seventh-unsigned \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.41.0\n\n"}]}