зеркало из https://github.com/microsoft/git.git
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:
Родитель
fdbf4510ae
Коммит
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.
|
||||
|
||||
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
|
||||
the group is preceded by one or more empty (or whitespace-only) lines.
|
||||
a group of one or more lines that (i) are all trailers, or (ii) contains at
|
||||
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
|
||||
non-whitespace lines before a line that starts with '---'. Such three
|
||||
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_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' '
|
||||
git config trailer.ack.key "Acked-by: " &&
|
||||
cat >expected <<-\EOF &&
|
||||
|
|
89
trailer.c
89
trailer.c
|
@ -27,6 +27,10 @@ static struct conf_info default_conf_info;
|
|||
|
||||
struct trailer_item {
|
||||
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 *value;
|
||||
};
|
||||
|
@ -44,6 +48,12 @@ static char *separators = ":";
|
|||
|
||||
#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. */
|
||||
#define list_for_each_dir(pos, head, is_reverse) \
|
||||
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)
|
||||
{
|
||||
size_t a_len = token_len_without_separator(a->token, strlen(a->token));
|
||||
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;
|
||||
size_t a_len, b_len, min_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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
return;
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
for (start = 0; start < count; start++) {
|
||||
|
@ -720,26 +743,60 @@ static int find_trailer_start(struct strbuf **lines, int count)
|
|||
end_of_title = start;
|
||||
|
||||
/*
|
||||
* Get the start of the trailers by looking starting from the end
|
||||
* for a line with only spaces before lines with one separator.
|
||||
* Get the start of the trailers by looking starting from the end for a
|
||||
* 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--) {
|
||||
const char **p;
|
||||
int separator_pos;
|
||||
|
||||
if (lines[start]->buf[0] == comment_line_char)
|
||||
continue;
|
||||
if (contains_only_spaces(lines[start]->buf)) {
|
||||
if (only_spaces)
|
||||
continue;
|
||||
if (recognized_prefix &&
|
||||
trailer_lines * 3 >= non_trailer_lines)
|
||||
return start + 1;
|
||||
if (trailer_lines && !non_trailer_lines)
|
||||
return start + 1;
|
||||
}
|
||||
if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
|
||||
if (only_spaces)
|
||||
only_spaces = 0;
|
||||
continue;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
only_spaces = 0;
|
||||
|
||||
return only_spaces ? count : 0;
|
||||
for (p = git_generated_prefixes; *p; p++) {
|
||||
if (starts_with(lines[start]->buf, *p)) {
|
||||
trailer_lines++;
|
||||
recognized_prefix = 1;
|
||||
goto continue_outer_loop;
|
||||
}
|
||||
}
|
||||
|
||||
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 count;
|
||||
}
|
||||
|
||||
/* Get the index of the end of the trailers */
|
||||
|
@ -810,6 +867,12 @@ static int process_input_file(FILE *outfile,
|
|||
add_trailer_item(head,
|
||||
strbuf_detach(&tok, 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче