Skip to content

Commit 60ef86a

Browse files
jonathantanmygitster
authored andcommitted
trailer: support values folded to multiple lines
Currently, interpret-trailers requires that a trailer be only on 1 line. For example: a: first line second line would be interpreted as one trailer line followed by one non-trailer line. Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer. Signed-off-by: Jonathan Tan <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c463a6b commit 60ef86a

File tree

3 files changed

+211
-10
lines changed

3 files changed

+211
-10
lines changed

Documentation/git-interpret-trailers.txt

+4-3
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,12 @@ minus signs start the patch part of the message.
5757

5858
When reading trailers, there can be whitespaces after the
5959
token, the separator and the value. There can also be whitespaces
60-
inside the token and the value.
60+
inside the token and the value. The value may be split over multiple lines with
61+
each subsequent line starting with whitespace, like the "folding" in RFC 822.
6162

6263
Note that 'trailers' do not follow and are not intended to follow many
63-
rules for RFC 822 headers. For example they do not follow the line
64-
folding rules, the encoding rules and probably many other rules.
64+
rules for RFC 822 headers. For example they do not follow
65+
the encoding rules and probably many other rules.
6566

6667
OPTIONS
6768
-------

t/t7513-interpret-trailers.sh

+169
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,175 @@ test_expect_success 'line with leading whitespace is not trailer' '
256256
test_cmp expected actual
257257
'
258258

259+
test_expect_success 'multiline field treated as one trailer for 25% check' '
260+
q_to_tab >patch <<-\EOF &&
261+
262+
Signed-off-by: a <[email protected]>
263+
name: value on
264+
Qmultiple lines
265+
this is not a trailer
266+
this is not a trailer
267+
this is not a trailer
268+
this is not a trailer
269+
this is not a trailer
270+
this is not a trailer
271+
EOF
272+
q_to_tab >expected <<-\EOF &&
273+
274+
Signed-off-by: a <[email protected]>
275+
name: value on
276+
Qmultiple lines
277+
this is not a trailer
278+
this is not a trailer
279+
this is not a trailer
280+
this is not a trailer
281+
this is not a trailer
282+
this is not a trailer
283+
name: value
284+
EOF
285+
git interpret-trailers --trailer "name: value" patch >actual &&
286+
test_cmp expected actual
287+
'
288+
289+
test_expect_success 'multiline field treated as atomic for placement' '
290+
q_to_tab >patch <<-\EOF &&
291+
292+
another: trailer
293+
name: value on
294+
Qmultiple lines
295+
another: trailer
296+
EOF
297+
q_to_tab >expected <<-\EOF &&
298+
299+
another: trailer
300+
name: value on
301+
Qmultiple lines
302+
name: value
303+
another: trailer
304+
EOF
305+
test_config trailer.name.where after &&
306+
git interpret-trailers --trailer "name: value" patch >actual &&
307+
test_cmp expected actual
308+
'
309+
310+
test_expect_success 'multiline field treated as atomic for replacement' '
311+
q_to_tab >patch <<-\EOF &&
312+
313+
another: trailer
314+
name: value on
315+
Qmultiple lines
316+
another: trailer
317+
EOF
318+
q_to_tab >expected <<-\EOF &&
319+
320+
another: trailer
321+
another: trailer
322+
name: value
323+
EOF
324+
test_config trailer.name.ifexists replace &&
325+
git interpret-trailers --trailer "name: value" patch >actual &&
326+
test_cmp expected actual
327+
'
328+
329+
test_expect_success 'multiline field treated as atomic for difference check' '
330+
q_to_tab >patch <<-\EOF &&
331+
332+
another: trailer
333+
name: first line
334+
Qsecond line
335+
another: trailer
336+
EOF
337+
test_config trailer.name.ifexists addIfDifferent &&
338+
339+
q_to_tab >trailer <<-\EOF &&
340+
name: first line
341+
Qsecond line
342+
EOF
343+
q_to_tab >expected <<-\EOF &&
344+
345+
another: trailer
346+
name: first line
347+
Qsecond line
348+
another: trailer
349+
EOF
350+
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
351+
test_cmp expected actual &&
352+
353+
q_to_tab >trailer <<-\EOF &&
354+
name: first line
355+
QQQQQsecond line
356+
EOF
357+
q_to_tab >expected <<-\EOF &&
358+
359+
another: trailer
360+
name: first line
361+
Qsecond line
362+
another: trailer
363+
name: first line
364+
QQQQQsecond line
365+
EOF
366+
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
367+
test_cmp expected actual &&
368+
369+
q_to_tab >trailer <<-\EOF &&
370+
name: first line *DIFFERENT*
371+
Qsecond line
372+
EOF
373+
q_to_tab >expected <<-\EOF &&
374+
375+
another: trailer
376+
name: first line
377+
Qsecond line
378+
another: trailer
379+
name: first line *DIFFERENT*
380+
Qsecond line
381+
EOF
382+
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
383+
test_cmp expected actual
384+
'
385+
386+
test_expect_success 'multiline field treated as atomic for neighbor check' '
387+
q_to_tab >patch <<-\EOF &&
388+
389+
another: trailer
390+
name: first line
391+
Qsecond line
392+
another: trailer
393+
EOF
394+
test_config trailer.name.where after &&
395+
test_config trailer.name.ifexists addIfDifferentNeighbor &&
396+
397+
q_to_tab >trailer <<-\EOF &&
398+
name: first line
399+
Qsecond line
400+
EOF
401+
q_to_tab >expected <<-\EOF &&
402+
403+
another: trailer
404+
name: first line
405+
Qsecond line
406+
another: trailer
407+
EOF
408+
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
409+
test_cmp expected actual &&
410+
411+
q_to_tab >trailer <<-\EOF &&
412+
name: first line
413+
QQQQQsecond line
414+
EOF
415+
q_to_tab >expected <<-\EOF &&
416+
417+
another: trailer
418+
name: first line
419+
Qsecond line
420+
name: first line
421+
QQQQQsecond line
422+
another: trailer
423+
EOF
424+
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
425+
test_cmp expected actual
426+
'
427+
259428
test_expect_success 'with config setup' '
260429
git config trailer.ack.key "Acked-by: " &&
261430
cat >expected <<-\EOF &&

trailer.c

+38-7
Original file line numberDiff line numberDiff line change
@@ -619,12 +619,14 @@ static void parse_trailer(struct strbuf *tok, struct strbuf *val,
619619
}
620620
}
621621

622-
static void add_trailer_item(struct list_head *head, char *tok, char *val)
622+
static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
623+
char *val)
623624
{
624625
struct trailer_item *new = xcalloc(sizeof(*new), 1);
625626
new->token = tok;
626627
new->value = val;
627628
list_add_tail(&new->list, head);
629+
return new;
628630
}
629631

630632
static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
@@ -732,6 +734,14 @@ static int find_trailer_start(struct strbuf **lines, int count)
732734
{
733735
int start, end_of_title, only_spaces = 1;
734736
int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
737+
/*
738+
* Number of possible continuation lines encountered. This will be
739+
* reset to 0 if we encounter a trailer (since those lines are to be
740+
* considered continuations of that trailer), and added to
741+
* non_trailer_lines if we encounter a non-trailer (since those lines
742+
* are to be considered non-trailers).
743+
*/
744+
int possible_continuation_lines = 0;
735745

736746
/* The first paragraph is the title and cannot be trailers */
737747
for (start = 0; start < count; start++) {
@@ -752,11 +762,15 @@ static int find_trailer_start(struct strbuf **lines, int count)
752762
const char **p;
753763
int separator_pos;
754764

755-
if (lines[start]->buf[0] == comment_line_char)
765+
if (lines[start]->buf[0] == comment_line_char) {
766+
non_trailer_lines += possible_continuation_lines;
767+
possible_continuation_lines = 0;
756768
continue;
769+
}
757770
if (contains_only_spaces(lines[start]->buf)) {
758771
if (only_spaces)
759772
continue;
773+
non_trailer_lines += possible_continuation_lines;
760774
if (recognized_prefix &&
761775
trailer_lines * 3 >= non_trailer_lines)
762776
return start + 1;
@@ -769,16 +783,18 @@ static int find_trailer_start(struct strbuf **lines, int count)
769783
for (p = git_generated_prefixes; *p; p++) {
770784
if (starts_with(lines[start]->buf, *p)) {
771785
trailer_lines++;
786+
possible_continuation_lines = 0;
772787
recognized_prefix = 1;
773788
goto continue_outer_loop;
774789
}
775790
}
776791

777-
separator_pos = find_separator(lines[start]->buf);
792+
separator_pos = find_separator(lines[start]->buf, separators);
778793
if (separator_pos >= 1 && !isspace(lines[start]->buf[0])) {
779794
struct list_head *pos;
780795

781796
trailer_lines++;
797+
possible_continuation_lines = 0;
782798
if (recognized_prefix)
783799
continue;
784800
list_for_each(pos, &conf_head) {
@@ -790,8 +806,13 @@ static int find_trailer_start(struct strbuf **lines, int count)
790806
break;
791807
}
792808
}
793-
} else
809+
} else if (isspace(lines[start]->buf[0]))
810+
possible_continuation_lines++;
811+
else {
794812
non_trailer_lines++;
813+
non_trailer_lines += possible_continuation_lines;
814+
possible_continuation_lines = 0;
815+
}
795816
continue_outer_loop:
796817
;
797818
}
@@ -840,6 +861,7 @@ static int process_input_file(FILE *outfile,
840861
int patch_start, trailer_start, trailer_end, i;
841862
struct strbuf tok = STRBUF_INIT;
842863
struct strbuf val = STRBUF_INIT;
864+
struct trailer_item *last = NULL;
843865

844866
/* Get the line count */
845867
while (lines[count])
@@ -860,19 +882,28 @@ static int process_input_file(FILE *outfile,
860882
int separator_pos;
861883
if (lines[i]->buf[0] == comment_line_char)
862884
continue;
885+
if (last && isspace(lines[i]->buf[0])) {
886+
struct strbuf sb = STRBUF_INIT;
887+
strbuf_addf(&sb, "%s\n%s", last->value, lines[i]->buf);
888+
strbuf_strip_suffix(&sb, "\n");
889+
free(last->value);
890+
last->value = strbuf_detach(&sb, NULL);
891+
continue;
892+
}
863893
separator_pos = find_separator(lines[i]->buf, separators);
864894
if (separator_pos >= 1) {
865895
parse_trailer(&tok, &val, NULL, lines[i]->buf,
866896
separator_pos);
867-
add_trailer_item(head,
868-
strbuf_detach(&tok, NULL),
869-
strbuf_detach(&val, NULL));
897+
last = add_trailer_item(head,
898+
strbuf_detach(&tok, NULL),
899+
strbuf_detach(&val, NULL));
870900
} else {
871901
strbuf_addbuf(&val, lines[i]);
872902
strbuf_strip_suffix(&val, "\n");
873903
add_trailer_item(head,
874904
NULL,
875905
strbuf_detach(&val, NULL));
906+
last = NULL;
876907
}
877908
}
878909

0 commit comments

Comments
 (0)