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:
Jonathan Tan 2016-10-21 10:55:01 -07:00 коммит произвёл Junio C Hamano
Родитель fdbf4510ae
Коммит 146245063e
3 изменённых файлов: 194 добавлений и 15 удалений

Просмотреть файл

@ -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 &&

Просмотреть файл

@ -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));
}
}