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