> >> I am not sure I have understood this, which helper? > > > > I think Junio is talking about the following function: > > > > static enum signature_option parse_signature_option(const char *arg) > > > > he suggested above. > > Correct. > > > With this function the above code could be just something like: > > > > if (parse_signature_option(name) < 0) > > continue; > > More or less so, but the first "if" in the helper I wrote in the > message above is broken. It should be > > static enum signature_option parse_signature_option(const char *arg) > { > if (!*arg) > return S_BARE; > else if (!strcmp(arg, "signer")) > return S_SIGNER; > ... > else > return -1; > } This way, running git for-each-ref refs/heads/signature8 --format="%(signature)" raises a seg fault, I looked for the bug using gdb when I check the contents of *arg like this:p *arg, I get this: Cannot access memory at 0x0. However, others like signature:key, signature:signer, etc are ok. Leaving it as arg makes everything fine. So I decided to leave it as you had suggested first. I had actually forgotten to add the test for "%(signature)", so this scenario reminded me to do so. On Mon, Jan 2, 2023 at 7:47 AM NSENGIYUMVA WILBERFORCE <nsengiyumvawilberforce@xxxxxxxxx> wrote: > > Hi >> >> > From: Nsengiyumva Wilberforce <nsengiyumvawilberforce@xxxxxxxxx> >> > >> > This only works for commits. Add "signature" atom with `grade`, >> > `signer`, `key`, `fingerprint`, `primarykeyfingerprint`, `trustlevel` >> > as arguments. This code and it's documentation are inspired by >> > how the %GG, %G?, %GS, %GK, %GF, %GP, and %GT pretty formats were >> > implemented. >> >> Lacking motivation. Without explaining why somebody may want to >> have the feature and what it would be used for, "only works for >> commits" would invite a "so what? does it even have to work?" as a >> response, so start with a brief descrioption "with the current set >> of atoms, $this_useful_thing cannot easily be achieved" before >> describing its limitation. > > Ok, I will edit the commit message. Thanks >> >> > diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt >> > index 6da899c6296..9a0be85368b 100644 >> > --- a/Documentation/git-for-each-ref.txt >> > +++ b/Documentation/git-for-each-ref.txt >> > @@ -212,6 +212,33 @@ symref:: >> > `:lstrip` and `:rstrip` options in the same way as `refname` >> > above. >> > >> > +signature:: >> > +... >> > +signature:trustlevel:: >> > + The Trust level of the GPG signature of a commit. Possible >> > + outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`. >> >> A good list. How do these work for signature made with a tool other >> than GPG (in other words, when "gpg.format" is set to something >> other than "openpgp")? > > You mean ssh and X509, right? honestly, I did not check the behavior. I am going to check > >> Having said that, wouldn't it be natural to expect that the same >> code can deal with signed tags? After all we use the same signature >> verification machinery at the lowest level in the callchain. > > Very right, it works for signed tags too. > >> >> Handing the !arg case first will make the if/else if/... cascade >> easier to follow, no? Also the body of the function may want to >> become a separate function that returns one of these S_FOO constants. >> >> static enum signatore_option signature_atom_parser(...) >> { >> enum signature_option opt = parse_signature_option(arg); >> if (opt < 0) >> return strbuf_addf_ret(err, opt, _("unknown ..."), arg); >> return opt; >> } >> >> where parse_signature_option() would look like >> >> static enum signature_option parse_signature_option(const char *arg) >> { >> if (!arg) >> return S_BARE; >> else if (!strcmp(arg, "signer")) >> return S_SIGNER; >> ... >> else >> return -1; >> } >> >> or something like that? > > It makes more sense >> >> > +{ >> > + int i; >> > + struct commit *commit = (struct commit *) obj; >> >> Style? No SP between cast and value? > > ok, noted >> >> > + >> > + for (i = 0; i < used_atom_cnt; i++) { >> > + struct used_atom *atom = &used_atom[i]; >> > + const char *name = atom->name; >> > + struct atom_value *v = &val[i]; >> > + struct signature_check sigc = { 0 }; >> > + >> > + if (!!deref != (*name == '*')) >> > + continue; >> > + if (deref) >> > + name++; >> > + if (strcmp(name, "signature") && >> > + strcmp(name, "signature:signer") && >> > + strcmp(name, "signature:grade") && >> > + strcmp(name, "signature:key") && >> > + strcmp(name, "signature:fingerprint") && >> > + strcmp(name, "signature:primarykeyfingerprint") && >> > + strcmp(name, "signature:trustlevel")) >> > + continue; >> >> And with the helper above, we can avoid the repetition here that can >> go out of sync with the parser function. > > I am not sure I have understood this, which helper? > >> > + check_commit_signature(commit, &sigc); >> >> If a format asks for signature:signer and signature:key, we >> shouldn't be running GPG twice. First check used_atom[] to see if >> we even need to do _any_ signature processing (and leave if there is >> not), populate the sigc just once and then enter the loop, perhaps? > > Yeah, I think it was not right calling check_commit_signature() in the loop. Populating sigc at once looks more good to me > > >> >> In adddition, a call to check_commit_signature() should have a >> >> matching call to signature_check_clear(); otherwise all the >> >> resources held by sigc would leak, wouldn't it? > > Yeah, it would. > > On Mon, Dec 26, 2022 at 9:20 PM Junio C Hamano <gitster@xxxxxxxxx> wrote: >> >> "nsengaw4c via GitGitGadget" <gitgitgadget@xxxxxxxxx> writes: >> >> > From: Nsengiyumva Wilberforce <nsengiyumvawilberforce@xxxxxxxxx> >> > >> > This only works for commits. Add "signature" atom with `grade`, >> > `signer`, `key`, `fingerprint`, `primarykeyfingerprint`, `trustlevel` >> > as arguments. This code and it's documentation are inspired by >> > how the %GG, %G?, %GS, %GK, %GF, %GP, and %GT pretty formats were >> > implemented. >> >> Lacking motivation. Without explaining why somebody may want to >> have the feature and what it would be used for, "only works for >> commits" would invite a "so what? does it even have to work?" as a >> response, so start with a brief descrioption "with the current set >> of atoms, $this_useful_thing cannot easily be achieved" before >> describing its limitation. >> >> Having said that, wouldn't it be natural to expect that the same >> code can deal with signed tags? After all we use the same signature >> verification machinery at the lowest level in the callchain. >> >> > diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt >> > index 6da899c6296..9a0be85368b 100644 >> > --- a/Documentation/git-for-each-ref.txt >> > +++ b/Documentation/git-for-each-ref.txt >> > @@ -212,6 +212,33 @@ symref:: >> > `:lstrip` and `:rstrip` options in the same way as `refname` >> > above. >> > >> > +signature:: >> > +... >> > +signature:trustlevel:: >> > + The Trust level of the GPG signature of a commit. Possible >> > + outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`. >> >> A good list. How do these work for signature made with a tool other >> than GPG (in other words, when "gpg.format" is set to something >> other than "openpgp")? >> >> > @@ -378,6 +383,30 @@ static int subject_atom_parser(struct ref_format *format, struct used_atom *atom >> > return 0; >> > } >> > >> > +static int signature_atom_parser(struct ref_format *format, struct used_atom *atom, >> > + const char *arg, struct strbuf *err) >> > +{ >> > + if (arg) { >> > + if (!strcmp(arg, "signer")) >> > + atom->u.signature.option = S_SIGNER; >> > + else if (!strcmp(arg, "grade")) >> > + atom->u.signature.option = S_GRADE; >> > + else if (!strcmp(arg, "key")) >> > + atom->u.signature.option = S_KEY; >> > + else if (!strcmp(arg, "fingerprint")) >> > + atom->u.signature.option = S_FINGERPRINT; >> > + else if (!strcmp(arg, "primarykeyfingerprint")) >> > + atom->u.signature.option = S_PRI_KEY_FP; >> > + else if (!strcmp(arg, "trustlevel")) >> > + atom->u.signature.option = S_TRUST_LEVEL; >> > + else >> > + return strbuf_addf_ret(err, -1, _("unknown %%(signature) argument: %s"), arg); >> > + } >> > + else >> > + atom->u.signature.option = S_BARE; >> > + return 0; >> > +} >> >> Handing the !arg case first will make the if/else if/... cascade >> easier to follow, no? Also the body of the function may want to >> become a separate function that returns one of these S_FOO constants. >> >> static enum signatore_option signature_atom_parser(...) >> { >> enum signature_option opt = parse_signature_option(arg); >> if (opt < 0) >> return strbuf_addf_ret(err, opt, _("unknown ..."), arg); >> return opt; >> } >> >> where parse_signature_option() would look like >> >> static enum signature_option parse_signature_option(const char *arg) >> { >> if (!arg) >> return S_BARE; >> else if (!strcmp(arg, "signer")) >> return S_SIGNER; >> ... >> else >> return -1; >> } >> >> or something like that? >> >> > @@ -1344,6 +1374,69 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void >> > } >> > } >> > >> > +static void grab_signature(struct atom_value *val, int deref, struct object *obj) >> >> To be considerate for future developers, perhaps rename this to >> grab_commit_signature(), so that they can add grab_tag_signature() >> when they lift the limitation of this implementaiton? >> >> > +{ >> > + int i; >> > + struct commit *commit = (struct commit *) obj; >> >> Style? No SP between cast and value? >> >> > + >> > + for (i = 0; i < used_atom_cnt; i++) { >> > + struct used_atom *atom = &used_atom[i]; >> > + const char *name = atom->name; >> > + struct atom_value *v = &val[i]; >> > + struct signature_check sigc = { 0 }; >> > + >> > + if (!!deref != (*name == '*')) >> > + continue; >> > + if (deref) >> > + name++; >> > + if (strcmp(name, "signature") && >> > + strcmp(name, "signature:signer") && >> > + strcmp(name, "signature:grade") && >> > + strcmp(name, "signature:key") && >> > + strcmp(name, "signature:fingerprint") && >> > + strcmp(name, "signature:primarykeyfingerprint") && >> > + strcmp(name, "signature:trustlevel")) >> > + continue; >> >> And with the helper above, we can avoid the repetition here that can >> go out of sync with the parser function. >> >> > + check_commit_signature(commit, &sigc); >> >> If a format asks for signature:signer and signature:key, we >> shouldn't be running GPG twice. First check used_atom[] to see if >> we even need to do _any_ signature processing (and leave if there is >> not), populate the sigc just once and then enter the loop, perhaps? >> >> In adddition, a call to check_commit_signature() should have a >> matching call to signature_check_clear(); otherwise all the >> resources held by sigc would leak, wouldn't it?