pretty: allow showing specific trailers
Adds a new "key=X" option to "%(trailers)" which will cause it to only print trailer lines which match any of the specified keys. Signed-off-by: Anders Waldenborg <anders@0x63.nu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Anders Waldenborg committed
Jan 28, 2019 at 22:33 UTC
250bea0c1652ad546cd0455852bd734e4820ec46
5 files changed
+107
-6
Documentation/pretty-formats.txt
+8
@@ -225,6 +225,14 @@ endif::git-rev-list[]
225
linkgit:git-interpret-trailers[1]. The
226
`trailers` string may be followed by a colon
227
and zero or more comma-separated options:
228
+** 'key=<K>': only show trailers with specified key. Matching is done
229
+ case-insensitively and trailing colon is optional. If option is
230
+ given multiple times trailer lines matching any of the keys are
231
+ shown. This option automatically enables the `only` option so that
232
+ non-trailer lines in the trailer block are hidden. If that is not
233
+ desired it can be disabled with `only=false`. E.g.,
234
+ `%(trailers:key=Reviewed-by)` shows trailer lines with key
235
+ `Reviewed-by`.
236
** 'only[=val]': select whether non-trailer lines from the trailer
237
block should be included. The `only` keyword may optionally be
238
followed by an equal sign and one of `true`, `on`, `yes` to omit or
pretty.c
+34
-2
@@ -1115,6 +1115,19 @@ static int match_placeholder_bool_arg(const char *to_parse, const char *candidat
1115
return 1;
1116
}
1117
1118
+static int format_trailer_match_cb(const struct strbuf *key, void *ud)
1119
+{
1120
+ const struct string_list *list = ud;
1121
+ const struct string_list_item *item;
1122
+
1123
+ for_each_string_list_item (item, list) {
1124
+ if (key->len == (uintptr_t)item->util &&
1125
+ !strncasecmp(item->string, key->buf, key->len))
1126
+ return 1;
1127
+ }
1128
+ return 0;
1129
+}
1130
+
1131
static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1132
const char *placeholder,
1133
void *context)
@@ -1353,6 +1366,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1366
1367
if (skip_prefix(placeholder, "(trailers", &arg)) {
1368
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
1369
+ struct string_list filter_list = STRING_LIST_INIT_NODUP;
1370
size_t ret = 0;
1371
1372
opts.no_divider = 1;
@@ -1360,8 +1374,24 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1374
if (*arg == ':') {
1375
arg++;
1376
for (;;) {
1363
- if (!match_placeholder_bool_arg(arg, "only", &arg, &opts.only_trailers) &&
1364
- !match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold))
1377
+ const char *argval;
1378
+ size_t arglen;
1379
+
1380
+ if (match_placeholder_arg_value(arg, "key", &arg, &argval, &arglen)) {
1381
+ uintptr_t len = arglen;
1382
+
1383
+ if (!argval)
1384
+ goto trailer_out;
1385
+
1386
+ if (len && argval[len - 1] == ':')
1387
+ len--;
1388
+ string_list_append(&filter_list, argval)->util = (char *)len;
1389
+
1390
+ opts.filter = format_trailer_match_cb;
1391
+ opts.filter_data = &filter_list;
1392
+ opts.only_trailers = 1;
1393
+ } else if (!match_placeholder_bool_arg(arg, "only", &arg, &opts.only_trailers) &&
1394
+ !match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold))
1395
break;
1396
}
1397
}
@@ -1369,6 +1399,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1399
format_trailers_from_commit(sb, msg + c->subject_off, &opts);
1400
ret = arg - placeholder + 1;
1401
}
1402
+ trailer_out:
1403
+ string_list_clear(&filter_list, 0);
1404
return ret;
1405
}
1406
t/t4205-log-pretty-formats.sh
+57
@@ -616,6 +616,63 @@ test_expect_success ':only and :unfold work together' '
616
test_cmp expect actual
617
'
618
619
+test_expect_success 'pretty format %(trailers:key=foo) shows that trailer' '
620
+ git log --no-walk --pretty="format:%(trailers:key=Acked-by)" >actual &&
621
+ echo "Acked-by: A U Thor <author@example.com>" >expect &&
622
+ test_cmp expect actual
623
+'
624
+
625
+test_expect_success 'pretty format %(trailers:key=foo) is case insensitive' '
626
+ git log --no-walk --pretty="format:%(trailers:key=AcKed-bY)" >actual &&
627
+ echo "Acked-by: A U Thor <author@example.com>" >expect &&
628
+ test_cmp expect actual
629
+'
630
+
631
+test_expect_success 'pretty format %(trailers:key=foo:) trailing colon also works' '
632
+ git log --no-walk --pretty="format:%(trailers:key=Acked-by:)" >actual &&
633
+ echo "Acked-by: A U Thor <author@example.com>" >expect &&
634
+ test_cmp expect actual
635
+'
636
+
637
+test_expect_success 'pretty format %(trailers:key=foo) multiple keys' '
638
+ git log --no-walk --pretty="format:%(trailers:key=Acked-by:,key=Signed-off-By)" >actual &&
639
+ grep -v patch.description <trailers >expect &&
640
+ test_cmp expect actual
641
+'
642
+
643
+test_expect_success '%(trailers:key=nonexistant) becomes empty' '
644
+ git log --no-walk --pretty="x%(trailers:key=Nacked-by)x" >actual &&
645
+ echo "xx" >expect &&
646
+ test_cmp expect actual
647
+'
648
+
649
+test_expect_success '%(trailers:key=foo) handles multiple lines even if folded' '
650
+ git log --no-walk --pretty="format:%(trailers:key=Signed-Off-by)" >actual &&
651
+ grep -v patch.description <trailers | grep -v Acked-by >expect &&
652
+ test_cmp expect actual
653
+'
654
+
655
+test_expect_success '%(trailers:key=foo,unfold) properly unfolds' '
656
+ git log --no-walk --pretty="format:%(trailers:key=Signed-Off-by,unfold)" >actual &&
657
+ unfold <trailers | grep Signed-off-by >expect &&
658
+ test_cmp expect actual
659
+'
660
+
661
+test_expect_success 'pretty format %(trailers:key=foo,only=no) also includes nontrailer lines' '
662
+ git log --no-walk --pretty="format:%(trailers:key=Acked-by,only=no)" >actual &&
663
+ {
664
+ echo "Acked-by: A U Thor <author@example.com>" &&
665
+ grep patch.description <trailers
666
+ } >expect &&
667
+ test_cmp expect actual
668
+'
669
+
670
+test_expect_success '%(trailers:key) without value is error' '
671
+ git log --no-walk --pretty="tformat:%(trailers:key)" >actual &&
672
+ echo "%(trailers:key)" >expect &&
673
+ test_cmp expect actual
674
+'
675
+
676
test_expect_success 'trailer parsing not fooled by --- line' '
677
git commit --allow-empty -F - <<-\EOF &&
678
this is the subject
trailer.c
+6
-4
@@ -1132,7 +1132,7 @@ static void format_trailer_info(struct strbuf *out,
1132
size_t i;
1133
1134
/* If we want the whole block untouched, we can take the fast path. */
1135
- if (!opts->only_trailers && !opts->unfold) {
1135
+ if (!opts->only_trailers && !opts->unfold && !opts->filter) {
1136
strbuf_add(out, info->trailer_start,
1137
info->trailer_end - info->trailer_start);
1138
return;
@@ -1147,10 +1147,12 @@ static void format_trailer_info(struct strbuf *out,
1147
struct strbuf val = STRBUF_INIT;
1148
1149
parse_trailer(&tok, &val, NULL, trailer, separator_pos);
1150
- if (opts->unfold)
1151
- unfold_value(&val);
1150
+ if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
1151
+ if (opts->unfold)
1152
+ unfold_value(&val);
1153
1153
- strbuf_addf(out, "%s: %s\n", tok.buf, val.buf);
1154
+ strbuf_addf(out, "%s: %s\n", tok.buf, val.buf);
1155
+ }
1156
strbuf_release(&tok);
1157
strbuf_release(&val);
1158
trailer.h
+2
@@ -72,6 +72,8 @@ struct process_trailer_options {
72
int only_input;
73
int unfold;
74
int no_divider;
75
+ int (*filter)(const struct strbuf *, void *);
76
+ void *filter_data;
77
};
78
79
#define PROCESS_TRAILER_OPTIONS_INIT {0}