trailer: allow non-trailers in trailer block
Currently, interpret-trailers requires all lines of a trailer block to
be trailers (or comments) - if not it would not identify that block as a
trailer block, and thus create its own trailer block, inserting a blank
line. For example:
echo -e "\nSigned-off-by: x\nnot trailer" |
git interpret-trailers --trailer "c: d"
would result in:
Signed-off-by: x
not trailer
c: d
Relax the definition of a trailer block to require that the trailers (i)
are all trailers, or (ii) contain at least one Git-generated trailer and
consists of at least 25% trailers.
Signed-off-by: x
not trailer
c: d
(i) is the existing functionality. (ii) allows arbitrary lines to be
included in trailer blocks, like those in [1], and still allow
interpret-trailers to be used.
[1]
e7d316a02f
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
fdbf4510ae
commit
146245063e
@ -48,8 +48,9 @@ with only spaces at the end of the commit message part, one blank line
|
|||||||
will be added before the new trailer.
|
will be added before the new trailer.
|
||||||
|
|
||||||
Existing trailers are extracted from the input message by looking for
|
Existing trailers are extracted from the input message by looking for
|
||||||
a group of one or more lines that contain a colon (by default), where
|
a group of one or more lines that (i) are all trailers, or (ii) contains at
|
||||||
the group is preceded by one or more empty (or whitespace-only) lines.
|
least one Git-generated trailer and consists of at least 25% trailers.
|
||||||
|
The group must be preceded by one or more empty (or whitespace-only) lines.
|
||||||
The group must either be at the end of the message or be the last
|
The group must either be at the end of the message or be the last
|
||||||
non-whitespace lines before a line that starts with '---'. Such three
|
non-whitespace lines before a line that starts with '---'. Such three
|
||||||
minus signs start the patch part of the message.
|
minus signs start the patch part of the message.
|
||||||
|
@ -126,6 +126,121 @@ test_expect_success 'with multiline title in the message' '
|
|||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'with non-trailer lines mixed with Signed-off-by' '
|
||||||
|
cat >patch <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
Signed-off-by: a <a@example.com>
|
||||||
|
this is not a trailer
|
||||||
|
EOF
|
||||||
|
cat >expected <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
Signed-off-by: a <a@example.com>
|
||||||
|
this is not a trailer
|
||||||
|
token: value
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "token: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'with non-trailer lines mixed with cherry picked from' '
|
||||||
|
cat >patch <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
(cherry picked from commit x)
|
||||||
|
this is not a trailer
|
||||||
|
EOF
|
||||||
|
cat >expected <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
(cherry picked from commit x)
|
||||||
|
this is not a trailer
|
||||||
|
token: value
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "token: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'with non-trailer lines mixed with a configured trailer' '
|
||||||
|
cat >patch <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
My-trailer: x
|
||||||
|
this is not a trailer
|
||||||
|
EOF
|
||||||
|
cat >expected <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
My-trailer: x
|
||||||
|
this is not a trailer
|
||||||
|
token: value
|
||||||
|
EOF
|
||||||
|
test_config trailer.my.key "My-trailer: " &&
|
||||||
|
git interpret-trailers --trailer "token: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'with non-trailer lines mixed with a non-configured trailer' '
|
||||||
|
cat >patch <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
I-am-not-configured: x
|
||||||
|
this is not a trailer
|
||||||
|
EOF
|
||||||
|
cat >expected <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
this is not a trailer
|
||||||
|
I-am-not-configured: x
|
||||||
|
this is not a trailer
|
||||||
|
|
||||||
|
token: value
|
||||||
|
EOF
|
||||||
|
test_config trailer.my.key "My-trailer: " &&
|
||||||
|
git interpret-trailers --trailer "token: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'with all non-configured trailers' '
|
||||||
|
cat >patch <<-\EOF &&
|
||||||
|
|
||||||
|
I-am-not-configured: x
|
||||||
|
I-am-also-not-configured: x
|
||||||
|
EOF
|
||||||
|
cat >expected <<-\EOF &&
|
||||||
|
|
||||||
|
I-am-not-configured: x
|
||||||
|
I-am-also-not-configured: x
|
||||||
|
token: value
|
||||||
|
EOF
|
||||||
|
test_config trailer.my.key "My-trailer: " &&
|
||||||
|
git interpret-trailers --trailer "token: value" patch >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'with non-trailer lines only' '
|
||||||
|
cat >patch <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
EOF
|
||||||
|
cat >expected <<-\EOF &&
|
||||||
|
|
||||||
|
this is not a trailer
|
||||||
|
|
||||||
|
token: value
|
||||||
|
EOF
|
||||||
|
git interpret-trailers --trailer "token: value" 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 &&
|
||||||
|
89
trailer.c
89
trailer.c
@ -27,6 +27,10 @@ static struct conf_info default_conf_info;
|
|||||||
|
|
||||||
struct trailer_item {
|
struct trailer_item {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
/*
|
||||||
|
* If this is not a trailer line, the line is stored in value
|
||||||
|
* (excluding the terminating newline) and token is NULL.
|
||||||
|
*/
|
||||||
char *token;
|
char *token;
|
||||||
char *value;
|
char *value;
|
||||||
};
|
};
|
||||||
@ -44,6 +48,12 @@ static char *separators = ":";
|
|||||||
|
|
||||||
#define TRAILER_ARG_STRING "$ARG"
|
#define TRAILER_ARG_STRING "$ARG"
|
||||||
|
|
||||||
|
static const char *git_generated_prefixes[] = {
|
||||||
|
"Signed-off-by: ",
|
||||||
|
"(cherry picked from commit ",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
/* Iterate over the elements of the list. */
|
/* Iterate over the elements of the list. */
|
||||||
#define list_for_each_dir(pos, head, is_reverse) \
|
#define list_for_each_dir(pos, head, is_reverse) \
|
||||||
for (pos = is_reverse ? (head)->prev : (head)->next; \
|
for (pos = is_reverse ? (head)->prev : (head)->next; \
|
||||||
@ -70,9 +80,14 @@ static size_t token_len_without_separator(const char *token, size_t len)
|
|||||||
|
|
||||||
static int same_token(struct trailer_item *a, struct arg_item *b)
|
static int same_token(struct trailer_item *a, struct arg_item *b)
|
||||||
{
|
{
|
||||||
size_t a_len = token_len_without_separator(a->token, strlen(a->token));
|
size_t a_len, b_len, min_len;
|
||||||
size_t b_len = token_len_without_separator(b->token, strlen(b->token));
|
|
||||||
size_t min_len = (a_len > b_len) ? b_len : a_len;
|
if (!a->token)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
a_len = token_len_without_separator(a->token, strlen(a->token));
|
||||||
|
b_len = token_len_without_separator(b->token, strlen(b->token));
|
||||||
|
min_len = (a_len > b_len) ? b_len : a_len;
|
||||||
|
|
||||||
return !strncasecmp(a->token, b->token, min_len);
|
return !strncasecmp(a->token, b->token, min_len);
|
||||||
}
|
}
|
||||||
@ -130,7 +145,14 @@ static char last_non_space_char(const char *s)
|
|||||||
|
|
||||||
static void print_tok_val(FILE *outfile, const char *tok, const char *val)
|
static void print_tok_val(FILE *outfile, const char *tok, const char *val)
|
||||||
{
|
{
|
||||||
char c = last_non_space_char(tok);
|
char c;
|
||||||
|
|
||||||
|
if (!tok) {
|
||||||
|
fprintf(outfile, "%s\n", val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = last_non_space_char(tok);
|
||||||
if (!c)
|
if (!c)
|
||||||
return;
|
return;
|
||||||
if (strchr(separators, c))
|
if (strchr(separators, c))
|
||||||
@ -709,6 +731,7 @@ static int find_patch_start(struct strbuf **lines, int count)
|
|||||||
static int find_trailer_start(struct strbuf **lines, int count)
|
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;
|
||||||
|
|
||||||
/* 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++) {
|
||||||
@ -720,26 +743,60 @@ static int find_trailer_start(struct strbuf **lines, int count)
|
|||||||
end_of_title = start;
|
end_of_title = start;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the start of the trailers by looking starting from the end
|
* Get the start of the trailers by looking starting from the end for a
|
||||||
* for a line with only spaces before lines with one separator.
|
* blank line before a set of non-blank lines that (i) are all
|
||||||
|
* trailers, or (ii) contains at least one Git-generated trailer and
|
||||||
|
* consists of at least 25% trailers.
|
||||||
*/
|
*/
|
||||||
for (start = count - 1; start >= end_of_title; start--) {
|
for (start = count - 1; start >= end_of_title; start--) {
|
||||||
|
const char **p;
|
||||||
|
int separator_pos;
|
||||||
|
|
||||||
if (lines[start]->buf[0] == comment_line_char)
|
if (lines[start]->buf[0] == comment_line_char)
|
||||||
continue;
|
continue;
|
||||||
if (contains_only_spaces(lines[start]->buf)) {
|
if (contains_only_spaces(lines[start]->buf)) {
|
||||||
if (only_spaces)
|
if (only_spaces)
|
||||||
continue;
|
continue;
|
||||||
return start + 1;
|
if (recognized_prefix &&
|
||||||
|
trailer_lines * 3 >= non_trailer_lines)
|
||||||
|
return start + 1;
|
||||||
|
if (trailer_lines && !non_trailer_lines)
|
||||||
|
return start + 1;
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
|
only_spaces = 0;
|
||||||
if (only_spaces)
|
|
||||||
only_spaces = 0;
|
for (p = git_generated_prefixes; *p; p++) {
|
||||||
continue;
|
if (starts_with(lines[start]->buf, *p)) {
|
||||||
|
trailer_lines++;
|
||||||
|
recognized_prefix = 1;
|
||||||
|
goto continue_outer_loop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return count;
|
|
||||||
|
separator_pos = find_separator(lines[start]->buf);
|
||||||
|
if (separator_pos >= 1) {
|
||||||
|
struct list_head *pos;
|
||||||
|
|
||||||
|
trailer_lines++;
|
||||||
|
if (recognized_prefix)
|
||||||
|
continue;
|
||||||
|
list_for_each(pos, &conf_head) {
|
||||||
|
struct arg_item *item;
|
||||||
|
item = list_entry(pos, struct arg_item, list);
|
||||||
|
if (token_matches_item(lines[start]->buf, item,
|
||||||
|
separator_pos)) {
|
||||||
|
recognized_prefix = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
non_trailer_lines++;
|
||||||
|
continue_outer_loop:
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
return only_spaces ? count : 0;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the index of the end of the trailers */
|
/* Get the index of the end of the trailers */
|
||||||
@ -810,6 +867,12 @@ static int process_input_file(FILE *outfile,
|
|||||||
add_trailer_item(head,
|
add_trailer_item(head,
|
||||||
strbuf_detach(&tok, NULL),
|
strbuf_detach(&tok, NULL),
|
||||||
strbuf_detach(&val, NULL));
|
strbuf_detach(&val, NULL));
|
||||||
|
} else {
|
||||||
|
strbuf_addbuf(&val, lines[i]);
|
||||||
|
strbuf_strip_suffix(&val, "\n");
|
||||||
|
add_trailer_item(head,
|
||||||
|
NULL,
|
||||||
|
strbuf_detach(&val, NULL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user