mirror of
https://github.com/git/git.git
synced 2024-11-16 02:13:11 +01:00
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 <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
c463a6b280
commit
60ef86a162
@ -57,11 +57,12 @@ minus signs start the patch part of the message.
|
|||||||
|
|
||||||
When reading trailers, there can be whitespaces after the
|
When reading trailers, there can be whitespaces after the
|
||||||
token, the separator and the value. There can also be whitespaces
|
token, the separator and the value. There can also be whitespaces
|
||||||
inside the token and the value.
|
inside the token and the value. The value may be split over multiple lines with
|
||||||
|
each subsequent line starting with whitespace, like the "folding" in RFC 822.
|
||||||
|
|
||||||
Note that 'trailers' do not follow and are not intended to follow many
|
Note that 'trailers' do not follow and are not intended to follow many
|
||||||
rules for RFC 822 headers. For example they do not follow the line
|
rules for RFC 822 headers. For example they do not follow
|
||||||
folding rules, the encoding rules and probably many other rules.
|
the encoding rules and probably many other rules.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
@ -256,6 +256,175 @@ test_expect_success 'line with leading whitespace is not trailer' '
|
|||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'multiline field treated as one trailer for 25% check' '
|
||||||
|
q_to_tab >patch <<-\EOF &&
|
||||||
|
|
||||||
|
Signed-off-by: a <a@example.com>
|
||||||
|
name: value on
|
||||||
|
Qmultiple lines
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
Signed-off-by: a <a@example.com>
|
||||||
|
name: value on
|
||||||
|
Qmultiple lines
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
name: value
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "name: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'multiline field treated as atomic for placement' '
|
||||||
|
q_to_tab >patch <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: value on
|
||||||
|
Qmultiple lines
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: value on
|
||||||
|
Qmultiple lines
|
||||||
|
name: value
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
test_config trailer.name.where after &&
|
||||||
|
git interpret-trailers --trailer "name: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'multiline field treated as atomic for replacement' '
|
||||||
|
q_to_tab >patch <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: value on
|
||||||
|
Qmultiple lines
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
another: trailer
|
||||||
|
name: value
|
||||||
|
EOF
|
||||||
|
test_config trailer.name.ifexists replace &&
|
||||||
|
git interpret-trailers --trailer "name: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'multiline field treated as atomic for difference check' '
|
||||||
|
q_to_tab >patch <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
test_config trailer.name.ifexists addIfDifferent &&
|
||||||
|
|
||||||
|
q_to_tab >trailer <<-\EOF &&
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
|
||||||
|
test_cmp expected actual &&
|
||||||
|
|
||||||
|
q_to_tab >trailer <<-\EOF &&
|
||||||
|
name: first line
|
||||||
|
QQQQQsecond line
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
QQQQQsecond line
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
|
||||||
|
test_cmp expected actual &&
|
||||||
|
|
||||||
|
q_to_tab >trailer <<-\EOF &&
|
||||||
|
name: first line *DIFFERENT*
|
||||||
|
Qsecond line
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
another: trailer
|
||||||
|
name: first line *DIFFERENT*
|
||||||
|
Qsecond line
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'multiline field treated as atomic for neighbor check' '
|
||||||
|
q_to_tab >patch <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
test_config trailer.name.where after &&
|
||||||
|
test_config trailer.name.ifexists addIfDifferentNeighbor &&
|
||||||
|
|
||||||
|
q_to_tab >trailer <<-\EOF &&
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
|
||||||
|
test_cmp expected actual &&
|
||||||
|
|
||||||
|
q_to_tab >trailer <<-\EOF &&
|
||||||
|
name: first line
|
||||||
|
QQQQQsecond line
|
||||||
|
EOF
|
||||||
|
q_to_tab >expected <<-\EOF &&
|
||||||
|
|
||||||
|
another: trailer
|
||||||
|
name: first line
|
||||||
|
Qsecond line
|
||||||
|
name: first line
|
||||||
|
QQQQQsecond line
|
||||||
|
another: trailer
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'with config setup' '
|
test_expect_success 'with config setup' '
|
||||||
git config trailer.ack.key "Acked-by: " &&
|
git config trailer.ack.key "Acked-by: " &&
|
||||||
cat >expected <<-\EOF &&
|
cat >expected <<-\EOF &&
|
||||||
|
45
trailer.c
45
trailer.c
@ -619,12 +619,14 @@ static void parse_trailer(struct strbuf *tok, struct strbuf *val,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_trailer_item(struct list_head *head, char *tok, char *val)
|
static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
|
||||||
|
char *val)
|
||||||
{
|
{
|
||||||
struct trailer_item *new = xcalloc(sizeof(*new), 1);
|
struct trailer_item *new = xcalloc(sizeof(*new), 1);
|
||||||
new->token = tok;
|
new->token = tok;
|
||||||
new->value = val;
|
new->value = val;
|
||||||
list_add_tail(&new->list, head);
|
list_add_tail(&new->list, head);
|
||||||
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
|
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)
|
|||||||
{
|
{
|
||||||
int start, end_of_title, only_spaces = 1;
|
int start, end_of_title, only_spaces = 1;
|
||||||
int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
|
int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
|
||||||
|
/*
|
||||||
|
* Number of possible continuation lines encountered. This will be
|
||||||
|
* reset to 0 if we encounter a trailer (since those lines are to be
|
||||||
|
* considered continuations of that trailer), and added to
|
||||||
|
* non_trailer_lines if we encounter a non-trailer (since those lines
|
||||||
|
* are to be considered non-trailers).
|
||||||
|
*/
|
||||||
|
int possible_continuation_lines = 0;
|
||||||
|
|
||||||
/* The first paragraph is the title and cannot be trailers */
|
/* The first paragraph is the title and cannot be trailers */
|
||||||
for (start = 0; start < count; start++) {
|
for (start = 0; start < count; start++) {
|
||||||
@ -752,11 +762,15 @@ static int find_trailer_start(struct strbuf **lines, int count)
|
|||||||
const char **p;
|
const char **p;
|
||||||
int separator_pos;
|
int separator_pos;
|
||||||
|
|
||||||
if (lines[start]->buf[0] == comment_line_char)
|
if (lines[start]->buf[0] == comment_line_char) {
|
||||||
|
non_trailer_lines += possible_continuation_lines;
|
||||||
|
possible_continuation_lines = 0;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
if (contains_only_spaces(lines[start]->buf)) {
|
if (contains_only_spaces(lines[start]->buf)) {
|
||||||
if (only_spaces)
|
if (only_spaces)
|
||||||
continue;
|
continue;
|
||||||
|
non_trailer_lines += possible_continuation_lines;
|
||||||
if (recognized_prefix &&
|
if (recognized_prefix &&
|
||||||
trailer_lines * 3 >= non_trailer_lines)
|
trailer_lines * 3 >= non_trailer_lines)
|
||||||
return start + 1;
|
return start + 1;
|
||||||
@ -769,16 +783,18 @@ static int find_trailer_start(struct strbuf **lines, int count)
|
|||||||
for (p = git_generated_prefixes; *p; p++) {
|
for (p = git_generated_prefixes; *p; p++) {
|
||||||
if (starts_with(lines[start]->buf, *p)) {
|
if (starts_with(lines[start]->buf, *p)) {
|
||||||
trailer_lines++;
|
trailer_lines++;
|
||||||
|
possible_continuation_lines = 0;
|
||||||
recognized_prefix = 1;
|
recognized_prefix = 1;
|
||||||
goto continue_outer_loop;
|
goto continue_outer_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
separator_pos = find_separator(lines[start]->buf);
|
separator_pos = find_separator(lines[start]->buf, separators);
|
||||||
if (separator_pos >= 1 && !isspace(lines[start]->buf[0])) {
|
if (separator_pos >= 1 && !isspace(lines[start]->buf[0])) {
|
||||||
struct list_head *pos;
|
struct list_head *pos;
|
||||||
|
|
||||||
trailer_lines++;
|
trailer_lines++;
|
||||||
|
possible_continuation_lines = 0;
|
||||||
if (recognized_prefix)
|
if (recognized_prefix)
|
||||||
continue;
|
continue;
|
||||||
list_for_each(pos, &conf_head) {
|
list_for_each(pos, &conf_head) {
|
||||||
@ -790,8 +806,13 @@ static int find_trailer_start(struct strbuf **lines, int count)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else if (isspace(lines[start]->buf[0]))
|
||||||
|
possible_continuation_lines++;
|
||||||
|
else {
|
||||||
non_trailer_lines++;
|
non_trailer_lines++;
|
||||||
|
non_trailer_lines += possible_continuation_lines;
|
||||||
|
possible_continuation_lines = 0;
|
||||||
|
}
|
||||||
continue_outer_loop:
|
continue_outer_loop:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@ -840,6 +861,7 @@ static int process_input_file(FILE *outfile,
|
|||||||
int patch_start, trailer_start, trailer_end, i;
|
int patch_start, trailer_start, trailer_end, i;
|
||||||
struct strbuf tok = STRBUF_INIT;
|
struct strbuf tok = STRBUF_INIT;
|
||||||
struct strbuf val = STRBUF_INIT;
|
struct strbuf val = STRBUF_INIT;
|
||||||
|
struct trailer_item *last = NULL;
|
||||||
|
|
||||||
/* Get the line count */
|
/* Get the line count */
|
||||||
while (lines[count])
|
while (lines[count])
|
||||||
@ -860,19 +882,28 @@ static int process_input_file(FILE *outfile,
|
|||||||
int separator_pos;
|
int separator_pos;
|
||||||
if (lines[i]->buf[0] == comment_line_char)
|
if (lines[i]->buf[0] == comment_line_char)
|
||||||
continue;
|
continue;
|
||||||
|
if (last && isspace(lines[i]->buf[0])) {
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
strbuf_addf(&sb, "%s\n%s", last->value, lines[i]->buf);
|
||||||
|
strbuf_strip_suffix(&sb, "\n");
|
||||||
|
free(last->value);
|
||||||
|
last->value = strbuf_detach(&sb, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
separator_pos = find_separator(lines[i]->buf, separators);
|
separator_pos = find_separator(lines[i]->buf, separators);
|
||||||
if (separator_pos >= 1) {
|
if (separator_pos >= 1) {
|
||||||
parse_trailer(&tok, &val, NULL, lines[i]->buf,
|
parse_trailer(&tok, &val, NULL, lines[i]->buf,
|
||||||
separator_pos);
|
separator_pos);
|
||||||
add_trailer_item(head,
|
last = add_trailer_item(head,
|
||||||
strbuf_detach(&tok, NULL),
|
strbuf_detach(&tok, NULL),
|
||||||
strbuf_detach(&val, NULL));
|
strbuf_detach(&val, NULL));
|
||||||
} else {
|
} else {
|
||||||
strbuf_addbuf(&val, lines[i]);
|
strbuf_addbuf(&val, lines[i]);
|
||||||
strbuf_strip_suffix(&val, "\n");
|
strbuf_strip_suffix(&val, "\n");
|
||||||
add_trailer_item(head,
|
add_trailer_item(head,
|
||||||
NULL,
|
NULL,
|
||||||
strbuf_detach(&val, NULL));
|
strbuf_detach(&val, NULL));
|
||||||
|
last = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user