for-each-ref: add split message parts to %(contents:*).

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>

Michał Górny committed Sep 7, 2011 at 13:46 UTC e2b239722a5806521016835f86e6c29c630a0e60
4 files changed +96 -11
Documentation/git-for-each-ref.txt
+4 -3
@@ -101,9 +101,10 @@ Fields that have name-email-date tuple as its value (`author`,
101 `committer`, and `tagger`) can be suffixed with `name`, `email`,
102 and `date` to extract the named component.
103
104 -The first line of the message in a commit and tag object is
105 -`subject`, the remaining lines are `body`. The whole message
106 -is `contents`.
104 +The complete message in a commit and tag object is `contents`.
105 +Its first line is `contents:subject`, the remaining lines
106 +are `contents:body` and the optional GPG signature
107 +is `contents:signature`.
108
109 For sorting purposes, fields with numeric values sort in numeric
110 order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
builtin/for-each-ref.c
+26 -6
@@ -69,6 +69,9 @@ static struct {
69 { "subject" },
70 { "body" },
71 { "contents" },
72 + { "contents:subject" },
73 + { "contents:body" },
74 + { "contents:signature" },
75 { "upstream" },
76 { "symref" },
77 { "flag" },
@@ -472,7 +475,9 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
475
476 static void find_subpos(const char *buf, unsigned long sz,
477 const char **sub, unsigned long *sublen,
475 - const char **body, unsigned long *bodylen)
478 + const char **body, unsigned long *bodylen,
479 + unsigned long *nonsiglen,
480 + const char **sig, unsigned long *siglen)
481 {
482 const char *eol;
483 /* skip past header until we hit empty line */
@@ -486,10 +491,14 @@ static void find_subpos(const char *buf, unsigned long sz,
491 while (*buf == '\n')
492 buf++;
493
494 + /* parse signature first; we might not even have a subject line */
495 + *sig = buf + parse_signature(buf, strlen(buf));
496 + *siglen = strlen(*sig);
497 +
498 /* subject is first non-empty line */
499 *sub = buf;
500 /* subject goes to first empty line */
492 - while (*buf && *buf != '\n') {
501 + while (buf < *sig && *buf && *buf != '\n') {
502 eol = strchrnul(buf, '\n');
503 if (*eol)
504 eol++;
@@ -505,14 +514,15 @@ static void find_subpos(const char *buf, unsigned long sz,
514 buf++;
515 *body = buf;
516 *bodylen = strlen(buf);
517 + *nonsiglen = *sig - buf;
518 }
519
520 /* See grab_values */
521 static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
522 {
523 int i;
514 - const char *subpos = NULL, *bodypos;
515 - unsigned long sublen, bodylen;
524 + const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
525 + unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
526
527 for (i = 0; i < used_atom_cnt; i++) {
528 const char *name = used_atom[i];
@@ -523,17 +533,27 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
533 name++;
534 if (strcmp(name, "subject") &&
535 strcmp(name, "body") &&
526 - strcmp(name, "contents"))
536 + strcmp(name, "contents") &&
537 + strcmp(name, "contents:subject") &&
538 + strcmp(name, "contents:body") &&
539 + strcmp(name, "contents:signature"))
540 continue;
541 if (!subpos)
542 find_subpos(buf, sz,
543 &subpos, &sublen,
531 - &bodypos, &bodylen);
544 + &bodypos, &bodylen, &nonsiglen,
545 + &sigpos, &siglen);
546
547 if (!strcmp(name, "subject"))
548 v->s = copy_subject(subpos, sublen);
549 + else if (!strcmp(name, "contents:subject"))
550 + v->s = copy_subject(subpos, sublen);
551 else if (!strcmp(name, "body"))
552 v->s = xmemdupz(bodypos, bodylen);
553 + else if (!strcmp(name, "contents:body"))
554 + v->s = xmemdupz(bodypos, nonsiglen);
555 + else if (!strcmp(name, "contents:signature"))
556 + v->s = xmemdupz(sigpos, siglen);
557 else if (!strcmp(name, "contents"))
558 v->s = xstrdup(subpos);
559 }
t/lib-gpg.sh
+8
@@ -24,3 +24,11 @@ else
24 ;;
25 esac
26 fi
27 +
28 +sanitize_pgp() {
29 + perl -ne '
30 + /^-----END PGP/ and $in_pgp = 0;
31 + print unless $in_pgp;
32 + /^-----BEGIN PGP/ and $in_pgp = 1;
33 + '
34 +}
t/t6300-for-each-ref.sh
+58 -2
@@ -6,6 +6,7 @@
6 test_description='for-each-ref test'
7
8 . ./test-lib.sh
9 +. "$TEST_DIRECTORY"/lib-gpg.sh
10
11 # Mon Jul 3 15:18:43 2006 +0000
12 datestamp=1151939923
@@ -40,9 +41,10 @@ test_atom() {
41 *) ref=$1 ;;
42 esac
43 printf '%s\n' "$3" >expected
43 - test_expect_${4:-success} "basic atom: $1 $2" "
44 + test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
45 git for-each-ref --format='%($2)' $ref >actual &&
45 - test_cmp expected actual
46 + sanitize_pgp <actual >actual.clean &&
47 + test_cmp expected actual.clean
48 "
49 }
50
@@ -72,7 +74,10 @@ test_atom head taggerdate ''
74 test_atom head creator 'C O Mitter <committer@example.com> 1151939923 +0200'
75 test_atom head creatordate 'Mon Jul 3 17:18:43 2006 +0200'
76 test_atom head subject 'Initial'
77 +test_atom head contents:subject 'Initial'
78 test_atom head body ''
79 +test_atom head contents:body ''
80 +test_atom head contents:signature ''
81 test_atom head contents 'Initial
82 '
83
@@ -102,7 +107,10 @@ test_atom tag taggerdate 'Mon Jul 3 17:18:45 2006 +0200'
107 test_atom tag creator 'C O Mitter <committer@example.com> 1151939925 +0200'
108 test_atom tag creatordate 'Mon Jul 3 17:18:45 2006 +0200'
109 test_atom tag subject 'Tagging at 1151939927'
110 +test_atom tag contents:subject 'Tagging at 1151939927'
111 test_atom tag body ''
112 +test_atom tag contents:body ''
113 +test_atom tag contents:signature ''
114 test_atom tag contents 'Tagging at 1151939927
115 '
116
@@ -390,9 +398,14 @@ test_expect_success 'create tag with multiline subject' '
398 git tag -F msg multiline
399 '
400 test_atom refs/tags/multiline subject 'first subject line second subject line'
401 +test_atom refs/tags/multiline contents:subject 'first subject line second subject line'
402 test_atom refs/tags/multiline body 'first body line
403 second body line
404 '
405 +test_atom refs/tags/multiline contents:body 'first body line
406 +second body line
407 +'
408 +test_atom refs/tags/multiline contents:signature ''
409 test_atom refs/tags/multiline contents 'first subject line
410 second subject line
411
@@ -400,4 +413,47 @@ first body line
413 second body line
414 '
415
416 +test_expect_success GPG 'create signed tags' '
417 + git tag -s -m "" signed-empty &&
418 + git tag -s -m "subject line" signed-short &&
419 + cat >msg <<-\EOF &&
420 + subject line
421 +
422 + body contents
423 + EOF
424 + git tag -s -F msg signed-long
425 +'
426 +
427 +sig='-----BEGIN PGP SIGNATURE-----
428 +-----END PGP SIGNATURE-----
429 +'
430 +
431 +PREREQ=GPG
432 +test_atom refs/tags/signed-empty subject ''
433 +test_atom refs/tags/signed-empty contents:subject ''
434 +test_atom refs/tags/signed-empty body "$sig"
435 +test_atom refs/tags/signed-empty contents:body ''
436 +test_atom refs/tags/signed-empty contents:signature "$sig"
437 +test_atom refs/tags/signed-empty contents "$sig"
438 +
439 +test_atom refs/tags/signed-short subject 'subject line'
440 +test_atom refs/tags/signed-short contents:subject 'subject line'
441 +test_atom refs/tags/signed-short body "$sig"
442 +test_atom refs/tags/signed-short contents:body ''
443 +test_atom refs/tags/signed-short contents:signature "$sig"
444 +test_atom refs/tags/signed-short contents "subject line
445 +$sig"
446 +
447 +test_atom refs/tags/signed-long subject 'subject line'
448 +test_atom refs/tags/signed-long contents:subject 'subject line'
449 +test_atom refs/tags/signed-long body "body contents
450 +$sig"
451 +test_atom refs/tags/signed-long contents:body 'body contents
452 +'
453 +test_atom refs/tags/signed-long contents:signature "$sig"
454 +test_atom refs/tags/signed-long contents "subject line
455 +
456 +body contents
457 +$sig"
458 +
459 test_done