Merge branch 'kh/trailers-no-urls'

The trailer parsing machinery has been updated to avoid mistaking
lines that begin with a URL (e.g., 'https://...') as trailer lines.
This prevents intended textual URLs from being mangled or mistakenly
treated as metadata keys.

* kh/trailers-no-urls:
  trailers: stop recognizing URLs as trailers
main
Junio C Hamano 2026-08-31 08:24:59 -07:00
commit 1e4d33de9b
4 changed files with 86 additions and 4 deletions

View File

@ -123,9 +123,16 @@ OTHER RULES
What was covered in the previous section are the rules that are relevant
for regular use. The following points are included for completeness.

This command ignores comment lines (see `core.commentString` in
linkgit:git-config[1]). This is for use with the `prepare-commit-msg`
and `commit-msg` hooks.
--
* This command ignores comment lines (see `core.commentString` in
linkgit:git-config[1]). This is for use with the `prepare-commit-msg`
and `commit-msg` hooks.

* Candidate trailer lines that have `:` as the separator, that have no
whitespace before the value part, and that start with `//` are not
recognized as trailers. This is to avoid accidentally interpreting
URLs as trailers (e.g. lines that start with `https://`).
--

OPTIONS
-------

View File

@ -1989,4 +1989,23 @@ test_expect_success 'handling of --- lines in conjunction with cut-lines' '
test_cmp expected actual
'

test_expect_success 'URLs and lines that are not quite URLs' '
cat >expect <<-\EOF &&
https: //www.a-trailer.org
https: //www.another-trailer.org
Signed-off-by: somebody <somebody@somewhere>
EOF
git interpret-trailers --only-trailers >actual <<-\EOF &&
subject

body

https://www.not-a-trailer.org
https ://www.a-trailer.org
https: //www.another-trailer.org
Signed-off-by: somebody <somebody@somewhere>
EOF
test_cmp expect actual
'

test_done

View File

@ -318,3 +318,55 @@ void test_trailer__one_non_trailer_no_git_trailers(void)
0,
expected_contents);
}

void test_trailer__URL(void)
{
struct contents expected_contents[] = { 0 };

t_trailer_iterator("Subject: foo bar\n"
"\n"
/*
* We do not want to match URLs as trailers.
*/
"https://www.example.org\n",
0,
expected_contents);
}

void test_trailer__not_a_URL_space_after_separator(void)
{
struct contents expected_contents[] = {
{ .raw = "https: //www.example.org\n",
.key = "https",
.val = "//www.example.org" },
{ 0 },
};

t_trailer_iterator("Subject: foo bar\n"
"\n"
/*
* This has a space after ':' so it's not a URL.
*/
"https: //www.example.org\n",
1,
expected_contents);
}

void test_trailer__not_a_URL_space_before_separator(void)
{
struct contents expected_contents[] = {
{ .raw = "https ://www.example.org\n",
.key = "https",
.val = "//www.example.org" },
{ 0 },
};

t_trailer_iterator("Subject: foo bar\n"
"\n"
/*
* This has a space before ':' so it's not a URL.
*/
"https ://www.example.org\n",
1,
expected_contents);
}

View File

@ -635,8 +635,12 @@ static ssize_t find_separator(const char *line, const char *separators)
int whitespace_found = 0;
const char *c;
for (c = line; *c; c++) {
if (strchr(separators, *c))
if (strchr(separators, *c)) {
/* avoid accidental URL matches */
if (!whitespace_found && starts_with(c, "://"))
return -1;
return c - line;
}
if (!whitespace_found && (isalnum(*c) || *c == '-'))
continue;
if (c != line && (*c == ' ' || *c == '\t')) {