interpret-trailers: add an option to show only the trailers

In theory it's easy for any reader who wants to parse trailers to do so. But there are a lot of subtle corner cases around what counts as a trailer, when the trailer block begins and ends, etc. Since interpret-trailers already has our parsing logic, let's let callers ask it to just output the trailers. They still have to parse the "key: value" lines, but at least they can ignore all of the other corner cases. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Aug 15, 2017 at 06:23 UTC 56c493ed1b9c067813fb95ff7cd4f69c7c1d2e36
5 files changed +58 -9
Documentation/git-interpret-trailers.txt
+3
@@ -80,6 +80,9 @@ OPTIONS
80 trailer to the input messages. See the description of this
81 command.
82
83 +--only-trailers::
84 + Output only the trailers, not any other parts of the input.
85 +
86 CONFIGURATION VARIABLES
87 -----------------------
88
builtin/interpret-trailers.c
+1
@@ -24,6 +24,7 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
24 struct option options[] = {
25 OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
26 OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
27 + OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
28 OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
29 N_("trailer(s) to add")),
30 OPT_END()
t/t7513-interpret-trailers.sh
+39
@@ -1275,4 +1275,43 @@ test_expect_success 'with cut line' '
1275 test_cmp expected actual
1276 '
1277
1278 +test_expect_success 'only trailers' '
1279 + git config trailer.sign.command "echo config-value" &&
1280 + cat >expected <<-\EOF &&
1281 + existing: existing-value
1282 + sign: config-value
1283 + added: added-value
1284 + EOF
1285 + git interpret-trailers \
1286 + --trailer added:added-value \
1287 + --only-trailers >actual <<-\EOF &&
1288 + my subject
1289 +
1290 + my body
1291 +
1292 + existing: existing-value
1293 + EOF
1294 + test_cmp expected actual
1295 +'
1296 +
1297 +test_expect_success 'only-trailers omits non-trailer in middle of block' '
1298 + git config trailer.sign.command "echo config-value" &&
1299 + cat >expected <<-\EOF &&
1300 + Signed-off-by: nobody <nobody@nowhere>
1301 + Signed-off-by: somebody <somebody@somewhere>
1302 + sign: config-value
1303 + EOF
1304 + git interpret-trailers --only-trailers >actual <<-\EOF &&
1305 + subject
1306 +
1307 + it is important that the trailers below are signed-off-by
1308 + so that they meet the "25% trailers Git knows about" heuristic
1309 +
1310 + Signed-off-by: nobody <nobody@nowhere>
1311 + this is not a trailer
1312 + Signed-off-by: somebody <somebody@somewhere>
1313 + EOF
1314 + test_cmp expected actual
1315 +'
1316 +
1317 test_done
trailer.c
+14 -9
@@ -163,13 +163,15 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)
163 fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
164 }
165
166 -static void print_all(FILE *outfile, struct list_head *head, int trim_empty)
166 +static void print_all(FILE *outfile, struct list_head *head,
167 + const struct process_trailer_options *opts)
168 {
169 struct list_head *pos;
170 struct trailer_item *item;
171 list_for_each(pos, head) {
172 item = list_entry(pos, struct trailer_item, list);
172 - if (!trim_empty || strlen(item->value) > 0)
173 + if ((!opts->trim_empty || strlen(item->value) > 0) &&
174 + (!opts->only_trailers || item->token))
175 print_tok_val(outfile, item->token, item->value);
176 }
177 }
@@ -886,7 +888,8 @@ static int ends_with_blank_line(const char *buf, size_t len)
888
889 static int process_input_file(FILE *outfile,
890 const char *str,
889 - struct list_head *head)
891 + struct list_head *head,
892 + const struct process_trailer_options *opts)
893 {
894 struct trailer_info info;
895 struct strbuf tok = STRBUF_INIT;
@@ -896,9 +899,10 @@ static int process_input_file(FILE *outfile,
899 trailer_info_get(&info, str);
900
901 /* Print lines before the trailers as is */
899 - fwrite(str, 1, info.trailer_start - str, outfile);
902 + if (!opts->only_trailers)
903 + fwrite(str, 1, info.trailer_start - str, outfile);
904
901 - if (!info.blank_line_before_trailer)
905 + if (!opts->only_trailers && !info.blank_line_before_trailer)
906 fprintf(outfile, "\n");
907
908 for (i = 0; i < info.trailer_nr; i++) {
@@ -913,7 +917,7 @@ static int process_input_file(FILE *outfile,
917 add_trailer_item(head,
918 strbuf_detach(&tok, NULL),
919 strbuf_detach(&val, NULL));
916 - } else {
920 + } else if (!opts->only_trailers) {
921 strbuf_addstr(&val, trailer);
922 strbuf_strip_suffix(&val, "\n");
923 add_trailer_item(head,
@@ -985,18 +989,19 @@ void process_trailers(const char *file,
989 outfile = create_in_place_tempfile(file);
990
991 /* Print the lines before the trailers */
988 - trailer_end = process_input_file(outfile, sb.buf, &head);
992 + trailer_end = process_input_file(outfile, sb.buf, &head, opts);
993
994 process_command_line_args(&arg_head, trailers);
995
996 process_trailers_lists(&head, &arg_head);
997
994 - print_all(outfile, &head, opts->trim_empty);
998 + print_all(outfile, &head, opts);
999
1000 free_all(&head);
1001
1002 /* Print the lines after the trailers as is */
999 - fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
1003 + if (!opts->only_trailers)
1004 + fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
1005
1006 if (opts->in_place)
1007 if (rename_tempfile(&trailers_tempfile, file))
trailer.h
+1
@@ -25,6 +25,7 @@ struct trailer_info {
25 struct process_trailer_options {
26 int in_place;
27 int trim_empty;
28 + int only_trailers;
29 };
30
31 #define PROCESS_TRAILER_OPTIONS_INIT {0}