Code

for-each-ref: add split message parts to %(contents:*).
authorMichał Górny <mgorny@gentoo.org>
Wed, 7 Sep 2011 17:46:08 +0000 (13:46 -0400)
committerJunio C Hamano <gitster@pobox.com>
Thu, 8 Sep 2011 20:56:19 +0000 (13:56 -0700)
The %(body) placeholder returns the whole body of a tag or
commit, including the signature. However, callers may want
to get just the body without signature, or just the
signature.

Rather than change the meaning of %(body), which might break
some scripts, this patch introduces a new set of
placeholders which break down the %(contents) placeholder
into its constituent parts.

[jk: initial patch by mg, rebased on top of my refactoring
and with tests by me]

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-for-each-ref.txt
builtin/for-each-ref.c
t/lib-gpg.sh
t/t6300-for-each-ref.sh

index 152e695c8178fee9daa478cdf67eae58a2f1af00..c872b883ba25144457eccb9967bc68003a3332ea 100644 (file)
@@ -101,9 +101,10 @@ Fields that have name-email-date tuple as its value (`author`,
 `committer`, and `tagger`) can be suffixed with `name`, `email`,
 and `date` to extract the named component.
 
-The first line of the message in a commit and tag object is
-`subject`, the remaining lines are `body`.  The whole message
-is `contents`.
+The complete message in a commit and tag object is `contents`.
+Its first line is `contents:subject`, the remaining lines
+are `contents:body` and the optional GPG signature
+is `contents:signature`.
 
 For sorting purposes, fields with numeric values sort in numeric
 order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
index ea2112b3881e3c3b791dad0c0b9996a7648e41bd..d90e5d2b29f9ac72a104d6308c04497991a03c17 100644 (file)
@@ -69,6 +69,9 @@ static struct {
        { "subject" },
        { "body" },
        { "contents" },
+       { "contents:subject" },
+       { "contents:body" },
+       { "contents:signature" },
        { "upstream" },
        { "symref" },
        { "flag" },
@@ -472,7 +475,9 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
 
 static void find_subpos(const char *buf, unsigned long sz,
                        const char **sub, unsigned long *sublen,
-                       const char **body, unsigned long *bodylen)
+                       const char **body, unsigned long *bodylen,
+                       unsigned long *nonsiglen,
+                       const char **sig, unsigned long *siglen)
 {
        const char *eol;
        /* skip past header until we hit empty line */
@@ -486,10 +491,14 @@ static void find_subpos(const char *buf, unsigned long sz,
        while (*buf == '\n')
                buf++;
 
+       /* parse signature first; we might not even have a subject line */
+       *sig = buf + parse_signature(buf, strlen(buf));
+       *siglen = strlen(*sig);
+
        /* subject is first non-empty line */
        *sub = buf;
        /* subject goes to first empty line */
-       while (*buf && *buf != '\n') {
+       while (buf < *sig && *buf && *buf != '\n') {
                eol = strchrnul(buf, '\n');
                if (*eol)
                        eol++;
@@ -505,14 +514,15 @@ static void find_subpos(const char *buf, unsigned long sz,
                buf++;
        *body = buf;
        *bodylen = strlen(buf);
+       *nonsiglen = *sig - buf;
 }
 
 /* See grab_values */
 static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 {
        int i;
-       const char *subpos = NULL, *bodypos;
-       unsigned long sublen, bodylen;
+       const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
+       unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
 
        for (i = 0; i < used_atom_cnt; i++) {
                const char *name = used_atom[i];
@@ -523,17 +533,27 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
                        name++;
                if (strcmp(name, "subject") &&
                    strcmp(name, "body") &&
-                   strcmp(name, "contents"))
+                   strcmp(name, "contents") &&
+                   strcmp(name, "contents:subject") &&
+                   strcmp(name, "contents:body") &&
+                   strcmp(name, "contents:signature"))
                        continue;
                if (!subpos)
                        find_subpos(buf, sz,
                                    &subpos, &sublen,
-                                   &bodypos, &bodylen);
+                                   &bodypos, &bodylen, &nonsiglen,
+                                   &sigpos, &siglen);
 
                if (!strcmp(name, "subject"))
                        v->s = copy_subject(subpos, sublen);
+               else if (!strcmp(name, "contents:subject"))
+                       v->s = copy_subject(subpos, sublen);
                else if (!strcmp(name, "body"))
                        v->s = xmemdupz(bodypos, bodylen);
+               else if (!strcmp(name, "contents:body"))
+                       v->s = xmemdupz(bodypos, nonsiglen);
+               else if (!strcmp(name, "contents:signature"))
+                       v->s = xmemdupz(sigpos, siglen);
                else if (!strcmp(name, "contents"))
                        v->s = xstrdup(subpos);
        }
index 28463fb6f426e6d6a74ab2092510c838a1aa95a8..05824fa8e4d18252f74c51531d6504039986c078 100755 (executable)
@@ -24,3 +24,11 @@ else
                ;;
        esac
 fi
+
+sanitize_pgp() {
+       perl -ne '
+               /^-----END PGP/ and $in_pgp = 0;
+               print unless $in_pgp;
+               /^-----BEGIN PGP/ and $in_pgp = 1;
+       '
+}
index 0c9ff96dbed61807483b44358cb9f97a817c57de..172178490ad126e07b70a6e3f72e0bf723d2c42b 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='for-each-ref test'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
 
 # Mon Jul 3 15:18:43 2006 +0000
 datestamp=1151939923
@@ -40,9 +41,10 @@ test_atom() {
                   *) ref=$1 ;;
        esac
        printf '%s\n' "$3" >expected
-       test_expect_${4:-success} "basic atom: $1 $2" "
+       test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
                git for-each-ref --format='%($2)' $ref >actual &&
-               test_cmp expected actual
+               sanitize_pgp <actual >actual.clean &&
+               test_cmp expected actual.clean
        "
 }
 
@@ -72,7 +74,10 @@ test_atom head taggerdate ''
 test_atom head creator 'C O Mitter <committer@example.com> 1151939923 +0200'
 test_atom head creatordate 'Mon Jul 3 17:18:43 2006 +0200'
 test_atom head subject 'Initial'
+test_atom head contents:subject 'Initial'
 test_atom head body ''
+test_atom head contents:body ''
+test_atom head contents:signature ''
 test_atom head contents 'Initial
 '
 
@@ -102,7 +107,10 @@ test_atom tag taggerdate 'Mon Jul 3 17:18:45 2006 +0200'
 test_atom tag creator 'C O Mitter <committer@example.com> 1151939925 +0200'
 test_atom tag creatordate 'Mon Jul 3 17:18:45 2006 +0200'
 test_atom tag subject 'Tagging at 1151939927'
+test_atom tag contents:subject 'Tagging at 1151939927'
 test_atom tag body ''
+test_atom tag contents:body ''
+test_atom tag contents:signature ''
 test_atom tag contents 'Tagging at 1151939927
 '
 
@@ -390,9 +398,14 @@ test_expect_success 'create tag with multiline subject' '
        git tag -F msg multiline
 '
 test_atom refs/tags/multiline subject 'first subject line second subject line'
+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'
 test_atom refs/tags/multiline body 'first body line
 second body line
 '
+test_atom refs/tags/multiline contents:body 'first body line
+second body line
+'
+test_atom refs/tags/multiline contents:signature ''
 test_atom refs/tags/multiline contents 'first subject line
 second subject line
 
@@ -400,4 +413,47 @@ first body line
 second body line
 '
 
+test_expect_success GPG 'create signed tags' '
+       git tag -s -m "" signed-empty &&
+       git tag -s -m "subject line" signed-short &&
+       cat >msg <<-\EOF &&
+       subject line
+
+       body contents
+       EOF
+       git tag -s -F msg signed-long
+'
+
+sig='-----BEGIN PGP SIGNATURE-----
+-----END PGP SIGNATURE-----
+'
+
+PREREQ=GPG
+test_atom refs/tags/signed-empty subject ''
+test_atom refs/tags/signed-empty contents:subject ''
+test_atom refs/tags/signed-empty body "$sig"
+test_atom refs/tags/signed-empty contents:body ''
+test_atom refs/tags/signed-empty contents:signature "$sig"
+test_atom refs/tags/signed-empty contents "$sig"
+
+test_atom refs/tags/signed-short subject 'subject line'
+test_atom refs/tags/signed-short contents:subject 'subject line'
+test_atom refs/tags/signed-short body "$sig"
+test_atom refs/tags/signed-short contents:body ''
+test_atom refs/tags/signed-short contents:signature "$sig"
+test_atom refs/tags/signed-short contents "subject line
+$sig"
+
+test_atom refs/tags/signed-long subject 'subject line'
+test_atom refs/tags/signed-long contents:subject 'subject line'
+test_atom refs/tags/signed-long body "body contents
+$sig"
+test_atom refs/tags/signed-long contents:body 'body contents
+'
+test_atom refs/tags/signed-long contents:signature "$sig"
+test_atom refs/tags/signed-long contents "subject line
+
+body contents
+$sig"
+
 test_done