{"thread":{"id":"62068","subject":"[PATCH] interpret-trailers: handle message without trailing newline","startedAt":"2024-09-05T17:35:08Z","lastAt":"2024-09-09T15:58:07Z","messageCount":10,"participants":["Brian Lyles","Phillip Wood","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"502269","messageId":"20240905173445.1677704-1-brianmlyles@gmail.com","threadId":"62068","inReplyTo":null,"subject":"[PATCH] interpret-trailers: handle message without trailing newline","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-09-05T17:34:40Z","receivedAt":"2024-09-05T17:35:08Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When git-interpret-trailers is used to add a trailer to a message that\ndoes not end in a trailing newline, the new trailer is added on the line\nimmediately following the message instead of as a trailer block\nseparated from the message by a newline character.\n\nFor example, if a message's text was \"The subject\" with no trailing\nnewline at all, `git interpret-trailers --trailer my-trailer=true` will\nresult in the following malformed commit message:\n\n    The subject\n    my-trailer: true\n\nWhile it is generally expected that a commit message should end with a\nnewline character, git-interpret-trailers should not be returning an\ninvalid message in this case.\n\nDetect when a message exists but does not end with a newline character,\nand add an extra newline before appending the new trailer.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n builtin/interpret-trailers.c  | 12 ++++++-----\n t/t7513-interpret-trailers.sh | 40 +++++++++++++++++++++++++++++++++++\n trailer.c                     | 18 ++++++++++++++++\n trailer.h                     |  5 +++++\n 4 files changed, 70 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 1d969494cf..9d8f94341d 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -153,13 +153,15 @@ static void interpret_trailers(const struct process_trailer_options *opts,\n\n \tinfo = parse_trailers(opts, sb.buf, &head);\n\n-\t/* Print the lines before the trailers */\n-\tif (!opts->only_trailers)\n+\tif (!opts->only_trailers) {\n+\t\t/* Print the lines before the trailers */\n \t\tfwrite(sb.buf, 1, trailer_block_start(info), outfile);\n\n-\tif (!opts->only_trailers && !blank_line_before_trailer_block(info))\n-\t\tfprintf(outfile, \"\\n\");\n-\n+\t\tif (message_without_trailing_newline_before_trailer_block(info))\n+\t\t\tfprintf(outfile, \"\\n\\n\");\n+\t\telse if (!blank_line_before_trailer_block(info))\n+\t\t\tfprintf(outfile, \"\\n\");\n+\t}\n\n \tif (!opts->only_input) {\n \t\tLIST_HEAD(config_head);\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nindex 3d3e13ccf8..d5303c3f74 100755\n--- a/t/t7513-interpret-trailers.sh\n+++ b/t/t7513-interpret-trailers.sh\n@@ -175,6 +175,46 @@ test_expect_success 'with only a title in the message' '\n \ttest_cmp expected actual\n '\n\n+test_expect_success 'with a bodiless message that lacks a trailing newline after the subject' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\" | \\\n+\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n+\t\t--trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with a bodied message that lacks a trailing newline after the body' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tdetails about the change.\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\\n\\ndetails about the change.\" | \\\n+\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n+\t\t--trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with a message that lacks a trailing newline after the trailers' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\\n\\nReviewed-by: Peff\" | \\\n+\tgit interpret-trailers --trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'with multiline title in the message' '\n \tcat >expected <<-\\EOF &&\n \t\tplace of\ndiff --git a/trailer.c b/trailer.c\nindex 72e5136c73..9c19632b6d 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -18,6 +18,12 @@ struct trailer_info {\n \t */\n \tint blank_line_before_trailer;\n\n+\t/*\n+\t * True if the last character before the location pointed to be\n+\t * trailer_block_start is a newline character.\n+\t */\n+\tint message_without_trailing_newline_before_trailer;\n+\n \t/*\n \t * Offsets to the trailer block start and end positions in the input\n \t * string. If no trailer block is found, these are both set to the\n@@ -946,6 +952,11 @@ static int ends_with_blank_line(const char *buf, size_t len)\n \treturn is_blank_line(buf + ll);\n }\n\n+static int has_message_without_trailing_newline_char(const char *buf, size_t len)\n+{\n+\treturn len > 0 && buf[len - 1] != '\\n';\n+}\n+\n static void unfold_value(struct strbuf *val)\n {\n \tstruct strbuf out = STRBUF_INIT;\n@@ -1017,6 +1028,8 @@ static struct trailer_info *trailer_info_get(const struct process_trailer_option\n\n \tinfo->blank_line_before_trailer = ends_with_blank_line(str,\n \t\t\t\t\t\t\t       trailer_block_start);\n+\tinfo->message_without_trailing_newline_before_trailer\n+\t\t= has_message_without_trailing_newline_char(str, trailer_block_start);\n \tinfo->trailer_block_start = trailer_block_start;\n \tinfo->trailer_block_end = end_of_log_message;\n \tinfo->trailers = trailer_strings;\n@@ -1090,6 +1103,11 @@ int blank_line_before_trailer_block(struct trailer_info *info)\n \treturn info->blank_line_before_trailer;\n }\n\n+int message_without_trailing_newline_before_trailer_block(struct trailer_info *info)\n+{\n+\treturn info->message_without_trailing_newline_before_trailer;\n+}\n+\n void trailer_info_release(struct trailer_info *info)\n {\n \tsize_t i;\ndiff --git a/trailer.h b/trailer.h\nindex 6eb53df155..04148be432 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -125,6 +125,11 @@ size_t trailer_block_end(struct trailer_info *);\n  */\n int blank_line_before_trailer_block(struct trailer_info *);\n\n+/*\n+ * Return 1 if the trailer block had a newline character\n+ */\n+int message_without_trailing_newline_before_trailer_block(struct trailer_info *);\n+\n /*\n  * Free trailer_info struct.\n  */\n--\n2.45.2\n"},{"id":"502276","messageId":"17f26c45e0877048.9cb17c440a994cc2.6350cf4ad3b0703a@epic110867","threadId":"62068","inReplyTo":"20240905173445.1677704-1-brianmlyles@gmail.com","subject":"Re: [PATCH] interpret-trailers: handle message without trailing newline","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-09-05T18:24:14Z","receivedAt":"2024-09-05T18:24:18Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"\nOn 05/09/2024 12:34, Brian Lyles wrote:\n> +\t/*\n> +\t * True if the last character before the location pointed to be\n> +\t * trailer_block_start is a newline character.\n> +\t */\n> +\tint message_without_trailing_newline_before_trailer;\n\nI just noticed that this comment is incorrect, reflecting an older\nlocal version of this patch. In v2 this will be:\n\n    True if there is a message before the trailer block and it does not\n\tend with a trailing newline character.\n\n\n> +/*\n> + * Return 1 if the trailer block had a newline character\n> + */\n> +int message_without_trailing_newline_before_trailer_block(struct trailer_info *);\n\nSame thing here. In v2 this will be:\n\n\tReturn true if there is a message before the trailer block and it\n\tdoes not end with a trailing newline character.\n\nI will wait to send out a v2 until later tonight to avoid spamming\nfolks, and give others a chance to provide feedback.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"502303","messageId":"20240906041326.1684570-1-brianmlyles@gmail.com","threadId":"62068","inReplyTo":"20240905173445.1677704-1-brianmlyles@gmail.com","subject":"[PATCH v2] interpret-trailers: handle message without trailing newline","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-09-06T04:08:41Z","receivedAt":"2024-09-06T04:14:01Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When git-interpret-trailers is used to add a trailer to a message that\ndoes not end in a trailing newline, the new trailer is added on the line\nimmediately following the message instead of as a trailer block\nseparated from the message by a blank line.\n\nFor example, if a message's text was exactly \"The subject\" with no\ntrailing newline present, `git interpret-trailers --trailer\nmy-trailer=true` will result in the following malformed commit message:\n\n    The subject\n    my-trailer: true\n\nWhile it is generally expected that a commit message should end with a\nnewline character, git-interpret-trailers should not be returning an\ninvalid message in this case.\n\nDetect when a message exists but does not end with a newline character,\nand add an extra newline before appending the new trailer.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nDifferences from v1:\n- Minor tweak to commit message wording\n- Updated stale documentation from initial prototype to accurately\n  reflect the actual state of the code.\n\nRange-diff vs v1:\n\n1:  7f67f06a08 ! 1:  af02465f86 interpret-trailers: handle message without trailing newline\n    @@ Commit message\n         When git-interpret-trailers is used to add a trailer to a message that\n         does not end in a trailing newline, the new trailer is added on the line\n         immediately following the message instead of as a trailer block\n    -    separated from the message by a newline character.\n    +    separated from the message by a blank line.\n\n    -    For example, if a message's text was \"The subject\" with no trailing\n    -    newline at all, `git interpret-trailers --trailer my-trailer=true` will\n    -    result in the following malformed commit message:\n    +    For example, if a message's text was exactly \"The subject\" with no\n    +    trailing newline present, `git interpret-trailers --trailer\n    +    my-trailer=true` will result in the following malformed commit message:\n\n             The subject\n             my-trailer: true\n    @@ trailer.c: struct trailer_info {\n         int blank_line_before_trailer;\n\n     +   /*\n    -+    * True if the last character before the location pointed to be\n    -+    * trailer_block_start is a newline character.\n    ++    * True if there is a message before the trailer block and it does not\n    ++    * end with a trailing newline character.\n     +    */\n     +   int message_without_trailing_newline_before_trailer;\n     +\n    @@ trailer.h: size_t trailer_block_end(struct trailer_info *);\n      int blank_line_before_trailer_block(struct trailer_info *);\n\n     +/*\n    -+ * Return 1 if the trailer block had a newline character\n    ++ * Return true if there is a message before the trailer block and it does not\n    ++ * end with a trailing newline character.\n     + */\n     +int message_without_trailing_newline_before_trailer_block(struct trailer_info *);\n     +\n\n builtin/interpret-trailers.c  | 12 ++++++-----\n t/t7513-interpret-trailers.sh | 40 +++++++++++++++++++++++++++++++++++\n trailer.c                     | 18 ++++++++++++++++\n trailer.h                     |  6 ++++++\n 4 files changed, 71 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 1d969494cf..9d8f94341d 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -153,13 +153,15 @@ static void interpret_trailers(const struct process_trailer_options *opts,\n\n \tinfo = parse_trailers(opts, sb.buf, &head);\n\n-\t/* Print the lines before the trailers */\n-\tif (!opts->only_trailers)\n+\tif (!opts->only_trailers) {\n+\t\t/* Print the lines before the trailers */\n \t\tfwrite(sb.buf, 1, trailer_block_start(info), outfile);\n\n-\tif (!opts->only_trailers && !blank_line_before_trailer_block(info))\n-\t\tfprintf(outfile, \"\\n\");\n-\n+\t\tif (message_without_trailing_newline_before_trailer_block(info))\n+\t\t\tfprintf(outfile, \"\\n\\n\");\n+\t\telse if (!blank_line_before_trailer_block(info))\n+\t\t\tfprintf(outfile, \"\\n\");\n+\t}\n\n \tif (!opts->only_input) {\n \t\tLIST_HEAD(config_head);\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nindex 3d3e13ccf8..d5303c3f74 100755\n--- a/t/t7513-interpret-trailers.sh\n+++ b/t/t7513-interpret-trailers.sh\n@@ -175,6 +175,46 @@ test_expect_success 'with only a title in the message' '\n \ttest_cmp expected actual\n '\n\n+test_expect_success 'with a bodiless message that lacks a trailing newline after the subject' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\" | \\\n+\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n+\t\t--trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with a bodied message that lacks a trailing newline after the body' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tdetails about the change.\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\\n\\ndetails about the change.\" | \\\n+\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n+\t\t--trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with a message that lacks a trailing newline after the trailers' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\\n\\nReviewed-by: Peff\" | \\\n+\tgit interpret-trailers --trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'with multiline title in the message' '\n \tcat >expected <<-\\EOF &&\n \t\tplace of\ndiff --git a/trailer.c b/trailer.c\nindex 72e5136c73..178adae6ad 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -18,6 +18,12 @@ struct trailer_info {\n \t */\n \tint blank_line_before_trailer;\n\n+\t/*\n+\t * True if there is a message before the trailer block and it does not\n+\t * end with a trailing newline character.\n+\t */\n+\tint message_without_trailing_newline_before_trailer;\n+\n \t/*\n \t * Offsets to the trailer block start and end positions in the input\n \t * string. If no trailer block is found, these are both set to the\n@@ -946,6 +952,11 @@ static int ends_with_blank_line(const char *buf, size_t len)\n \treturn is_blank_line(buf + ll);\n }\n\n+static int has_message_without_trailing_newline_char(const char *buf, size_t len)\n+{\n+\treturn len > 0 && buf[len - 1] != '\\n';\n+}\n+\n static void unfold_value(struct strbuf *val)\n {\n \tstruct strbuf out = STRBUF_INIT;\n@@ -1017,6 +1028,8 @@ static struct trailer_info *trailer_info_get(const struct process_trailer_option\n\n \tinfo->blank_line_before_trailer = ends_with_blank_line(str,\n \t\t\t\t\t\t\t       trailer_block_start);\n+\tinfo->message_without_trailing_newline_before_trailer\n+\t\t= has_message_without_trailing_newline_char(str, trailer_block_start);\n \tinfo->trailer_block_start = trailer_block_start;\n \tinfo->trailer_block_end = end_of_log_message;\n \tinfo->trailers = trailer_strings;\n@@ -1090,6 +1103,11 @@ int blank_line_before_trailer_block(struct trailer_info *info)\n \treturn info->blank_line_before_trailer;\n }\n\n+int message_without_trailing_newline_before_trailer_block(struct trailer_info *info)\n+{\n+\treturn info->message_without_trailing_newline_before_trailer;\n+}\n+\n void trailer_info_release(struct trailer_info *info)\n {\n \tsize_t i;\ndiff --git a/trailer.h b/trailer.h\nindex 6eb53df155..81449151c7 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -125,6 +125,12 @@ size_t trailer_block_end(struct trailer_info *);\n  */\n int blank_line_before_trailer_block(struct trailer_info *);\n\n+/*\n+ * Return true if there is a message before the trailer block and it does not\n+ * end with a trailing newline character.\n+ */\n+int message_without_trailing_newline_before_trailer_block(struct trailer_info *);\n+\n /*\n  * Free trailer_info struct.\n  */\n--\n2.45.2\n"},{"id":"502312","messageId":"fab48d5b-4808-439e-9384-ca4861b95edc@gmail.com","threadId":"62068","inReplyTo":"20240906041326.1684570-1-brianmlyles@gmail.com","subject":"Re: [PATCH v2] interpret-trailers: handle message without trailing newline","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-09-06T09:07:02Z","receivedAt":"2024-09-06T09:07:07Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 06/09/2024 05:08, Brian Lyles wrote:\n> When git-interpret-trailers is used to add a trailer to a message that\n> does not end in a trailing newline, the new trailer is added on the line\n> immediately following the message instead of as a trailer block\n> separated from the message by a blank line.\n> \n> For example, if a message's text was exactly \"The subject\" with no\n> trailing newline present, `git interpret-trailers --trailer\n> my-trailer=true` will result in the following malformed commit message:\n> \n>      The subject\n>      my-trailer: true\n> \n> While it is generally expected that a commit message should end with a\n> newline character, git-interpret-trailers should not be returning an\n> invalid message in this case.\n> \n> Detect when a message exists but does not end with a newline character,\n> and add an extra newline before appending the new trailer.\n\nThanks for the comprehensive commit message. If the problem only affects\n\"git interpret-trailers\" I wonder if it would be simpler to do\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 1d969494cf..e6f22459f1 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -132,6 +132,7 @@ static void read_input_file(struct strbuf *sb, const char *file)\n                  if (strbuf_read(sb, fileno(stdin), 0) < 0)\n                          die_errno(_(\"could not read from stdin\"));\n          }\n+        strbuf_complete_line(sb);\n  }\n  \n  static void interpret_trailers(const struct process_trailer_options *opts,\n\nSo that we feed the trailer machinery a message with a trailing new line.\nThanks for adding some tests, I've left one small comment below.\n\n> diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\n> index 1d969494cf..9d8f94341d 100644\n> --- a/builtin/interpret-trailers.c\n> +++ b/builtin/interpret-trailers.c\n> @@ -153,13 +153,15 @@ static void interpret_trailers(const struct process_trailer_options *opts,\n> \n>   \tinfo = parse_trailers(opts, sb.buf, &head);\n> \n> -\t/* Print the lines before the trailers */\n> -\tif (!opts->only_trailers)\n> +\tif (!opts->only_trailers) {\n> +\t\t/* Print the lines before the trailers */\n>   \t\tfwrite(sb.buf, 1, trailer_block_start(info), outfile);\n> \n> -\tif (!opts->only_trailers && !blank_line_before_trailer_block(info))\n> -\t\tfprintf(outfile, \"\\n\");\n> -\n> +\t\tif (message_without_trailing_newline_before_trailer_block(info))\n> +\t\t\tfprintf(outfile, \"\\n\\n\");\n> +\t\telse if (!blank_line_before_trailer_block(info))\n> +\t\t\tfprintf(outfile, \"\\n\");\n> +\t}\n> \n>   \tif (!opts->only_input) {\n>   \t\tLIST_HEAD(config_head);\n> diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\n> index 3d3e13ccf8..d5303c3f74 100755\n> --- a/t/t7513-interpret-trailers.sh\n> +++ b/t/t7513-interpret-trailers.sh\n> @@ -175,6 +175,46 @@ test_expect_success 'with only a title in the message' '\n>   \ttest_cmp expected actual\n>   '\n> \n> +test_expect_success 'with a bodiless message that lacks a trailing newline after the subject' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\" | \\\n\nThere is no need to add a backslash after the pipe here or in the other tests.\n\nBest Wishes\n\nPhillip\n\n> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'with a bodied message that lacks a trailing newline after the body' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tdetails about the change.\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\\n\\ndetails about the change.\" | \\\n> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'with a message that lacks a trailing newline after the trailers' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\\n\\nReviewed-by: Peff\" | \\\n> +\tgit interpret-trailers --trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n>   test_expect_success 'with multiline title in the message' '\n>   \tcat >expected <<-\\EOF &&\n>   \t\tplace of\n> diff --git a/trailer.c b/trailer.c\n> index 72e5136c73..178adae6ad 100644\n> --- a/trailer.c\n> +++ b/trailer.c\n> @@ -18,6 +18,12 @@ struct trailer_info {\n>   \t */\n>   \tint blank_line_before_trailer;\n> \n> +\t/*\n> +\t * True if there is a message before the trailer block and it does not\n> +\t * end with a trailing newline character.\n> +\t */\n> +\tint message_without_trailing_newline_before_trailer;\n> +\n>   \t/*\n>   \t * Offsets to the trailer block start and end positions in the input\n>   \t * string. If no trailer block is found, these are both set to the\n> @@ -946,6 +952,11 @@ static int ends_with_blank_line(const char *buf, size_t len)\n>   \treturn is_blank_line(buf + ll);\n>   }\n> \n> +static int has_message_without_trailing_newline_char(const char *buf, size_t len)\n> +{\n> +\treturn len > 0 && buf[len - 1] != '\\n';\n> +}\n> +\n>   static void unfold_value(struct strbuf *val)\n>   {\n>   \tstruct strbuf out = STRBUF_INIT;\n> @@ -1017,6 +1028,8 @@ static struct trailer_info *trailer_info_get(const struct process_trailer_option\n> \n>   \tinfo->blank_line_before_trailer = ends_with_blank_line(str,\n>   \t\t\t\t\t\t\t       trailer_block_start);\n> +\tinfo->message_without_trailing_newline_before_trailer\n> +\t\t= has_message_without_trailing_newline_char(str, trailer_block_start);\n>   \tinfo->trailer_block_start = trailer_block_start;\n>   \tinfo->trailer_block_end = end_of_log_message;\n>   \tinfo->trailers = trailer_strings;\n> @@ -1090,6 +1103,11 @@ int blank_line_before_trailer_block(struct trailer_info *info)\n>   \treturn info->blank_line_before_trailer;\n>   }\n> \n> +int message_without_trailing_newline_before_trailer_block(struct trailer_info *info)\n> +{\n> +\treturn info->message_without_trailing_newline_before_trailer;\n> +}\n> +\n>   void trailer_info_release(struct trailer_info *info)\n>   {\n>   \tsize_t i;\n> diff --git a/trailer.h b/trailer.h\n> index 6eb53df155..81449151c7 100644\n> --- a/trailer.h\n> +++ b/trailer.h\n> @@ -125,6 +125,12 @@ size_t trailer_block_end(struct trailer_info *);\n>    */\n>   int blank_line_before_trailer_block(struct trailer_info *);\n> \n> +/*\n> + * Return true if there is a message before the trailer block and it does not\n> + * end with a trailing newline character.\n> + */\n> +int message_without_trailing_newline_before_trailer_block(struct trailer_info *);\n> +\n>   /*\n>    * Free trailer_info struct.\n>    */\n> --\n> 2.45.2\n> \n"},{"id":"502330","messageId":"20240906145743.2059405-1-brianmlyles@gmail.com","threadId":"62068","inReplyTo":"20240905173445.1677704-1-brianmlyles@gmail.com","subject":"[PATCH v3] interpret-trailers: handle message without trailing newline","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-09-06T14:50:08Z","receivedAt":"2024-09-06T14:57:57Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When git-interpret-trailers is used to add a trailer to a message that\ndoes not end in a trailing newline, the new trailer is added on the line\nimmediately following the message instead of as a trailer block\nseparated from the message by a blank line.\n\nFor example, if a message's text was exactly \"The subject\" with no\ntrailing newline present, `git interpret-trailers --trailer\nmy-trailer=true` will result in the following malformed commit message:\n\n    The subject\n    my-trailer: true\n\nWhile it is generally expected that a commit message should end with a\nnewline character, git-interpret-trailers should not be returning an\ninvalid message in this case.\n\nUse `strbuf_complete_line` to ensure that the message ends with a\nnewline character when reading the input.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nDifferences from v2:\n- We now use `strbuf_complete_line` when reading the input file instead\n  of handling the lack of a newline when constructing the output, which\n  drastically simplifies the patch. Thanks to Phillip for this\n  suggestion.\n- Removed some unnecessary `\\` in the new tests.\n\nThe range-diff from v2 is not included since the patch is so different\nthat range-diff is not able to provide anything meaningful.\n\n\n builtin/interpret-trailers.c  |  1 +\n t/t7513-interpret-trailers.sh | 40 +++++++++++++++++++++++++++++++++++\n 2 files changed, 41 insertions(+)\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 1d969494cf..e6f22459f1 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -132,6 +132,7 @@ static void read_input_file(struct strbuf *sb, const char *file)\n \t\tif (strbuf_read(sb, fileno(stdin), 0) < 0)\n \t\t\tdie_errno(_(\"could not read from stdin\"));\n \t}\n+\tstrbuf_complete_line(sb);\n }\n\n static void interpret_trailers(const struct process_trailer_options *opts,\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nindex 3d3e13ccf8..d78cae3e04 100755\n--- a/t/t7513-interpret-trailers.sh\n+++ b/t/t7513-interpret-trailers.sh\n@@ -175,6 +175,46 @@ test_expect_success 'with only a title in the message' '\n \ttest_cmp expected actual\n '\n\n+test_expect_success 'with a bodiless message that lacks a trailing newline after the subject' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\" |\n+\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n+\t\t--trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with a bodied message that lacks a trailing newline after the body' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tdetails about the change.\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\\n\\ndetails about the change.\" |\n+\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n+\t\t--trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with a message that lacks a trailing newline after the trailers' '\n+\tcat >expected <<-\\EOF &&\n+\t\tarea: change\n+\n+\t\tReviewed-by: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tprintf \"area: change\\n\\nReviewed-by: Peff\" |\n+\tgit interpret-trailers --trailer \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'with multiline title in the message' '\n \tcat >expected <<-\\EOF &&\n \t\tplace of\n--\n2.45.2\n"},{"id":"502333","messageId":"xmqqmskkyfep.fsf@gitster.g","threadId":"62068","inReplyTo":"fab48d5b-4808-439e-9384-ca4861b95edc@gmail.com","subject":"Re: [PATCH v2] interpret-trailers: handle message without trailing newline","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-06T15:23:42Z","receivedAt":"2024-09-06T15:23:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> Thanks for the comprehensive commit message. If the problem only affects\n> \"git interpret-trailers\" I wonder if it would be simpler to do\n>\n> diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\n> index 1d969494cf..e6f22459f1 100644\n> --- a/builtin/interpret-trailers.c\n> +++ b/builtin/interpret-trailers.c\n> @@ -132,6 +132,7 @@ static void read_input_file(struct strbuf *sb, const char *file)\n>                  if (strbuf_read(sb, fileno(stdin), 0) < 0)\n>                          die_errno(_(\"could not read from stdin\"));\n>          }\n> +        strbuf_complete_line(sb);\n>  }\n\nIt is much simpler, and if we are to require a message that the user\nuses interpret-trailers on not to end in an incomplete line (which I\ndo not have any objection to), it is absolutely the right approach.\n\nWith a devil's advocate hat on, though, if the trailer operation is\nto find the trailer on the incomplete line at the end, and insert a\ntrailer _before_ that one, would it be more faithful to the command\ngiven by the end-user, if we inserted the new trailer without\ntouching the existing trailer line (including its lack of\nterminating EOL)?  Which would mean that we'd need to remember the\nfact that we added a LF here, and then before writing the result out\nmake the buffer to end with an incomplete line.  Which I personally\nthink is crazy, compared to the approach to declare that a message\nthat you subject to interpret-trailers command MUST BE a proper\ntext, not ending with an incomplete line.\n\nSo, yeah, I like that idea.\n\n"},{"id":"502339","messageId":"xmqqy144wy60.fsf@gitster.g","threadId":"62068","inReplyTo":"20240906145743.2059405-1-brianmlyles@gmail.com","subject":"Re: [PATCH v3] interpret-trailers: handle message without trailing newline","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-06T16:21:27Z","receivedAt":"2024-09-06T16:21:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brian Lyles <brianmlyles@gmail.com> writes:\n\n> When git-interpret-trailers is used to add a trailer to a message that\n> does not end in a trailing newline, the new trailer is added on the line\n> immediately following the message instead of as a trailer block\n> separated from the message by a blank line.\n>\n> For example, if a message's text was exactly \"The subject\" with no\n> trailing newline present, `git interpret-trailers --trailer\n> my-trailer=true` will result in the following malformed commit message:\n>\n>     The subject\n>     my-trailer: true\n>\n> While it is generally expected that a commit message should end with a\n> newline character, git-interpret-trailers should not be returning an\n> invalid message in this case.\n\nI am not sure if the above example resulted in \"an invalid message\",\nthough ;-)  As far as Git is concerned, a commit log can contain any\nsequence of bytes.\n\nBut of course, various tools to manipulate the messages (e.g.\n\"commit --amend\" and your editor that gets invoked by it,\n\"interpret-trailers\") may not be prepared to see any arbitrary\nbytes.  I would have written\n\n    While a commit message can contain arbitrary byte sequence, the\n    fact that the user invoked the interpret-trailers command on it\n    means that the contents is expected to be a proper text, which\n    should not end in an incomplete line.  Instead of detecting and\n    erroring out upon seeing such a log message, complete the last\n    line if it lacks the terminating LF.\n\nor something like that, if I were working on this change.\n\n> Use `strbuf_complete_line` to ensure that the message ends with a\n> newline character when reading the input.\n>\n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n>\n> The range-diff from v2 is not included since the patch is so different\n> that range-diff is not able to provide anything meaningful.\n\nVery sensible.\n\nWill queue.  Thanks.\n\n> diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\n> index 3d3e13ccf8..d78cae3e04 100755\n> --- a/t/t7513-interpret-trailers.sh\n> +++ b/t/t7513-interpret-trailers.sh\n> @@ -175,6 +175,46 @@ test_expect_success 'with only a title in the message' '\n>  \ttest_cmp expected actual\n>  '\n>\n> +test_expect_success 'with a bodiless message that lacks a trailing newline after the subject' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\" |\n> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'with a bodied message that lacks a trailing newline after the body' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tdetails about the change.\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\\n\\ndetails about the change.\" |\n> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'with a message that lacks a trailing newline after the trailers' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\\n\\nReviewed-by: Peff\" |\n> +\tgit interpret-trailers --trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n>  test_expect_success 'with multiline title in the message' '\n>  \tcat >expected <<-\\EOF &&\n>  \t\tplace of\n> --\n> 2.45.2\n"},{"id":"502427","messageId":"ca753ef4-c567-44c3-b4c3-8e483a9a3a95@gmail.com","threadId":"62068","inReplyTo":"20240906145743.2059405-1-brianmlyles@gmail.com","subject":"Re: [PATCH v3] interpret-trailers: handle message without trailing newline","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-09-09T09:13:31Z","receivedAt":"2024-09-09T09:13:35Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nThis version looks good to me\n\nThanks\n\nPhillip\n\nOn 06/09/2024 15:50, Brian Lyles wrote:\n> When git-interpret-trailers is used to add a trailer to a message that\n> does not end in a trailing newline, the new trailer is added on the line\n> immediately following the message instead of as a trailer block\n> separated from the message by a blank line.\n> \n> For example, if a message's text was exactly \"The subject\" with no\n> trailing newline present, `git interpret-trailers --trailer\n> my-trailer=true` will result in the following malformed commit message:\n> \n>      The subject\n>      my-trailer: true\n> \n> While it is generally expected that a commit message should end with a\n> newline character, git-interpret-trailers should not be returning an\n> invalid message in this case.\n> \n> Use `strbuf_complete_line` to ensure that the message ends with a\n> newline character when reading the input.\n> \n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n> \n> Differences from v2:\n> - We now use `strbuf_complete_line` when reading the input file instead\n>    of handling the lack of a newline when constructing the output, which\n>    drastically simplifies the patch. Thanks to Phillip for this\n>    suggestion.\n> - Removed some unnecessary `\\` in the new tests.\n> \n> The range-diff from v2 is not included since the patch is so different\n> that range-diff is not able to provide anything meaningful.\n> \n> \n>   builtin/interpret-trailers.c  |  1 +\n>   t/t7513-interpret-trailers.sh | 40 +++++++++++++++++++++++++++++++++++\n>   2 files changed, 41 insertions(+)\n> \n> diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\n> index 1d969494cf..e6f22459f1 100644\n> --- a/builtin/interpret-trailers.c\n> +++ b/builtin/interpret-trailers.c\n> @@ -132,6 +132,7 @@ static void read_input_file(struct strbuf *sb, const char *file)\n>   \t\tif (strbuf_read(sb, fileno(stdin), 0) < 0)\n>   \t\t\tdie_errno(_(\"could not read from stdin\"));\n>   \t}\n> +\tstrbuf_complete_line(sb);\n>   }\n> \n>   static void interpret_trailers(const struct process_trailer_options *opts,\n> diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\n> index 3d3e13ccf8..d78cae3e04 100755\n> --- a/t/t7513-interpret-trailers.sh\n> +++ b/t/t7513-interpret-trailers.sh\n> @@ -175,6 +175,46 @@ test_expect_success 'with only a title in the message' '\n>   \ttest_cmp expected actual\n>   '\n> \n> +test_expect_success 'with a bodiless message that lacks a trailing newline after the subject' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\" |\n> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'with a bodied message that lacks a trailing newline after the body' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tdetails about the change.\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\\n\\ndetails about the change.\" |\n> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'with a message that lacks a trailing newline after the trailers' '\n> +\tcat >expected <<-\\EOF &&\n> +\t\tarea: change\n> +\n> +\t\tReviewed-by: Peff\n> +\t\tAcked-by: Johan\n> +\tEOF\n> +\tprintf \"area: change\\n\\nReviewed-by: Peff\" |\n> +\tgit interpret-trailers --trailer \"Acked-by: Johan\" >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n>   test_expect_success 'with multiline title in the message' '\n>   \tcat >expected <<-\\EOF &&\n>   \t\tplace of\n> --\n> 2.45.2\n> \n"},{"id":"502428","messageId":"5f804f35-a69c-454e-a505-4ceeb9859387@gmail.com","threadId":"62068","inReplyTo":"xmqqy144wy60.fsf@gitster.g","subject":"Re: [PATCH v3] interpret-trailers: handle message without trailing newline","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-09-09T09:13:41Z","receivedAt":"2024-09-09T09:13:45Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 06/09/2024 17:21, Junio C Hamano wrote:\n> Brian Lyles <brianmlyles@gmail.com> writes:\n> \n>> When git-interpret-trailers is used to add a trailer to a message that\n>> does not end in a trailing newline, the new trailer is added on the line\n>> immediately following the message instead of as a trailer block\n>> separated from the message by a blank line.\n>>\n>> For example, if a message's text was exactly \"The subject\" with no\n>> trailing newline present, `git interpret-trailers --trailer\n>> my-trailer=true` will result in the following malformed commit message:\n>>\n>>      The subject\n>>      my-trailer: true\n>>\n>> While it is generally expected that a commit message should end with a\n>> newline character, git-interpret-trailers should not be returning an\n>> invalid message in this case.\n> \n> I am not sure if the above example resulted in \"an invalid message\",\n> though ;-)  As far as Git is concerned, a commit log can contain any\n> sequence of bytes.\n\nI assume it means invalid in the sense that the trailers are not \nseparated from the rest of the message by a blank line, not in the sense \nthat the resulting commit object is invalid.\n\nBest Wishes\n\nPhillip\n\n> But of course, various tools to manipulate the messages (e.g.\n> \"commit --amend\" and your editor that gets invoked by it,\n> \"interpret-trailers\") may not be prepared to see any arbitrary\n> bytes.  I would have written\n> \n>      While a commit message can contain arbitrary byte sequence, the\n>      fact that the user invoked the interpret-trailers command on it\n>      means that the contents is expected to be a proper text, which\n>      should not end in an incomplete line.  Instead of detecting and\n>      erroring out upon seeing such a log message, complete the last\n>      line if it lacks the terminating LF.\n> \n> or something like that, if I were working on this change.\n> \n>> Use `strbuf_complete_line` to ensure that the message ends with a\n>> newline character when reading the input.\n>>\n>> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n>> ---\n>>\n>> The range-diff from v2 is not included since the patch is so different\n>> that range-diff is not able to provide anything meaningful.\n> \n> Very sensible.\n> \n> Will queue.  Thanks.\n> \n>> diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\n>> index 3d3e13ccf8..d78cae3e04 100755\n>> --- a/t/t7513-interpret-trailers.sh\n>> +++ b/t/t7513-interpret-trailers.sh\n>> @@ -175,6 +175,46 @@ test_expect_success 'with only a title in the message' '\n>>   \ttest_cmp expected actual\n>>   '\n>>\n>> +test_expect_success 'with a bodiless message that lacks a trailing newline after the subject' '\n>> +\tcat >expected <<-\\EOF &&\n>> +\t\tarea: change\n>> +\n>> +\t\tReviewed-by: Peff\n>> +\t\tAcked-by: Johan\n>> +\tEOF\n>> +\tprintf \"area: change\" |\n>> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n>> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n>> +\ttest_cmp expected actual\n>> +'\n>> +\n>> +test_expect_success 'with a bodied message that lacks a trailing newline after the body' '\n>> +\tcat >expected <<-\\EOF &&\n>> +\t\tarea: change\n>> +\n>> +\t\tdetails about the change.\n>> +\n>> +\t\tReviewed-by: Peff\n>> +\t\tAcked-by: Johan\n>> +\tEOF\n>> +\tprintf \"area: change\\n\\ndetails about the change.\" |\n>> +\tgit interpret-trailers --trailer \"Reviewed-by: Peff\" \\\n>> +\t\t--trailer \"Acked-by: Johan\" >actual &&\n>> +\ttest_cmp expected actual\n>> +'\n>> +\n>> +test_expect_success 'with a message that lacks a trailing newline after the trailers' '\n>> +\tcat >expected <<-\\EOF &&\n>> +\t\tarea: change\n>> +\n>> +\t\tReviewed-by: Peff\n>> +\t\tAcked-by: Johan\n>> +\tEOF\n>> +\tprintf \"area: change\\n\\nReviewed-by: Peff\" |\n>> +\tgit interpret-trailers --trailer \"Acked-by: Johan\" >actual &&\n>> +\ttest_cmp expected actual\n>> +'\n>> +\n>>   test_expect_success 'with multiline title in the message' '\n>>   \tcat >expected <<-\\EOF &&\n>>   \t\tplace of\n>> --\n>> 2.45.2\n> \n"},{"id":"502461","messageId":"xmqqmskgu8dw.fsf@gitster.g","threadId":"62068","inReplyTo":"5f804f35-a69c-454e-a505-4ceeb9859387@gmail.com","subject":"Re: [PATCH v3] interpret-trailers: handle message without trailing newline","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-09T15:58:03Z","receivedAt":"2024-09-09T15:58:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> I assume it means invalid in the sense that the trailers are not\n> separated from the rest of the message by a blank line, not in the\n> sense that the resulting commit object is invalid.\n\nOK, then \"invalid message\" -> \"message with invalid trailer lines\",\nperhaps.\n\n>> But of course, various tools to manipulate the messages (e.g.\n>> \"commit --amend\" and your editor that gets invoked by it,\n>> \"interpret-trailers\") may not be prepared to see any arbitrary\n>> bytes.  I would have written\n>>      While a commit message can contain arbitrary byte sequence, the\n>>      fact that the user invoked the interpret-trailers command on it\n>>      means that the contents is expected to be a proper text, which\n>>      should not end in an incomplete line.  Instead of detecting and\n>>      erroring out upon seeing such a log message, complete the last\n>>      line if it lacks the terminating LF.\n>> or something like that, if I were working on this change.\n\nThanks.\n"}]}