{"thread":{"id":"64318","subject":"[PATCH v4 00/29] rebase: support --trailer","startedAt":"2025-10-14T12:25:24Z","lastAt":"2025-11-03T16:20:35Z","messageCount":43,"participants":["Li Chen","Kristoffer Haugsbakk","Phillip Wood"],"isPatch":true,"patchVersion":4,"patchTotal":29},"messages":[{"id":"528702","messageId":"20251014122452.1851103-1-me@linux.beauty","threadId":"64318","inReplyTo":null,"subject":"[PATCH v4 00/29] rebase: support --trailer","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:13Z","receivedAt":"2025-10-14T12:25:24Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nThis patch series teaches git rebase a new\n--trailer <text> option and, as a prerequisite, moves all trailer\nhandling out of the external interpret-trailers helper and into the\nbuiltin code path, as suggested by Phillip Wood.\n\nPatch 0 switches trailer.c to an in-memory implementation\n(amend_strbuf_with_trailers()). It removes every fork/exec.\n\nPatch 1~8 fix all reviewer comments from v3 for patch 0. \n\nPatch 9 builds on that helper to implement\ngit rebase --trailer. When the option is given we:\nforce the merge backend (apply/am backend lacks a message filter),\nautomatically enable --force-rebase so that fast-forwarded\ncommits are rewritten, and append the requested trailer(s) to every\nrewritten commit.\nState is stored in $state_dir/trailer so an interrupted rebase can\nresume safely. A dedicated test-suite (t3440) exercises plain,\nconflict, --root, invalid-input scenarios and etc.\n\nThe rest patches address all reviewer comments from v3 for patch 9. \n\nAll t/*.sh testcases have run successfully.\n\nv4: fix all reviewer comments in v3. [2]\nv3: merges the remaining trailer paths into one in-process helper, dropping the\n    duplicate code, as pointed by Junio and Phillip [1]\nv2: fix issues pointed by Phillip \nRFC link: https://lore.kernel.org/git/196a5ac1393.f5b4db7d187309.2451613571977217927@linux.beauty/\n\nComments welcome!\n\n[1]: https://lore.kernel.org/git/xmqq8qlzkukw.fsf@gitster.g/\n[2]: https://lore.kernel.org/git/20250803150059.402017-1-me@linux.beauty/\n\nLi Chen (29):\n  trailer: append trailers in-process and drop the fork to\n    `interpret-trailers`\n  trailer: restore interpret_trailers helper\n  trailer: drop --trailer prefix handling in amend helper\n  trailer: move config_head and arg_head to if storage\n  trailer: use bool for had_trailer_before\n  interpret-trailers: buffer stdout output\n  trailer: mirror interpret-trailers output flow\n  trailer: handle trailer append failures gently\n  rebase: support --trailer\n  rebase: inline trailer state paths\n  rebase: reuse buffer for trailer args\n  rebase: drop redundant strbuf_release call\n  rebase: skip stripping of --trailer option prefix\n  rebase: die on invalid trailer args\n  rebase: validate trailers with configured separators\n  sequencer: add trailers to message before writing file\n  tests: t3440: create expect files at point of use\n  tests: t3440: check apply backend error includes option\n  tests: t3440: use test_commit_message for trailer checks\n  tests: t3440: drop redundant resets and pass branch to rebase where\n    needed\n  tests: t3440: assert trailer on HEAD after conflict rebase\n  rebase: persist --trailer options across restarts\n  tests: t3440: remove redundant --keep-empty\n  tests: t3440: use helper for trailer checks\n  tests: t3440: test --trailer without values\n  tests: t3440: convert ex.com to example.com\n  tests: t3440: ensure trailers persist after rebase continue\n  tests: t3440: exercise trailer config mapping\n  sequencer: honor --trailer with fixup -C\n\n Documentation/git-rebase.adoc |   7 ++\n builtin/commit.c              |   2 +-\n builtin/interpret-trailers.c  |  94 +++++-------------------\n builtin/rebase.c              |  50 +++++++++++++\n builtin/tag.c                 |   3 +-\n sequencer.c                   |  34 +++++++++\n sequencer.h                   |   4 +-\n t/meson.build                 |   1 +\n t/t3440-rebase-trailer.sh     | 134 ++++++++++++++++++++++++++++++++++\n trailer.c                     | 130 ++++++++++++++++++++++++++++++---\n trailer.h                     |  23 +++++-\n wrapper.c                     |  16 ++++\n wrapper.h                     |   6 ++\n 13 files changed, 411 insertions(+), 93 deletions(-)\n create mode 100755 t/t3440-rebase-trailer.sh\n\n-- \n2.51.0\n\n"},{"id":"528703","messageId":"20251014122452.1851103-2-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 01/29] trailer: append trailers in-process and drop the fork to `interpret-trailers`","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:14Z","receivedAt":"2025-10-14T12:25:33Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nRoute all trailer insertion through trailer_process() and make\nbuiltin/interpret-trailers just do file I/O before calling into it.\namend_file_with_trailers() now shares the same code path.\n\nThis removes the fork/exec and tempfile juggling, cutting overhead and\nsimplifying error handling. No functional change is intended. It also\ncentralizes logic to prepare for follow-up rebase --trailer patch.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/interpret-trailers.c | 116 ++++++++------------------------\n trailer.c                    | 125 ++++++++++++++++++++++++++++++++---\n trailer.h                    |  18 ++++-\n 3 files changed, 157 insertions(+), 102 deletions(-)\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 41b0750e5a..be0fa83f79 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -10,7 +10,6 @@\n #include \"gettext.h\"\n #include \"parse-options.h\"\n #include \"string-list.h\"\n-#include \"tempfile.h\"\n #include \"trailer.h\"\n #include \"config.h\"\n \n@@ -93,37 +92,6 @@ static int parse_opt_parse(const struct option *opt, const char *arg,\n \treturn 0;\n }\n \n-static struct tempfile *trailers_tempfile;\n-\n-static FILE *create_in_place_tempfile(const char *file)\n-{\n-\tstruct stat st;\n-\tstruct strbuf filename_template = STRBUF_INIT;\n-\tconst char *tail;\n-\tFILE *outfile;\n-\n-\tif (stat(file, &st))\n-\t\tdie_errno(_(\"could not stat %s\"), file);\n-\tif (!S_ISREG(st.st_mode))\n-\t\tdie(_(\"file %s is not a regular file\"), file);\n-\tif (!(st.st_mode & S_IWUSR))\n-\t\tdie(_(\"file %s is not writable by user\"), file);\n-\n-\t/* Create temporary file in the same directory as the original */\n-\ttail = strrchr(file, '/');\n-\tif (tail)\n-\t\tstrbuf_add(&filename_template, file, tail - file + 1);\n-\tstrbuf_addstr(&filename_template, \"git-interpret-trailers-XXXXXX\");\n-\n-\ttrailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);\n-\tstrbuf_release(&filename_template);\n-\toutfile = fdopen_tempfile(trailers_tempfile, \"w\");\n-\tif (!outfile)\n-\t\tdie_errno(_(\"could not open temporary file\"));\n-\n-\treturn outfile;\n-}\n-\n static void read_input_file(struct strbuf *sb, const char *file)\n {\n \tif (file) {\n@@ -136,61 +104,6 @@ static void read_input_file(struct strbuf *sb, const char *file)\n \tstrbuf_complete_line(sb);\n }\n \n-static void interpret_trailers(const struct process_trailer_options *opts,\n-\t\t\t       struct list_head *new_trailer_head,\n-\t\t\t       const char *file)\n-{\n-\tLIST_HEAD(head);\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tstruct strbuf trailer_block_sb = STRBUF_INIT;\n-\tstruct trailer_block *trailer_block;\n-\tFILE *outfile = stdout;\n-\n-\ttrailer_config_init();\n-\n-\tread_input_file(&sb, file);\n-\n-\tif (opts->in_place)\n-\t\toutfile = create_in_place_tempfile(file);\n-\n-\ttrailer_block = parse_trailers(opts, sb.buf, &head);\n-\n-\t/* Print the lines before the trailer block */\n-\tif (!opts->only_trailers)\n-\t\tfwrite(sb.buf, 1, trailer_block_start(trailer_block), outfile);\n-\n-\tif (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block))\n-\t\tfprintf(outfile, \"\\n\");\n-\n-\n-\tif (!opts->only_input) {\n-\t\tLIST_HEAD(config_head);\n-\t\tLIST_HEAD(arg_head);\n-\t\tparse_trailers_from_config(&config_head);\n-\t\tparse_trailers_from_command_line_args(&arg_head, new_trailer_head);\n-\t\tlist_splice(&config_head, &arg_head);\n-\t\tprocess_trailers_lists(&head, &arg_head);\n-\t}\n-\n-\t/* Print trailer block. */\n-\tformat_trailers(opts, &head, &trailer_block_sb);\n-\tfree_trailers(&head);\n-\tfwrite(trailer_block_sb.buf, 1, trailer_block_sb.len, outfile);\n-\tstrbuf_release(&trailer_block_sb);\n-\n-\t/* Print the lines after the trailer block as is. */\n-\tif (!opts->only_trailers)\n-\t\tfwrite(sb.buf + trailer_block_end(trailer_block), 1,\n-\t\t       sb.len - trailer_block_end(trailer_block), outfile);\n-\ttrailer_block_release(trailer_block);\n-\n-\tif (opts->in_place)\n-\t\tif (rename_tempfile(&trailers_tempfile, file))\n-\t\t\tdie_errno(_(\"could not rename temporary file to %s\"), file);\n-\n-\tstrbuf_release(&sb);\n-}\n-\n int cmd_interpret_trailers(int argc,\n \t\t\t   const char **argv,\n \t\t\t   const char *prefix,\n@@ -232,14 +145,37 @@ int cmd_interpret_trailers(int argc,\n \t\t\tgit_interpret_trailers_usage,\n \t\t\toptions);\n \n+\ttrailer_config_init();\n+\n \tif (argc) {\n \t\tint i;\n-\t\tfor (i = 0; i < argc; i++)\n-\t\t\tinterpret_trailers(&opts, &trailers, argv[i]);\n+\t\tfor (i = 0; i < argc; i++) {\n+\t\t\tstruct strbuf in_buf = STRBUF_INIT;\n+\t\t\tstruct strbuf out_buf = STRBUF_INIT;\n+\n+\t\t\tread_input_file(&in_buf, argv[i]);\n+\t\t\tif (trailer_process(&opts, in_buf.buf, &trailers, &out_buf) < 0)\n+\t\t\t\tdie(_(\"failed to process trailers for %s\"), argv[i]);\n+\t\t\tif (opts.in_place)\n+\t\t\t\twrite_file_buf(argv[i], out_buf.buf, out_buf.len);\n+\t\t\telse\n+\t\t\t\tfwrite(out_buf.buf, 1, out_buf.len, stdout);\n+\t\t\tstrbuf_release(&in_buf);\n+\t\t\tstrbuf_release(&out_buf);\n+\t\t}\n \t} else {\n+\t\tstruct strbuf in_buf = STRBUF_INIT;\n+\t\tstruct strbuf out_buf = STRBUF_INIT;\n+\n \t\tif (opts.in_place)\n \t\t\tdie(_(\"no input file given for in-place editing\"));\n-\t\tinterpret_trailers(&opts, &trailers, NULL);\n+\n+\t\tread_input_file(&in_buf, NULL);\n+\t\tif (trailer_process(&opts, in_buf.buf, &trailers, &out_buf) < 0)\n+\t\t\tdie(_(\"failed to process trailers\"));\n+\t\tfwrite(out_buf.buf, 1, out_buf.len, stdout);\n+\t\tstrbuf_release(&in_buf);\n+\t\tstrbuf_release(&out_buf);\n \t}\n \n \tnew_trailers_clear(&trailers);\ndiff --git a/trailer.c b/trailer.c\nindex 911a81ed99..8aec466b5f 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1224,14 +1224,121 @@ void trailer_iterator_release(struct trailer_iterator *iter)\n \tstrbuf_release(&iter->key);\n }\n \n-int amend_file_with_trailers(const char *path, const struct strvec *trailer_args)\n+static int amend_strbuf_with_trailers(struct strbuf *buf,\n+\t\t\t\t\t\t\t\t\t  const struct strvec *trailer_args)\n {\n-\tstruct child_process run_trailer = CHILD_PROCESS_INIT;\n-\n-\trun_trailer.git_cmd = 1;\n-\tstrvec_pushl(&run_trailer.args, \"interpret-trailers\",\n-\t\t     \"--in-place\", \"--no-divider\",\n-\t\t     path, NULL);\n-\tstrvec_pushv(&run_trailer.args, trailer_args->v);\n-\treturn run_command(&run_trailer);\n+\tstruct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;\n+\tLIST_HEAD(new_trailer_head);\n+\tstruct strbuf out = STRBUF_INIT;\n+\tsize_t i;\n+\n+\topts.no_divider = 1;\n+\n+\tfor (i = 0; i < trailer_args->nr; i++) {\n+\t\tconst char *arg = trailer_args->v[i];\n+\t\tconst char *text;\n+\t\tstruct new_trailer_item *item;\n+\n+\t\tif (!skip_prefix(arg, \"--trailer=\", &text))\n+\t\t\ttext = arg;\n+\t\tif (!*text)\n+\t\t\tcontinue;\n+\t\titem = xcalloc(1, sizeof(*item));\n+\t\tINIT_LIST_HEAD(&item->list);\n+\t\titem->text = text;\n+\t\tlist_add_tail(&item->list, &new_trailer_head);\n+\t}\n+\tif (trailer_process(&opts, buf->buf, &new_trailer_head, &out) < 0)\n+\t\treturn -1;\n+\tstrbuf_swap(buf, &out);\n+\tstrbuf_release(&out);\n+\twhile (!list_empty(&new_trailer_head)) {\n+\t\tstruct new_trailer_item *item =\n+\t\t\tlist_first_entry(&new_trailer_head, struct new_trailer_item, list);\n+\t\tlist_del(&item->list);\n+\t\tfree(item);\n+\t}\n+\treturn 0;\n+}\n+\n+int trailer_process(const struct process_trailer_options *opts,\n+\t\t\t\t\tconst char *msg,\n+\t\t\t\t\tstruct list_head *new_trailer_head,\n+\t\t\t\t\tstruct strbuf *out)\n+{\n+\tstruct trailer_block *blk;\n+\tLIST_HEAD(orig_head);\n+\tLIST_HEAD(config_head);\n+\tLIST_HEAD(arg_head);\n+\tstruct strbuf trailers_sb = STRBUF_INIT;\n+\tint had_trailer_before;\n+\n+\tblk = parse_trailers(opts, msg, &orig_head);\n+\thad_trailer_before = !list_empty(&orig_head);\n+\tif (!opts->only_input) {\n+\t\tparse_trailers_from_config(&config_head);\n+\t\tparse_trailers_from_command_line_args(&arg_head, new_trailer_head);\n+\t\tlist_splice(&config_head, &arg_head);\n+\t\tprocess_trailers_lists(&orig_head, &arg_head);\n+\t}\n+\tformat_trailers(opts, &orig_head, &trailers_sb);\n+\tif (!opts->only_trailers && !opts->only_input && !opts->unfold &&\n+\t    !opts->trim_empty && list_empty(&orig_head) &&\n+\t    (list_empty(new_trailer_head) || opts->only_input)) {\n+\t\tsize_t split = trailer_block_start(blk); /* end-of-log-msg */\n+\t\tif (!blank_line_before_trailer_block(blk)) {\n+\t\t\tstrbuf_add(out, msg, split);\n+\t\t\tstrbuf_addch(out, '\\n');\n+\t\t\tstrbuf_addstr(out, msg + split);\n+\t\t} else\n+\t\t\tstrbuf_addstr(out, msg);\n+\n+\t\tstrbuf_release(&trailers_sb);\n+\t\ttrailer_block_release(blk);\n+\t\treturn 0;\n+\t}\n+\tif (opts->only_trailers) {\n+\t\tstrbuf_addbuf(out, &trailers_sb);\n+\t} else if (had_trailer_before) {\n+\t\tstrbuf_add(out, msg, trailer_block_start(blk));\n+\t\tif (!blank_line_before_trailer_block(blk))\n+\t\t\tstrbuf_addch(out, '\\n');\n+\t\tstrbuf_addbuf(out, &trailers_sb);\n+\t\tstrbuf_add(out, msg + trailer_block_end(blk),\n+\t\t\t   strlen(msg) - trailer_block_end(blk));\n+\t} else {\n+\t\tsize_t cpos = trailer_block_start(blk);\n+\t\tstrbuf_add(out, msg, cpos);\n+\t\tif (cpos == 0) /* empty body → just one \\n */\n+\t\t\tstrbuf_addch(out, '\\n');\n+\t\telse if (!blank_line_before_trailer_block(blk))\n+\t\t\tstrbuf_addch(out, '\\n'); /* body without trailing blank */\n+\n+\t\tstrbuf_addbuf(out, &trailers_sb);\n+\t\tstrbuf_add(out, msg + cpos, strlen(msg) - cpos);\n+\t}\n+\tstrbuf_release(&trailers_sb);\n+\tfree_trailers(&orig_head);\n+\ttrailer_block_release(blk);\n+\treturn 0;\n+}\n+\n+int amend_file_with_trailers(const char *path,\n+\t\t\t\t\t\t\t const struct strvec *trailer_args)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tif (!trailer_args || !trailer_args->nr)\n+\t\treturn 0;\n+\n+\tif (strbuf_read_file(&buf, path, 0) < 0)\n+\t\treturn error_errno(\"could not read '%s'\", path);\n+\n+\tif (amend_strbuf_with_trailers(&buf, trailer_args))\n+\t\tdie(\"failed to append trailers\");\n+\n+\t/* `write_file_buf()` aborts on error internally */\n+\twrite_file_buf(path, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\treturn 0;\n }\ndiff --git a/trailer.h b/trailer.h\nindex 4740549586..01f711fb13 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -196,10 +196,22 @@ int trailer_iterator_advance(struct trailer_iterator *iter);\n void trailer_iterator_release(struct trailer_iterator *iter);\n \n /*\n- * Augment a file to add trailers to it by running git-interpret-trailers.\n- * This calls run_command() and its return value is the same (i.e. 0 for\n- * success, various non-zero for other errors). See run-command.h.\n+ * Augment a file to add trailers to it (similar to 'git interpret-trailers').\n+ * Returns 0 on success or a non-zero error code on failure.\n  */\n int amend_file_with_trailers(const char *path, const struct strvec *trailer_args);\n \n+/*\n+ * Process trailer lines for a commit message in-memory.\n+ * @opts: trailer processing options (e.g. from parse-options)\n+ * @msg: the input message string\n+ * @new_trailer_head: list of new trailers to add (struct new_trailer_item)\n+ * @out: strbuf to store the resulting message (must be initialized)\n+ *\n+ * Returns 0 on success, <0 on error.\n+ */\n+int trailer_process(const struct process_trailer_options *opts,\n+\t\t\tconst char *msg,\n+\t\t\tstruct list_head *new_trailer_head,\n+\t\t\tstruct strbuf *out);\n #endif /* TRAILER_H */\n-- \n2.51.0\n\n"},{"id":"528704","messageId":"20251014122452.1851103-3-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 02/29] trailer: restore interpret_trailers helper","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:15Z","receivedAt":"2025-10-14T12:25:42Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"The change factors the duplicated\nread/process/write logic into a single\ninterpret_trailers() helper(which was removed\nin previous commit) while preserving the\noriginal error handling and output paths for\nboth in-place edits and stdout output.\n\ncmd_interpret_trailers() now reuses the helper\nfor each filename and for the stdin path,\nkeeping the option parsing and safety checks intact.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/interpret-trailers.c | 50 +++++++++++++++++++-----------------\n 1 file changed, 27 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex be0fa83f79..2c8b6fc3b9 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -104,6 +104,30 @@ static void read_input_file(struct strbuf *sb, const char *file)\n \tstrbuf_complete_line(sb);\n }\n \n+static void interpret_trailers(const struct process_trailer_options *opts,\n+\t\t\t       struct list_head *new_trailer_head,\n+\t\t\t       const char *file)\n+{\n+\tstruct strbuf in_buf = STRBUF_INIT;\n+\tstruct strbuf out_buf = STRBUF_INIT;\n+\n+\tread_input_file(&in_buf, file);\n+\tif (trailer_process(opts, in_buf.buf, new_trailer_head, &out_buf) < 0) {\n+\t\tif (file)\n+\t\t\tdie(_(\"failed to process trailers for %s\"), file);\n+\t\telse\n+\t\t\tdie(_(\"failed to process trailers\"));\n+\t}\n+\n+\tif (opts->in_place)\n+\t\twrite_file_buf(file, out_buf.buf, out_buf.len);\n+\telse\n+\t\tfwrite(out_buf.buf, 1, out_buf.len, stdout);\n+\n+\tstrbuf_release(&in_buf);\n+\tstrbuf_release(&out_buf);\n+}\n+\n int cmd_interpret_trailers(int argc,\n \t\t\t   const char **argv,\n \t\t\t   const char *prefix,\n@@ -149,33 +173,13 @@ int cmd_interpret_trailers(int argc,\n \n \tif (argc) {\n \t\tint i;\n-\t\tfor (i = 0; i < argc; i++) {\n-\t\t\tstruct strbuf in_buf = STRBUF_INIT;\n-\t\t\tstruct strbuf out_buf = STRBUF_INIT;\n-\n-\t\t\tread_input_file(&in_buf, argv[i]);\n-\t\t\tif (trailer_process(&opts, in_buf.buf, &trailers, &out_buf) < 0)\n-\t\t\t\tdie(_(\"failed to process trailers for %s\"), argv[i]);\n-\t\t\tif (opts.in_place)\n-\t\t\t\twrite_file_buf(argv[i], out_buf.buf, out_buf.len);\n-\t\t\telse\n-\t\t\t\tfwrite(out_buf.buf, 1, out_buf.len, stdout);\n-\t\t\tstrbuf_release(&in_buf);\n-\t\t\tstrbuf_release(&out_buf);\n-\t\t}\n+\t\tfor (i = 0; i < argc; i++)\n+\t\t\tinterpret_trailers(&opts, &trailers, argv[i]);\n \t} else {\n-\t\tstruct strbuf in_buf = STRBUF_INIT;\n-\t\tstruct strbuf out_buf = STRBUF_INIT;\n-\n \t\tif (opts.in_place)\n \t\t\tdie(_(\"no input file given for in-place editing\"));\n \n-\t\tread_input_file(&in_buf, NULL);\n-\t\tif (trailer_process(&opts, in_buf.buf, &trailers, &out_buf) < 0)\n-\t\t\tdie(_(\"failed to process trailers\"));\n-\t\tfwrite(out_buf.buf, 1, out_buf.len, stdout);\n-\t\tstrbuf_release(&in_buf);\n-\t\tstrbuf_release(&out_buf);\n+\t\tinterpret_trailers(&opts, &trailers, NULL);\n \t}\n \n \tnew_trailers_clear(&trailers);\n-- \n2.51.0\n\n"},{"id":"528705","messageId":"20251014122452.1851103-4-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 03/29] trailer: drop --trailer prefix handling in amend helper","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:16Z","receivedAt":"2025-10-14T12:25:50Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Make callers pass plain trailer text instead of recreating\nthe option prefix before invoking interpret-trailers.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/commit.c | 2 +-\n builtin/tag.c    | 3 +--\n trailer.c        | 5 +----\n 3 files changed, 3 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0243f17d53..67070d6a54 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1719,7 +1719,7 @@ int cmd_commit(int argc,\n \t\tOPT_STRING(0, \"fixup\", &fixup_message, N_(\"[(amend|reword):]commit\"), N_(\"use autosquash formatted message to fixup or amend/reword specified commit\")),\n \t\tOPT_STRING(0, \"squash\", &squash_message, N_(\"commit\"), N_(\"use autosquash formatted message to squash specified commit\")),\n \t\tOPT_BOOL(0, \"reset-author\", &renew_authorship, N_(\"the commit is authored by me now (used with -C/-c/--amend)\")),\n-\t\tOPT_PASSTHRU_ARGV(0, \"trailer\", &trailer_args, N_(\"trailer\"), N_(\"add custom trailer(s)\"), PARSE_OPT_NONEG),\n+\t\tOPT_CALLBACK_F(0, \"trailer\", &trailer_args, N_(\"trailer\"), N_(\"add custom trailer(s)\"), PARSE_OPT_NONEG, parse_opt_strvec),\n \t\tOPT_BOOL('s', \"signoff\", &signoff, N_(\"add a Signed-off-by trailer\")),\n \t\tOPT_FILENAME('t', \"template\", &template_file, N_(\"use specified template file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of commit\")),\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex f0665af3ac..65c4a0b36b 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -499,8 +499,7 @@ int cmd_tag(int argc,\n \t\tOPT_CALLBACK_F('m', \"message\", &msg, N_(\"message\"),\n \t\t\t       N_(\"tag message\"), PARSE_OPT_NONEG, parse_msg_arg),\n \t\tOPT_FILENAME('F', \"file\", &msgfile, N_(\"read message from file\")),\n-\t\tOPT_PASSTHRU_ARGV(0, \"trailer\", &trailer_args, N_(\"trailer\"),\n-\t\t\t\t  N_(\"add custom trailer(s)\"), PARSE_OPT_NONEG),\n+\t\tOPT_CALLBACK_F(0, \"trailer\", &trailer_args, N_(\"trailer\"), N_(\"add custom trailer(s)\"), PARSE_OPT_NONEG, parse_opt_strvec),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of tag message\")),\n \t\tOPT_BOOL('s', \"sign\", &opt.sign, N_(\"annotated and GPG-signed tag\")),\n \t\tOPT_CLEANUP(&cleanup_arg),\ndiff --git a/trailer.c b/trailer.c\nindex 8aec466b5f..42ac6f58a2 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1235,12 +1235,9 @@ static int amend_strbuf_with_trailers(struct strbuf *buf,\n \topts.no_divider = 1;\n \n \tfor (i = 0; i < trailer_args->nr; i++) {\n-\t\tconst char *arg = trailer_args->v[i];\n-\t\tconst char *text;\n+\t\tconst char *text = trailer_args->v[i];\n \t\tstruct new_trailer_item *item;\n \n-\t\tif (!skip_prefix(arg, \"--trailer=\", &text))\n-\t\t\ttext = arg;\n \t\tif (!*text)\n \t\t\tcontinue;\n \t\titem = xcalloc(1, sizeof(*item));\n-- \n2.51.0\n\n"},{"id":"528706","messageId":"20251014122452.1851103-5-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 04/29] trailer: move config_head and arg_head to if storage","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:17Z","receivedAt":"2025-10-14T12:25:59Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nMove LIST_HEAD(config_head) and LIST_HEAD(arg_head) into the\nnon-only_input branch so they are created only when needed.\nNo functional change.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n trailer.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/trailer.c b/trailer.c\nindex 42ac6f58a2..3169e315c0 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1265,14 +1265,15 @@ int trailer_process(const struct process_trailer_options *opts,\n {\n \tstruct trailer_block *blk;\n \tLIST_HEAD(orig_head);\n-\tLIST_HEAD(config_head);\n-\tLIST_HEAD(arg_head);\n \tstruct strbuf trailers_sb = STRBUF_INIT;\n \tint had_trailer_before;\n \n \tblk = parse_trailers(opts, msg, &orig_head);\n \thad_trailer_before = !list_empty(&orig_head);\n \tif (!opts->only_input) {\n+\t\tLIST_HEAD(config_head);\n+\t\tLIST_HEAD(arg_head);\n+\n \t\tparse_trailers_from_config(&config_head);\n \t\tparse_trailers_from_command_line_args(&arg_head, new_trailer_head);\n \t\tlist_splice(&config_head, &arg_head);\n-- \n2.51.0\n\n"},{"id":"528707","messageId":"20251014122452.1851103-6-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 05/29] trailer: use bool for had_trailer_before","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:18Z","receivedAt":"2025-10-14T12:26:07Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nSwitch had_trailer_before from int to bool\nto match its logical use. No functional change.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n trailer.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/trailer.c b/trailer.c\nindex 3169e315c0..ac756020a3 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1266,7 +1266,7 @@ int trailer_process(const struct process_trailer_options *opts,\n \tstruct trailer_block *blk;\n \tLIST_HEAD(orig_head);\n \tstruct strbuf trailers_sb = STRBUF_INIT;\n-\tint had_trailer_before;\n+\tbool had_trailer_before;\n \n \tblk = parse_trailers(opts, msg, &orig_head);\n \thad_trailer_before = !list_empty(&orig_head);\n-- \n2.51.0\n\n"},{"id":"528708","messageId":"20251014122452.1851103-7-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 06/29] interpret-trailers: buffer stdout output","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:19Z","receivedAt":"2025-10-14T12:26:16Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Simplified the non-in-place path in interpret_trailers\nby writing the existing output strbuf directly to\nstdout without creating a redundant temporary buffer.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/interpret-trailers.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 2c8b6fc3b9..cdf39dbca8 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -122,7 +122,7 @@ static void interpret_trailers(const struct process_trailer_options *opts,\n \tif (opts->in_place)\n \t\twrite_file_buf(file, out_buf.buf, out_buf.len);\n \telse\n-\t\tfwrite(out_buf.buf, 1, out_buf.len, stdout);\n+\t\tstrbuf_write(&out_buf, stdout);\n \n \tstrbuf_release(&in_buf);\n \tstrbuf_release(&out_buf);\n-- \n2.51.0\n\n"},{"id":"528709","messageId":"20251014122452.1851103-8-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 07/29] trailer: mirror interpret-trailers output flow","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:20Z","receivedAt":"2025-10-14T12:26:25Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Drop the early-return branch that mirrored the\nspecial case. Let trailer_process() always\nfollow the same path as interpret-trailers.\nEnsure trailer lists and buffers are freed\nalong the unified exit path.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n trailer.c | 38 +++++++-------------------------------\n 1 file changed, 7 insertions(+), 31 deletions(-)\n\ndiff --git a/trailer.c b/trailer.c\nindex ac756020a3..5329589064 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1266,10 +1266,8 @@ int trailer_process(const struct process_trailer_options *opts,\n \tstruct trailer_block *blk;\n \tLIST_HEAD(orig_head);\n \tstruct strbuf trailers_sb = STRBUF_INIT;\n-\tbool had_trailer_before;\n \n \tblk = parse_trailers(opts, msg, &orig_head);\n-\thad_trailer_before = !list_empty(&orig_head);\n \tif (!opts->only_input) {\n \t\tLIST_HEAD(config_head);\n \t\tLIST_HEAD(arg_head);\n@@ -1280,40 +1278,18 @@ int trailer_process(const struct process_trailer_options *opts,\n \t\tprocess_trailers_lists(&orig_head, &arg_head);\n \t}\n \tformat_trailers(opts, &orig_head, &trailers_sb);\n-\tif (!opts->only_trailers && !opts->only_input && !opts->unfold &&\n-\t    !opts->trim_empty && list_empty(&orig_head) &&\n-\t    (list_empty(new_trailer_head) || opts->only_input)) {\n-\t\tsize_t split = trailer_block_start(blk); /* end-of-log-msg */\n-\t\tif (!blank_line_before_trailer_block(blk)) {\n-\t\t\tstrbuf_add(out, msg, split);\n-\t\t\tstrbuf_addch(out, '\\n');\n-\t\t\tstrbuf_addstr(out, msg + split);\n-\t\t} else\n-\t\t\tstrbuf_addstr(out, msg);\n-\n-\t\tstrbuf_release(&trailers_sb);\n-\t\ttrailer_block_release(blk);\n-\t\treturn 0;\n-\t}\n \tif (opts->only_trailers) {\n \t\tstrbuf_addbuf(out, &trailers_sb);\n-\t} else if (had_trailer_before) {\n-\t\tstrbuf_add(out, msg, trailer_block_start(blk));\n-\t\tif (!blank_line_before_trailer_block(blk))\n-\t\t\tstrbuf_addch(out, '\\n');\n-\t\tstrbuf_addbuf(out, &trailers_sb);\n-\t\tstrbuf_add(out, msg + trailer_block_end(blk),\n-\t\t\t   strlen(msg) - trailer_block_end(blk));\n \t} else {\n-\t\tsize_t cpos = trailer_block_start(blk);\n-\t\tstrbuf_add(out, msg, cpos);\n-\t\tif (cpos == 0) /* empty body → just one \\n */\n-\t\t\tstrbuf_addch(out, '\\n');\n-\t\telse if (!blank_line_before_trailer_block(blk))\n-\t\t\tstrbuf_addch(out, '\\n'); /* body without trailing blank */\n+\t\tsize_t block_start = trailer_block_start(blk);\n+\t\tsize_t block_end = trailer_block_end(blk);\n+\t\tbool need_blank_line = !blank_line_before_trailer_block(blk);\n \n+\t\tstrbuf_add(out, msg, block_start);\n+\t\tif (need_blank_line)\n+\t\t\tstrbuf_addch(out, '\\n');\n \t\tstrbuf_addbuf(out, &trailers_sb);\n-\t\tstrbuf_add(out, msg + cpos, strlen(msg) - cpos);\n+\t\tstrbuf_add(out, msg + block_end, strlen(msg) - block_end);\n \t}\n \tstrbuf_release(&trailers_sb);\n \tfree_trailers(&orig_head);\n-- \n2.51.0\n\n"},{"id":"528710","messageId":"20251014122452.1851103-9-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 08/29] trailer: handle trailer append failures gently","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:21Z","receivedAt":"2025-10-14T12:26:33Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Added write_file_buf_gently so callers can rewrite\nfiles while surfacing errors instead of aborting.\n\nUpdated amend_file_with_trailers to release buffers\nand propagate trailer and write failures back to the caller,\nbecause amend_file_with_trailers shuold not die.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n trailer.c | 14 ++++++++++----\n wrapper.c | 16 ++++++++++++++++\n wrapper.h |  6 ++++++\n 3 files changed, 32 insertions(+), 4 deletions(-)\n\ndiff --git a/trailer.c b/trailer.c\nindex 5329589064..b0ad7dc5c3 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -9,6 +9,7 @@\n #include \"commit.h\"\n #include \"trailer.h\"\n #include \"list.h\"\n+#include \"wrapper.h\"\n /*\n  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>\n  */\n@@ -1308,11 +1309,16 @@ int amend_file_with_trailers(const char *path,\n \tif (strbuf_read_file(&buf, path, 0) < 0)\n \t\treturn error_errno(\"could not read '%s'\", path);\n \n-\tif (amend_strbuf_with_trailers(&buf, trailer_args))\n-\t\tdie(\"failed to append trailers\");\n+\tif (amend_strbuf_with_trailers(&buf, trailer_args)) {\n+\t\tstrbuf_release(&buf);\n+\t\treturn error(\"failed to append trailers\");\n+\t}\n+\n+\tif (write_file_buf_gently(path, buf.buf, buf.len)) {\n+\t\tstrbuf_release(&buf);\n+\t\treturn -1;\n+\t}\n \n-\t/* `write_file_buf()` aborts on error internally */\n-\twrite_file_buf(path, buf.buf, buf.len);\n \tstrbuf_release(&buf);\n \treturn 0;\n }\ndiff --git a/wrapper.c b/wrapper.c\nindex 2f00d2ac87..2aeba8b049 100644\n--- a/wrapper.c\n+++ b/wrapper.c\n@@ -688,6 +688,22 @@ void write_file_buf(const char *path, const char *buf, size_t len)\n \t\tdie_errno(_(\"could not close '%s'\"), path);\n }\n \n+int write_file_buf_gently(const char *path, const char *buf, size_t len)\n+{\n+\tint fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);\n+\n+\tif (fd < 0)\n+\t\treturn error_errno(_(\"could not open '%s'\"), path);\n+\tif (write_in_full(fd, buf, len) < 0) {\n+\t\tint ret = error_errno(_(\"could not write to '%s'\"), path);\n+\t\tclose(fd);\n+\t\treturn ret;\n+\t}\n+\tif (close(fd))\n+\t\treturn error_errno(_(\"could not close '%s'\"), path);\n+\treturn 0;\n+}\n+\n void write_file(const char *path, const char *fmt, ...)\n {\n \tva_list params;\ndiff --git a/wrapper.h b/wrapper.h\nindex 7df824e34a..5b7d7a78fb 100644\n--- a/wrapper.h\n+++ b/wrapper.h\n@@ -56,6 +56,12 @@ static inline ssize_t write_str_in_full(int fd, const char *str)\n  */\n void write_file_buf(const char *path, const char *buf, size_t len);\n \n+/**\n+ * Like write_file_buf(), but report errors instead of exiting. Returns 0 on\n+ * success or a negative value on error after emitting a message.\n+ */\n+int write_file_buf_gently(const char *path, const char *buf, size_t len);\n+\n /**\n  * Like write_file_buf(), but format the contents into a buffer first.\n  * Additionally, write_file() will append a newline if one is not already\n-- \n2.51.0\n\n"},{"id":"528711","messageId":"20251014122452.1851103-10-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 09/29] rebase: support --trailer","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:22Z","receivedAt":"2025-10-14T12:26:41Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nImplement a new `--trailer <text>` option for `git rebase`\n(support merge backend only now), which appends arbitrary\ntrailer lines to each rebased commit message.\n\nReject it if the user passes an option that requires the\napply backend (git am) since it lacks message‑filter/trailer\nhook. otherwise we can just use the merge backend.\n\nAutomatically set REBASE_FORCE when any trailer is supplied.\n\nAnd reject invalid input before user edit the interactive file.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n Documentation/git-rebase.adoc |  7 +++\n builtin/rebase.c              | 89 +++++++++++++++++++++++++++++++++\n sequencer.c                   | 13 +++++\n sequencer.h                   |  4 +-\n t/meson.build                 |  1 +\n t/t3440-rebase-trailer.sh     | 94 +++++++++++++++++++++++++++++++++++\n 6 files changed, 207 insertions(+), 1 deletion(-)\n create mode 100755 t/t3440-rebase-trailer.sh\n\ndiff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc\nindex 005caf6164..b2003b70d7 100644\n--- a/Documentation/git-rebase.adoc\n+++ b/Documentation/git-rebase.adoc\n@@ -488,6 +488,13 @@ See also INCOMPATIBLE OPTIONS below.\n \tthat if `--interactive` is given then only commits marked to be\n \tpicked, edited or reworded will have the trailer added.\n +\n+--trailer <trailer>::\n+       Append the given trailer line(s) to every rebased commit\n+       message, processed via linkgit:git-interpret-trailers[1].\n+       When this option is present *rebase automatically implies*\n+       `--force-rebase` so that fast‑forwarded commits are also\n+       rewritten.\n+\n See also INCOMPATIBLE OPTIONS below.\n \n -i::\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex c468828189..3b001c0757 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -36,6 +36,7 @@\n #include \"reset.h\"\n #include \"trace2.h\"\n #include \"hook.h\"\n+#include \"trailer.h\"\n \n static char const * const builtin_rebase_usage[] = {\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n@@ -113,6 +114,7 @@ struct rebase_options {\n \tenum action action;\n \tchar *reflog_action;\n \tint signoff;\n+\tstruct strvec trailer_args;\n \tint allow_rerere_autoupdate;\n \tint keep_empty;\n \tint autosquash;\n@@ -143,6 +145,7 @@ struct rebase_options {\n \t\t.flags = REBASE_NO_QUIET, \t\t\\\n \t\t.git_am_opts = STRVEC_INIT,\t\t\\\n \t\t.exec = STRING_LIST_INIT_NODUP,\t\t\\\n+\t\t.trailer_args = STRVEC_INIT,  \\\n \t\t.git_format_patch_opt = STRBUF_INIT,\t\\\n \t\t.fork_point = -1,\t\t\t\\\n \t\t.reapply_cherry_picks = -1,             \\\n@@ -166,6 +169,7 @@ static void rebase_options_release(struct rebase_options *opts)\n \tfree(opts->strategy);\n \tstring_list_clear(&opts->strategy_opts, 0);\n \tstrbuf_release(&opts->git_format_patch_opt);\n+\tstrvec_clear(&opts->trailer_args);\n }\n \n static struct replay_opts get_replay_opts(const struct rebase_options *opts)\n@@ -177,6 +181,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)\n \tsequencer_init_config(&replay);\n \n \treplay.signoff = opts->signoff;\n+\n+\tfor (size_t i = 0; i < opts->trailer_args.nr; i++)\n+\t\tstrvec_push(&replay.trailer_args, opts->trailer_args.v[i]);\n+\n \treplay.allow_ff = !(opts->flags & REBASE_FORCE);\n \tif (opts->allow_rerere_autoupdate)\n \t\treplay.allow_rerere_auto = opts->allow_rerere_autoupdate;\n@@ -434,6 +442,8 @@ static int read_basic_state(struct rebase_options *opts)\n \tstruct strbuf head_name = STRBUF_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id oid;\n+\tconst char trailer_state_name[] = \"trailer\";\n+\tconst char *path = state_dir_path(trailer_state_name, opts);\n \n \tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n \t\t\t   READ_ONELINER_WARN_MISSING) ||\n@@ -502,11 +512,31 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tstrbuf_release(&buf);\n \n+\tif (strbuf_read_file(&buf, path, 0) >= 0) {\n+\t\tconst char *p = buf.buf, *end = buf.buf + buf.len;\n+\n+\t\twhile (p < end) {\n+\t\t\tchar *nl = memchr(p, '\\n', end - p);\n+\t\t\tif (!nl)\n+\t\t\t\tdie(\"nl shouldn't be NULL\");\n+\t\t\t*nl = '\\0';\n+\n+\t\t\tif (*p)\n+\t\t\t\tstrvec_push(&opts->trailer_args, p);\n+\n+\t\t\tp = nl + 1;\n+\t\t}\n+\t\tstrbuf_release(&buf);\n+\t}\n+\tstrbuf_release(&buf);\n+\n \treturn 0;\n }\n \n static int rebase_write_basic_state(struct rebase_options *opts)\n {\n+\tconst char trailer_state_name[] = \"trailer\";\n+\n \twrite_file(state_dir_path(\"head-name\", opts), \"%s\",\n \t\t   opts->head_name ? opts->head_name : \"detached HEAD\");\n \twrite_file(state_dir_path(\"onto\", opts), \"%s\",\n@@ -528,6 +558,21 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n \tif (opts->signoff)\n \t\twrite_file(state_dir_path(\"signoff\", opts), \"--signoff\");\n \n+\t/*\n+\t * save opts->trailer_args into state_dir/trailer\n+\t */\n+\tif (opts->trailer_args.nr) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\n+\t\tfor (size_t i = 0; i < opts->trailer_args.nr; i++) {\n+\t\t\t\tstrbuf_addstr(&buf, opts->trailer_args.v[i]);\n+\t\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t}\n+\t\twrite_file(state_dir_path(trailer_state_name, opts),\n+\t\t\t\t   \"%s\", buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t}\n+\n \treturn 0;\n }\n \n@@ -1084,6 +1129,35 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n+static int validate_trailer_args_after_config(const struct strvec *cli_args,\n+\t\t\t\t       struct strbuf *err)\n+{\n+\tfor (size_t i = 0; i < cli_args->nr; i++) {\n+\t\tconst char *raw = cli_args->v[i];\n+\t\tconst char *txt; // Key[:=]Val\n+\t\tconst char *sep;\n+\n+\t\tif (!skip_prefix(raw, \"--trailer=\", &txt))\n+\t\t\ttxt = raw;\n+\n+\t\tif (!*txt) {\n+\t\t\tstrbuf_addstr(err, _(\"empty --trailer argument\"));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tsep = strpbrk(txt, \":=\");\n+\n+\t\t/* there must be key bfore seperator */\n+\t\tif (sep && sep == txt) {\n+\t\t\tstrbuf_addf(err,\n+\t\t\t\t    _(\"invalid trailer '%s': missing key before separator\"),\n+\t\t\t\t    txt);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n int cmd_rebase(int argc,\n \t       const char **argv,\n \t       const char *prefix,\n@@ -1132,6 +1206,8 @@ int cmd_rebase(int argc,\n \t\t\t.flags = PARSE_OPT_NOARG,\n \t\t\t.defval = REBASE_DIFFSTAT,\n \t\t},\n+\t\tOPT_STRVEC(0, \"trailer\", &options.trailer_args, N_(\"trailer\"),\n+\t\t\t\tN_(\"add custom trailer(s)\")),\n \t\tOPT_BOOL(0, \"signoff\", &options.signoff,\n \t\t\t N_(\"add a Signed-off-by trailer to each commit\")),\n \t\tOPT_BOOL(0, \"committer-date-is-author-date\",\n@@ -1285,6 +1361,16 @@ int cmd_rebase(int argc,\n \t\t\t     builtin_rebase_options,\n \t\t\t     builtin_rebase_usage, 0);\n \n+\tif (options.trailer_args.nr) {\n+\t\tstruct strbuf err = STRBUF_INIT;\n+\n+\t\tif (validate_trailer_args_after_config(&options.trailer_args, &err))\n+\t\t\tdie(\"%s\", err.buf);\n+\n+\t\toptions.flags |= REBASE_FORCE;\n+\t\tstrbuf_release(&err);\n+\t}\n+\n \tif (preserve_merges_selected)\n \t\tdie(_(\"--preserve-merges was replaced by --rebase-merges\\n\"\n \t\t\t\"Note: Your `pull.rebase` configuration may also be set to 'preserve',\\n\"\n@@ -1542,6 +1628,9 @@ int cmd_rebase(int argc,\n \tif (options.root && !options.onto_name)\n \t\timply_merge(&options, \"--root without --onto\");\n \n+\tif (options.trailer_args.nr)\n+\t\timply_merge(&options, \"--trailer\");\n+\n \tif (isatty(2) && options.flags & REBASE_NO_QUIET)\n \t\tstrbuf_addstr(&options.git_format_patch_opt, \" --progress\");\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 5476d39ba9..5103ae786c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -420,6 +420,7 @@ void replay_opts_release(struct replay_opts *opts)\n \tif (opts->revs)\n \t\trelease_revisions(opts->revs);\n \tfree(opts->revs);\n+\tstrvec_clear(&opts->trailer_args);\n \treplay_ctx_release(ctx);\n \tfree(opts->ctx);\n }\n@@ -2517,6 +2518,18 @@ static int do_pick_commit(struct repository *r,\n \t\t\t_(\"dropping %s %s -- patch contents already upstream\\n\"),\n \t\t\toid_to_hex(&commit->object.oid), msg.subject);\n \t} /* else allow == 0 and there's nothing special to do */\n+\n+\tif (!res && opts->trailer_args.nr && !drop_commit) {\n+\t\tconst char *trailer_file =\n+\t\t\tmsg_file ? msg_file : git_path_merge_msg(r);\n+\n+\t\tif (amend_file_with_trailers(trailer_file,\n+\t\t\t\t\t\t&opts->trailer_args)) {\n+\t\t\tres = error(_(\"unable to add trailers to commit message\"));\n+\t\t\tgoto leave;\n+\t\t}\n+\t}\n+\n \tif (!opts->no_commit && !drop_commit) {\n \t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n \t\t\tres = do_commit(r, msg_file, author, reflog_action,\ndiff --git a/sequencer.h b/sequencer.h\nindex 719684c8a9..e21835c5a0 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -44,6 +44,7 @@ struct replay_opts {\n \tint record_origin;\n \tint no_commit;\n \tint signoff;\n+\tstruct strvec trailer_args;\n \tint allow_ff;\n \tint allow_rerere_auto;\n \tint allow_empty;\n@@ -82,8 +83,9 @@ struct replay_opts {\n \tstruct replay_ctx *ctx;\n };\n #define REPLAY_OPTS_INIT {\t\t\t\\\n-\t.edit = -1,\t\t\t\t\\\n \t.action = -1,\t\t\t\t\\\n+\t.edit = -1,\t\t\t\t\\\n+\t.trailer_args = STRVEC_INIT, \\\n \t.xopts = STRVEC_INIT,\t\t\t\\\n \t.ctx = replay_ctx_new(),\t\t\\\n }\ndiff --git a/t/meson.build b/t/meson.build\nindex 11376b9e25..13b44c9a7b 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -383,6 +383,7 @@ integration_tests = [\n   't3436-rebase-more-options.sh',\n   't3437-rebase-fixup-options.sh',\n   't3438-rebase-broken-files.sh',\n+  't3440-rebase-trailer.sh',\n   't3500-cherry.sh',\n   't3501-revert-cherry-pick.sh',\n   't3502-cherry-pick-merge.sh',\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nnew file mode 100755\nindex 0000000000..c08a9c4abf\n--- /dev/null\n+++ b/t/t3440-rebase-trailer.sh\n@@ -0,0 +1,94 @@\n+#!/bin/sh\n+#\n+\n+test_description='git rebase --trailer integration tests\n+We verify that --trailer works with the merge backend,\n+and that it is rejected early when the apply backend is requested.'\n+\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-rebase.sh # test_commit_message, helpers\n+\n+create_expect() {\n+\tcat >\"$1\" <<-EOF\n+\t\t$2\n+\n+\t\tReviewed-by: Dev <dev@example.com>\n+\tEOF\n+}\n+\n+test_expect_success 'setup repo with a small history' '\n+\tgit commit --allow-empty -m \"Initial empty commit\" &&\n+\ttest_commit first file a &&\n+\ttest_commit second file &&\n+\tgit checkout -b conflict-branch first &&\n+\ttest_commit file-2 file-2 &&\n+\ttest_commit conflict file &&\n+\ttest_commit third file &&\n+\tident=\"$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\" &&\n+\tcreate_expect initial-signed  \"Initial empty commit\" &&\n+\tcreate_expect first-signed    \"first\"                 &&\n+\tcreate_expect second-signed   \"second\"                &&\n+\tcreate_expect file2-signed    \"file-2\"                &&\n+\tcreate_expect third-signed    \"third\"                 &&\n+\tcreate_expect conflict-signed \"conflict\"\n+'\n+\n+test_expect_success 'apply backend is rejected with --trailer' '\n+\thead_before=$(git rev-parse HEAD) &&\n+\ttest_expect_code 128 \\\n+\tgit rebase --apply --trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n+\t\t\t\tHEAD^ 2>err &&\n+\ttest_grep \"requires the merge backend\" err &&\n+\ttest_cmp_rev HEAD $head_before\n+'\n+\n+test_expect_success 'reject empty --trailer argument' '\n+\tgit reset --hard third &&\n+\ttest_expect_code 128 git rebase -m --trailer \"\" HEAD^ 2>err &&\n+\ttest_grep \"empty --trailer\" err\n+'\n+\n+test_expect_success 'reject trailer with missing key before separator' '\n+\tgit reset --hard third &&\n+\ttest_expect_code 128 git rebase -m --trailer \": no-key\" HEAD^ 2>err &&\n+\ttest_grep \"missing key before separator\" err\n+'\n+\n+test_expect_success 'CLI trailer duplicates allowed; replace policy keeps last' '\n+\tgit reset --hard third &&\n+\tgit -c trailer.Bug.ifexists=replace -c trailer.Bug.ifmissing=add rebase -m --trailer \"Bug: 123\" --trailer \"Bug: 456\" HEAD~1 &&\n+\tgit cat-file commit HEAD | grep \"^Bug: 456\" &&\n+\tgit cat-file commit HEAD | grep -v \"^Bug: 123\"\n+'\n+\n+test_expect_success 'multiple Signed-off-by trailers all preserved' '\n+\tgit reset --hard third &&\n+\tgit rebase -m \\\n+\t\t--trailer \"Signed-off-by: Dev A <a@ex.com>\" \\\n+\t\t--trailer \"Signed-off-by: Dev B <b@ex.com>\" HEAD~1 &&\n+\tgit cat-file commit HEAD | grep -c \"^Signed-off-by:\" >count &&\n+\ttest \"$(cat count)\" = 2   # two new commits\n+'\n+\n+test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n+\tgit reset --hard third &&\n+\ttest_must_fail git rebase -m \\\n+\t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n+\t\tsecond third &&\n+\tgit checkout --theirs file &&\n+\tgit add file &&\n+\tgit rebase --continue &&\n+\ttest_commit_message HEAD~2 file2-signed\n+'\n+\n+test_expect_success 'rebase --root --trailer updates every commit' '\n+\tgit checkout first &&\n+\tgit rebase --root --keep-empty \\\n+\t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" &&\n+\ttest_commit_message HEAD   first-signed &&\n+\ttest_commit_message HEAD^  initial-signed\n+'\n+test_done\n-- \n2.51.0\n\n"},{"id":"528712","messageId":"20251014122452.1851103-11-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 10/29] rebase: inline trailer state paths","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:23Z","receivedAt":"2025-10-14T12:26:50Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Simplified read_basic_state() by dropping the\ntemporary trailer path variables and calling\nstate_dir_path(\"trailer\", opts) directly when\nloading trailer arguments.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/rebase.c | 8 ++------\n 1 file changed, 2 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 3b001c0757..2a2674e375 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -442,8 +442,6 @@ static int read_basic_state(struct rebase_options *opts)\n \tstruct strbuf head_name = STRBUF_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id oid;\n-\tconst char trailer_state_name[] = \"trailer\";\n-\tconst char *path = state_dir_path(trailer_state_name, opts);\n \n \tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n \t\t\t   READ_ONELINER_WARN_MISSING) ||\n@@ -512,7 +510,7 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tstrbuf_release(&buf);\n \n-\tif (strbuf_read_file(&buf, path, 0) >= 0) {\n+\tif (strbuf_read_file(&buf, state_dir_path(\"trailer\", opts), 0) >= 0) {\n \t\tconst char *p = buf.buf, *end = buf.buf + buf.len;\n \n \t\twhile (p < end) {\n@@ -535,8 +533,6 @@ static int read_basic_state(struct rebase_options *opts)\n \n static int rebase_write_basic_state(struct rebase_options *opts)\n {\n-\tconst char trailer_state_name[] = \"trailer\";\n-\n \twrite_file(state_dir_path(\"head-name\", opts), \"%s\",\n \t\t   opts->head_name ? opts->head_name : \"detached HEAD\");\n \twrite_file(state_dir_path(\"onto\", opts), \"%s\",\n@@ -568,7 +564,7 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n \t\t\t\tstrbuf_addstr(&buf, opts->trailer_args.v[i]);\n \t\t\t\tstrbuf_addch(&buf, '\\n');\n \t\t}\n-\t\twrite_file(state_dir_path(trailer_state_name, opts),\n+\t\twrite_file(state_dir_path(\"trailer\", opts),\n \t\t\t\t   \"%s\", buf.buf);\n \t\tstrbuf_release(&buf);\n \t}\n-- \n2.51.0\n\n"},{"id":"528713","messageId":"20251014122452.1851103-12-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 11/29] rebase: reuse buffer for trailer args","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:24Z","receivedAt":"2025-10-14T12:26:58Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Reset the reusable buffer before reading trailer\narguments in read_basic_state() so the existing\nallocation can be reused.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/rebase.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 2a2674e375..ff8dd9ec90 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -508,7 +508,7 @@ static int read_basic_state(struct rebase_options *opts)\n \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n \t}\n \n-\tstrbuf_release(&buf);\n+\tstrbuf_reset(&buf);\n \n \tif (strbuf_read_file(&buf, state_dir_path(\"trailer\", opts), 0) >= 0) {\n \t\tconst char *p = buf.buf, *end = buf.buf + buf.len;\n-- \n2.51.0\n\n"},{"id":"528714","messageId":"20251014122452.1851103-13-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 12/29] rebase: drop redundant strbuf_release call","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:25Z","receivedAt":"2025-10-14T12:27:06Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Removed the redundant strbuf_release() call in\nread_basic_state() so the buffer is released once\neven if strbuf_read_file() fails.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/rebase.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex ff8dd9ec90..51fb9388c7 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -524,7 +524,6 @@ static int read_basic_state(struct rebase_options *opts)\n \n \t\t\tp = nl + 1;\n \t\t}\n-\t\tstrbuf_release(&buf);\n \t}\n \tstrbuf_release(&buf);\n \n-- \n2.51.0\n\n"},{"id":"528715","messageId":"20251014122452.1851103-14-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 13/29] rebase: skip stripping of --trailer option prefix","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:26Z","receivedAt":"2025-10-14T12:27:15Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Removed redundant --trailer= prefix stripping in\nvalidate_trailer_args_after_config() since OPT_STRVEC\nalready stores only the argument text.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/rebase.c | 6 +-----\n 1 file changed, 1 insertion(+), 5 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 51fb9388c7..cc90980d7d 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1128,13 +1128,9 @@ static int validate_trailer_args_after_config(const struct strvec *cli_args,\n \t\t\t\t       struct strbuf *err)\n {\n \tfor (size_t i = 0; i < cli_args->nr; i++) {\n-\t\tconst char *raw = cli_args->v[i];\n-\t\tconst char *txt; // Key[:=]Val\n+\t\tconst char *txt = cli_args->v[i]; // Key[:=]Val\n \t\tconst char *sep;\n \n-\t\tif (!skip_prefix(raw, \"--trailer=\", &txt))\n-\t\t\ttxt = raw;\n-\n \t\tif (!*txt) {\n \t\t\tstrbuf_addstr(err, _(\"empty --trailer argument\"));\n \t\t\treturn -1;\n-- \n2.51.0\n\n"},{"id":"528716","messageId":"20251014122452.1851103-15-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 14/29] rebase: die on invalid trailer args","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:27Z","receivedAt":"2025-10-14T12:27:23Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"This can simplify the error handle in\nvalidate_trailer_args_after_config and\nits caller.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/rebase.c | 26 +++++++-------------------\n 1 file changed, 7 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex cc90980d7d..872945a897 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1124,29 +1124,22 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static int validate_trailer_args_after_config(const struct strvec *cli_args,\n-\t\t\t\t       struct strbuf *err)\n+static void validate_trailer_args_after_config(const struct strvec *cli_args)\n {\n \tfor (size_t i = 0; i < cli_args->nr; i++) {\n \t\tconst char *txt = cli_args->v[i]; // Key[:=]Val\n \t\tconst char *sep;\n \n-\t\tif (!*txt) {\n-\t\t\tstrbuf_addstr(err, _(\"empty --trailer argument\"));\n-\t\t\treturn -1;\n-\t\t}\n+\t\tif (!*txt)\n+\t\t\tdie(_(\"empty --trailer argument\"));\n \n \t\tsep = strpbrk(txt, \":=\");\n \n \t\t/* there must be key bfore seperator */\n-\t\tif (sep && sep == txt) {\n-\t\t\tstrbuf_addf(err,\n-\t\t\t\t    _(\"invalid trailer '%s': missing key before separator\"),\n-\t\t\t\t    txt);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tif (sep && sep == txt)\n+\t\t\tdie(_(\"invalid trailer '%s': missing key before separator\"),\n+\t\t\t    txt);\n \t}\n-\treturn 0;\n }\n \n int cmd_rebase(int argc,\n@@ -1353,13 +1346,8 @@ int cmd_rebase(int argc,\n \t\t\t     builtin_rebase_usage, 0);\n \n \tif (options.trailer_args.nr) {\n-\t\tstruct strbuf err = STRBUF_INIT;\n-\n-\t\tif (validate_trailer_args_after_config(&options.trailer_args, &err))\n-\t\t\tdie(\"%s\", err.buf);\n-\n+\t\tvalidate_trailer_args_after_config(&options.trailer_args);\n \t\toptions.flags |= REBASE_FORCE;\n-\t\tstrbuf_release(&err);\n \t}\n \n \tif (preserve_merges_selected)\n-- \n2.51.0\n\n"},{"id":"528717","messageId":"20251014122452.1851103-16-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 15/29] rebase: validate trailers with configured separators","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:28Z","receivedAt":"2025-10-14T12:27:32Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Moved validate_trailer_args_after_config() into\ntrailer.c so trailer argument validation reuses\nfind_separator() and respects configured separators.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n builtin/rebase.c | 18 ------------------\n trailer.c        | 25 +++++++++++++++++++++++++\n trailer.h        |  2 ++\n 3 files changed, 27 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 872945a897..a88abe08b4 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1124,24 +1124,6 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void validate_trailer_args_after_config(const struct strvec *cli_args)\n-{\n-\tfor (size_t i = 0; i < cli_args->nr; i++) {\n-\t\tconst char *txt = cli_args->v[i]; // Key[:=]Val\n-\t\tconst char *sep;\n-\n-\t\tif (!*txt)\n-\t\t\tdie(_(\"empty --trailer argument\"));\n-\n-\t\tsep = strpbrk(txt, \":=\");\n-\n-\t\t/* there must be key bfore seperator */\n-\t\tif (sep && sep == txt)\n-\t\t\tdie(_(\"invalid trailer '%s': missing key before separator\"),\n-\t\t\t    txt);\n-\t}\n-}\n-\n int cmd_rebase(int argc,\n \t       const char **argv,\n \t       const char *prefix,\ndiff --git a/trailer.c b/trailer.c\nindex b0ad7dc5c3..5ff518b436 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -7,6 +7,7 @@\n #include \"string-list.h\"\n #include \"run-command.h\"\n #include \"commit.h\"\n+#include \"strvec.h\"\n #include \"trailer.h\"\n #include \"list.h\"\n #include \"wrapper.h\"\n@@ -773,6 +774,30 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head,\n \tfree(cl_separators);\n }\n \n+void validate_trailer_args_after_config(const struct strvec *cli_args)\n+{\n+\tchar *cl_separators;\n+\n+\ttrailer_config_init();\n+\n+\tcl_separators = xstrfmt(\"=%s\", separators);\n+\n+\tfor (size_t i = 0; i < cli_args->nr; i++) {\n+\t\tconst char *txt = cli_args->v[i];\n+\t\tssize_t separator_pos;\n+\n+\t\tif (!*txt)\n+\t\t\tdie(_(\"empty --trailer argument\"));\n+\n+\t\tseparator_pos = find_separator(txt, cl_separators);\n+\t\tif (separator_pos == 0)\n+\t\t\tdie(_(\"invalid trailer '%s': missing key before separator\"),\n+\t\t    txt);\n+\t}\n+\n+\tfree(cl_separators);\n+}\n+\n static const char *next_line(const char *str)\n {\n \tconst char *nl = strchrnul(str, '\\n');\ndiff --git a/trailer.h b/trailer.h\nindex 01f711fb13..28719aa480 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -68,6 +68,8 @@ void parse_trailers_from_config(struct list_head *config_head);\n void parse_trailers_from_command_line_args(struct list_head *arg_head,\n \t\t\t\t\t   struct list_head *new_trailer_head);\n \n+void validate_trailer_args_after_config(const struct strvec *cli_args);\n+\n void process_trailers_lists(struct list_head *head,\n \t\t\t    struct list_head *arg_head);\n \n-- \n2.51.0\n\n"},{"id":"528718","messageId":"20251014122452.1851103-17-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 16/29] sequencer: add trailers to message before writing file","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:29Z","receivedAt":"2025-10-14T12:27:40Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Added trailer processing to the in-memory commit message\nwithin do_pick_commit, ensuring fixup/squash commands\nremain untouched before the message is written.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n sequencer.c | 19 ++++++++-----------\n trailer.c   |  4 ++--\n trailer.h   |  3 +++\n 3 files changed, 13 insertions(+), 13 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5103ae786c..552e629e4f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2444,6 +2444,14 @@ static int do_pick_commit(struct repository *r,\n \tif (opts->signoff && !is_fixup(command))\n \t\tappend_signoff(&ctx->message, 0, 0);\n \n+\tif (opts->trailer_args.nr && !is_fixup(command)) {\n+\t\tif (amend_strbuf_with_trailers(&ctx->message,\n+\t\t\t\t\t       &opts->trailer_args)) {\n+\t\t\tres = error(_(\"unable to add trailers to commit message\"));\n+\t\t\tgoto leave;\n+\t\t}\n+\t}\n+\n \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n \t\tres = -1;\n \telse if (!opts->strategy ||\n@@ -2519,17 +2527,6 @@ static int do_pick_commit(struct repository *r,\n \t\t\toid_to_hex(&commit->object.oid), msg.subject);\n \t} /* else allow == 0 and there's nothing special to do */\n \n-\tif (!res && opts->trailer_args.nr && !drop_commit) {\n-\t\tconst char *trailer_file =\n-\t\t\tmsg_file ? msg_file : git_path_merge_msg(r);\n-\n-\t\tif (amend_file_with_trailers(trailer_file,\n-\t\t\t\t\t\t&opts->trailer_args)) {\n-\t\t\tres = error(_(\"unable to add trailers to commit message\"));\n-\t\t\tgoto leave;\n-\t\t}\n-\t}\n-\n \tif (!opts->no_commit && !drop_commit) {\n \t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n \t\t\tres = do_commit(r, msg_file, author, reflog_action,\ndiff --git a/trailer.c b/trailer.c\nindex 5ff518b436..fb691c6400 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1250,8 +1250,8 @@ void trailer_iterator_release(struct trailer_iterator *iter)\n \tstrbuf_release(&iter->key);\n }\n \n-static int amend_strbuf_with_trailers(struct strbuf *buf,\n-\t\t\t\t\t\t\t\t\t  const struct strvec *trailer_args)\n+int amend_strbuf_with_trailers(struct strbuf *buf,\n+       const struct strvec *trailer_args)\n {\n \tstruct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;\n \tLIST_HEAD(new_trailer_head);\ndiff --git a/trailer.h b/trailer.h\nindex 28719aa480..e4b1de855e 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -197,6 +197,9 @@ int trailer_iterator_advance(struct trailer_iterator *iter);\n  */\n void trailer_iterator_release(struct trailer_iterator *iter);\n \n+int amend_strbuf_with_trailers(struct strbuf *buf,\n+       const struct strvec *trailer_args);\n+\n /*\n  * Augment a file to add trailers to it (similar to 'git interpret-trailers').\n  * Returns 0 on success or a non-zero error code on failure.\n-- \n2.51.0\n\n"},{"id":"528719","messageId":"20251014122452.1851103-18-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 17/29] tests: t3440: create expect files at point of use","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:30Z","receivedAt":"2025-10-14T12:27:49Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Created the expected trailer files within\nthe individual rebase tests that use them,\nsimplifying the shared history setup and\navoiding unused fixtures.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 12 ++++--------\n 1 file changed, 4 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex c08a9c4abf..0c0185d058 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -26,14 +26,7 @@ test_expect_success 'setup repo with a small history' '\n \tgit checkout -b conflict-branch first &&\n \ttest_commit file-2 file-2 &&\n \ttest_commit conflict file &&\n-\ttest_commit third file &&\n-\tident=\"$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\" &&\n-\tcreate_expect initial-signed  \"Initial empty commit\" &&\n-\tcreate_expect first-signed    \"first\"                 &&\n-\tcreate_expect second-signed   \"second\"                &&\n-\tcreate_expect file2-signed    \"file-2\"                &&\n-\tcreate_expect third-signed    \"third\"                 &&\n-\tcreate_expect conflict-signed \"conflict\"\n+\ttest_commit third file\n '\n \n test_expect_success 'apply backend is rejected with --trailer' '\n@@ -74,6 +67,7 @@ test_expect_success 'multiple Signed-off-by trailers all preserved' '\n '\n \n test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n+\tcreate_expect file2-signed \"file-2\" &&\n \tgit reset --hard third &&\n \ttest_must_fail git rebase -m \\\n \t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n@@ -85,6 +79,8 @@ test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n '\n \n test_expect_success 'rebase --root --trailer updates every commit' '\n+\tcreate_expect initial-signed \"Initial empty commit\" &&\n+\tcreate_expect first-signed \"first\" &&\n \tgit checkout first &&\n \tgit rebase --root --keep-empty \\\n \t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" &&\n-- \n2.51.0\n\n"},{"id":"528720","messageId":"20251014122452.1851103-19-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 18/29] tests: t3440: check apply backend error includes option","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:31Z","receivedAt":"2025-10-14T12:27:57Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nUpdated the rebase trailer test to assert that\nthe apply-backend error explicitly includes the\n--trailer option in its message while retaining\nthe existing backend check.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex 0c0185d058..6f1a062e8f 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -34,7 +34,7 @@ test_expect_success 'apply backend is rejected with --trailer' '\n \ttest_expect_code 128 \\\n \tgit rebase --apply --trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n \t\t\t\tHEAD^ 2>err &&\n-\ttest_grep \"requires the merge backend\" err &&\n+\ttest_grep \"fatal: --trailer requires the merge backend\" err &&\n \ttest_cmp_rev HEAD $head_before\n '\n \n-- \n2.51.0\n\n"},{"id":"528721","messageId":"20251014122452.1851103-20-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 19/29] tests: t3440: use test_commit_message for trailer checks","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:32Z","receivedAt":"2025-10-14T12:28:06Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Replaced the pipeline-based trailer assertions with\nexplicit expectations verified by test_commit_message,\nensuring the rebase trailer tests catch git command\nfailures reliably.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 24 +++++++++++++++++-------\n 1 file changed, 17 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex 6f1a062e8f..16b059c2c3 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -52,18 +52,28 @@ test_expect_success 'reject trailer with missing key before separator' '\n \n test_expect_success 'CLI trailer duplicates allowed; replace policy keeps last' '\n \tgit reset --hard third &&\n-\tgit -c trailer.Bug.ifexists=replace -c trailer.Bug.ifmissing=add rebase -m --trailer \"Bug: 123\" --trailer \"Bug: 456\" HEAD~1 &&\n-\tgit cat-file commit HEAD | grep \"^Bug: 456\" &&\n-\tgit cat-file commit HEAD | grep -v \"^Bug: 123\"\n+\tgit -c trailer.Bug.ifexists=replace -c trailer.Bug.ifmissing=add \\\n+\t\trebase -m --trailer \"Bug: 123\" --trailer \"Bug: 456\" HEAD~1 &&\n+\tcat >expect <<-\\EOF &&\n+\tthird\n+\n+\tBug: 456\n+\tEOF\n+\ttest_commit_message HEAD expect\n '\n \n test_expect_success 'multiple Signed-off-by trailers all preserved' '\n \tgit reset --hard third &&\n \tgit rebase -m \\\n-\t\t--trailer \"Signed-off-by: Dev A <a@ex.com>\" \\\n-\t\t--trailer \"Signed-off-by: Dev B <b@ex.com>\" HEAD~1 &&\n-\tgit cat-file commit HEAD | grep -c \"^Signed-off-by:\" >count &&\n-\ttest \"$(cat count)\" = 2   # two new commits\n+\t\t\t--trailer \"Signed-off-by: Dev A <a@ex.com>\" \\\n+\t\t\t--trailer \"Signed-off-by: Dev B <b@ex.com>\" HEAD~1 &&\n+\tcat >expect <<-\\EOF &&\n+\tthird\n+\n+\tSigned-off-by: Dev A <a@ex.com>\n+\tSigned-off-by: Dev B <b@ex.com>\n+\tEOF\n+\ttest_commit_message HEAD expect\n '\n \n test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n-- \n2.51.0\n\n"},{"id":"528722","messageId":"20251014122452.1851103-21-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 20/29] tests: t3440: drop redundant resets and pass branch to rebase where needed","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:33Z","receivedAt":"2025-10-14T12:28:14Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nStop hard-resetting to third in these tests. Where the branch matters,\ninvoke git rebase -m ... HEAD~1 third to make the target explicit and\npreserve the original semantics.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 9 ++-------\n 1 file changed, 2 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex 16b059c2c3..4f313654d6 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -39,21 +39,18 @@ test_expect_success 'apply backend is rejected with --trailer' '\n '\n \n test_expect_success 'reject empty --trailer argument' '\n-\tgit reset --hard third &&\n \ttest_expect_code 128 git rebase -m --trailer \"\" HEAD^ 2>err &&\n \ttest_grep \"empty --trailer\" err\n '\n \n test_expect_success 'reject trailer with missing key before separator' '\n-\tgit reset --hard third &&\n \ttest_expect_code 128 git rebase -m --trailer \": no-key\" HEAD^ 2>err &&\n \ttest_grep \"missing key before separator\" err\n '\n \n test_expect_success 'CLI trailer duplicates allowed; replace policy keeps last' '\n-\tgit reset --hard third &&\n \tgit -c trailer.Bug.ifexists=replace -c trailer.Bug.ifmissing=add \\\n-\t\trebase -m --trailer \"Bug: 123\" --trailer \"Bug: 456\" HEAD~1 &&\n+\t\trebase -m --trailer \"Bug: 123\" --trailer \"Bug: 456\" HEAD~1 third &&\n \tcat >expect <<-\\EOF &&\n \tthird\n \n@@ -63,10 +60,9 @@ test_expect_success 'CLI trailer duplicates allowed; replace policy keeps last'\n '\n \n test_expect_success 'multiple Signed-off-by trailers all preserved' '\n-\tgit reset --hard third &&\n \tgit rebase -m \\\n \t\t\t--trailer \"Signed-off-by: Dev A <a@ex.com>\" \\\n-\t\t\t--trailer \"Signed-off-by: Dev B <b@ex.com>\" HEAD~1 &&\n+\t\t\t--trailer \"Signed-off-by: Dev B <b@ex.com>\" HEAD~1 third &&\n \tcat >expect <<-\\EOF &&\n \tthird\n \n@@ -78,7 +74,6 @@ test_expect_success 'multiple Signed-off-by trailers all preserved' '\n \n test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n \tcreate_expect file2-signed \"file-2\" &&\n-\tgit reset --hard third &&\n \ttest_must_fail git rebase -m \\\n \t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n \t\tsecond third &&\n-- \n2.51.0\n\n"},{"id":"528723","messageId":"20251014122452.1851103-22-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 21/29] tests: t3440: assert trailer on HEAD after conflict rebase","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:34Z","receivedAt":"2025-10-14T12:28:22Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nSwitch the test to check the trailer on HEAD (not HEAD~2) and build the\nexpected message for \"third\", matching the rebased tip after conflicts.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex 4f313654d6..e1a3d2e3eb 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -73,14 +73,14 @@ test_expect_success 'multiple Signed-off-by trailers all preserved' '\n '\n \n test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n-\tcreate_expect file2-signed \"file-2\" &&\n+\tcreate_expect third-signed \"third\" &&\n \ttest_must_fail git rebase -m \\\n \t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n \t\tsecond third &&\n \tgit checkout --theirs file &&\n \tgit add file &&\n \tgit rebase --continue &&\n-\ttest_commit_message HEAD~2 file2-signed\n+\ttest_commit_message HEAD third-signed\n '\n \n test_expect_success 'rebase --root --trailer updates every commit' '\n-- \n2.51.0\n\n"},{"id":"528724","messageId":"20251014122452.1851103-23-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 22/29] rebase: persist --trailer options across restarts","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:35Z","receivedAt":"2025-10-14T12:28:31Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"t3440 showed rebase -m loses trailers after conflicts.\nEach --continue spawns a fresh replay_opts without trailer_args.\nSerialize them into rebase-merge/trailer so new runs reload them.\nThis keeps the user requested trailers intact when resuming.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n sequencer.c | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 552e629e4f..c02364cfce 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -209,6 +209,7 @@ static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, \"rebase-merge/reschedul\n static GIT_PATH_FUNC(rebase_path_no_reschedule_failed_exec, \"rebase-merge/no-reschedule-failed-exec\")\n static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, \"rebase-merge/drop_redundant_commits\")\n static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, \"rebase-merge/keep_redundant_commits\")\n+static GIT_PATH_FUNC(rebase_path_trailer, \"rebase-merge/trailer\")\n \n /*\n  * A 'struct replay_ctx' represents the private state of the sequencer.\n@@ -3244,6 +3245,17 @@ static int read_populate_opts(struct replay_opts *opts)\n \n \t\tread_strategy_opts(opts, &buf);\n \t\tstrbuf_reset(&buf);\n+\t\tif (strbuf_read_file(&buf, rebase_path_trailer(), 0) >= 0) {\n+\t\t\tchar *p = buf.buf, *nl;\n+\n+\t\t\twhile ((nl = strchr(p, '\\n'))) {\n+\t\t\t\t*nl = '\\0';\n+\t\t\t\tif (*p)\n+\t\t\t\t\tstrvec_push(&opts->trailer_args, p);\n+\t\t\t\tp = nl + 1;\n+\t\t\t}\n+\t\t\tstrbuf_reset(&buf);\n+\t\t}\n \n \t\tif (read_oneliner(&ctx->current_fixups,\n \t\t\t\t  rebase_path_current_fixups(),\n@@ -3338,6 +3350,14 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,\n \t\twrite_file(rebase_path_reschedule_failed_exec(), \"%s\", \"\");\n \telse\n \t\twrite_file(rebase_path_no_reschedule_failed_exec(), \"%s\", \"\");\n+\tif (opts->trailer_args.nr) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\n+\t\tfor (size_t i = 0; i < opts->trailer_args.nr; i++)\n+\t\t\tstrbuf_addf(&buf, \"%s\\n\", opts->trailer_args.v[i]);\n+\t\twrite_file(rebase_path_trailer(), \"%s\", buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t}\n \n \treturn 0;\n }\n-- \n2.51.0\n\n"},{"id":"528725","messageId":"20251014122452.1851103-24-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 23/29] tests: t3440: remove redundant --keep-empty","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:36Z","receivedAt":"2025-10-14T12:28:40Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\n--keep-empty is the default these days so\nwe can drop that.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex e1a3d2e3eb..2315a0c86c 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -87,7 +87,7 @@ test_expect_success 'rebase --root --trailer updates every commit' '\n \tcreate_expect initial-signed \"Initial empty commit\" &&\n \tcreate_expect first-signed \"first\" &&\n \tgit checkout first &&\n-\tgit rebase --root --keep-empty \\\n+\tgit rebase --root \\\n \t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" &&\n \ttest_commit_message HEAD   first-signed &&\n \ttest_commit_message HEAD^  initial-signed\n-- \n2.51.0\n\n"},{"id":"528726","messageId":"20251014122452.1851103-25-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 24/29] tests: t3440: use helper for trailer checks","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:37Z","receivedAt":"2025-10-14T12:28:48Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Introduce expect_trailer_msg() to wrap test_commit_message\nand dedupe the trailer via REVIEWED_BY_TRAILER.\nDrop create_expect and temp files. In the conflict case,\nassert on HEAD (rebased \"third\") instead of HEAD~2. Update\n--apply rejection and --root tests to use the helper.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 23 +++++++++++------------\n 1 file changed, 11 insertions(+), 12 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex 2315a0c86c..36f11f579e 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -11,11 +11,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-rebase.sh # test_commit_message, helpers\n \n-create_expect() {\n-\tcat >\"$1\" <<-EOF\n+REVIEWED_BY_TRAILER=\"Reviewed-by: Dev <dev@example.com>\"\n+\n+expect_trailer_msg() {\n+\ttest_commit_message \"$1\" <<-EOF\n \t\t$2\n \n-\t\tReviewed-by: Dev <dev@example.com>\n+\t\t${3:-$REVIEWED_BY_TRAILER}\n \tEOF\n }\n \n@@ -32,7 +34,7 @@ test_expect_success 'setup repo with a small history' '\n test_expect_success 'apply backend is rejected with --trailer' '\n \thead_before=$(git rev-parse HEAD) &&\n \ttest_expect_code 128 \\\n-\tgit rebase --apply --trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n+\tgit rebase --apply --trailer \"$REVIEWED_BY_TRAILER\" \\\n \t\t\t\tHEAD^ 2>err &&\n \ttest_grep \"fatal: --trailer requires the merge backend\" err &&\n \ttest_cmp_rev HEAD $head_before\n@@ -73,23 +75,20 @@ test_expect_success 'multiple Signed-off-by trailers all preserved' '\n '\n \n test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n-\tcreate_expect third-signed \"third\" &&\n \ttest_must_fail git rebase -m \\\n-\t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" \\\n+\t\t--trailer \"$REVIEWED_BY_TRAILER\" \\\n \t\tsecond third &&\n \tgit checkout --theirs file &&\n \tgit add file &&\n \tgit rebase --continue &&\n-\ttest_commit_message HEAD third-signed\n+\texpect_trailer_msg HEAD \"third\"\n '\n \n test_expect_success 'rebase --root --trailer updates every commit' '\n-\tcreate_expect initial-signed \"Initial empty commit\" &&\n-\tcreate_expect first-signed \"first\" &&\n \tgit checkout first &&\n \tgit rebase --root \\\n-\t\t--trailer \"Reviewed-by: Dev <dev@example.com>\" &&\n-\ttest_commit_message HEAD   first-signed &&\n-\ttest_commit_message HEAD^  initial-signed\n+\t\t--trailer \"$REVIEWED_BY_TRAILER\" &&\n+\texpect_trailer_msg HEAD  \"first\" &&\n+\texpect_trailer_msg HEAD^ \"Initial empty commit\"\n '\n test_done\n-- \n2.51.0\n\n"},{"id":"528727","messageId":"20251014122452.1851103-26-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 25/29] tests: t3440: test --trailer without values","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:38Z","receivedAt":"2025-10-14T12:28:57Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Added a regression test to ensure git\nrebase --trailer accepts trailers without\nvalues while preserving the separator’s\ntrailing space in the recorded message.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex 36f11f579e..df121efd0e 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -50,6 +50,16 @@ test_expect_success 'reject trailer with missing key before separator' '\n \ttest_grep \"missing key before separator\" err\n '\n \n+test_expect_success 'allow trailer with missing value after separator' '\n+\tgit rebase -m --trailer \"Acked-by:\" HEAD~1 third &&\n+\tcat >expect <<-\\EOF &&\n+\tthird\n+\n+\tAcked-by: \n+\tEOF\n+\ttest_commit_message HEAD expect\n+'\n+\n test_expect_success 'CLI trailer duplicates allowed; replace policy keeps last' '\n \tgit -c trailer.Bug.ifexists=replace -c trailer.Bug.ifmissing=add \\\n \t\trebase -m --trailer \"Bug: 123\" --trailer \"Bug: 456\" HEAD~1 third &&\n-- \n2.51.0\n\n"},{"id":"528728","messageId":"20251014122452.1851103-27-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 26/29] tests: t3440: convert ex.com to example.com","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:39Z","receivedAt":"2025-10-14T12:29:05Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"From: Li Chen <chenl311@chinatelecom.cn>\n\nLets use example.com here rather than some\nrandom domain that might actually exist.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex df121efd0e..dd703b0eb7 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -73,13 +73,13 @@ test_expect_success 'CLI trailer duplicates allowed; replace policy keeps last'\n \n test_expect_success 'multiple Signed-off-by trailers all preserved' '\n \tgit rebase -m \\\n-\t\t\t--trailer \"Signed-off-by: Dev A <a@ex.com>\" \\\n-\t\t\t--trailer \"Signed-off-by: Dev B <b@ex.com>\" HEAD~1 third &&\n+\t\t\t--trailer \"Signed-off-by: Dev A <a@example.com>\" \\\n+\t\t\t--trailer \"Signed-off-by: Dev B <b@example.com>\" HEAD~1 third &&\n \tcat >expect <<-\\EOF &&\n \tthird\n \n-\tSigned-off-by: Dev A <a@ex.com>\n-\tSigned-off-by: Dev B <b@ex.com>\n+\tSigned-off-by: Dev A <a@example.com>\n+\tSigned-off-by: Dev B <b@example.com>\n \tEOF\n \ttest_commit_message HEAD expect\n '\n-- \n2.51.0\n\n"},{"id":"528729","messageId":"20251014122452.1851103-28-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 27/29] tests: t3440: ensure trailers persist after rebase continue","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:40Z","receivedAt":"2025-10-14T12:29:14Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Rebuilt the conflict test branch, added a fourth commit,\nand asserted that both the conflicted and subsequent commits\nreceive the --trailer data in t/t3440-rebase-trailer.sh.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex dd703b0eb7..7a2ddb440e 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -85,13 +85,16 @@ test_expect_success 'multiple Signed-off-by trailers all preserved' '\n '\n \n test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n+\tgit checkout -B conflict-branch third &&\n+\ttest_commit fourth file &&\n \ttest_must_fail git rebase -m \\\n-\t\t--trailer \"$REVIEWED_BY_TRAILER\" \\\n-\t\tsecond third &&\n+\t\t\t--trailer \"$REVIEWED_BY_TRAILER\" \\\n+\t\t\tsecond &&\n \tgit checkout --theirs file &&\n \tgit add file &&\n \tgit rebase --continue &&\n-\texpect_trailer_msg HEAD \"third\"\n+\texpect_trailer_msg HEAD \"fourth\" &&\n+\texpect_trailer_msg HEAD^ \"third\"\n '\n \n test_expect_success 'rebase --root --trailer updates every commit' '\n-- \n2.51.0\n\n"},{"id":"528730","messageId":"20251014122452.1851103-29-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 28/29] tests: t3440: exercise trailer config mapping","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:41Z","receivedAt":"2025-10-14T12:29:22Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Updated the rebase --root --trailer test\nto exercise trailer.review.key configuration\nand the --trailer= CLI form that uses an\nequals separator, ensuring we still add the\nexpected Reviewed-by trailer.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n t/t3440-rebase-trailer.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex 7a2ddb440e..ca0619655e 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -99,8 +99,8 @@ test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n \n test_expect_success 'rebase --root --trailer updates every commit' '\n \tgit checkout first &&\n-\tgit rebase --root \\\n-\t\t--trailer \"$REVIEWED_BY_TRAILER\" &&\n+\tgit -c trailer.review.key=Reviewed-by rebase --root \\\n+\t\t--trailer=review=\"Dev <dev@example.com>\" &&\n \texpect_trailer_msg HEAD  \"first\" &&\n \texpect_trailer_msg HEAD^ \"Initial empty commit\"\n '\n-- \n2.51.0\n\n"},{"id":"528731","messageId":"20251014122452.1851103-30-me@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"[PATCH v4 29/29] sequencer: honor --trailer with fixup -C","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:24:42Z","receivedAt":"2025-10-14T12:29:30Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Add an interactive rebase test that exercises\ntodo lists containing fixup and fixup -C commands,\nand teach append_squash_message() to append trailers\nwhen replacing the commit message.\n\nSigned-off-by: Li Chen <chenl311@chinatelecom.cn>\n---\n sequencer.c               |  4 ++++\n t/t3440-rebase-trailer.sh | 27 +++++++++++++++++++++++++++\n 2 files changed, 31 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex c02364cfce..fbf35cb474 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2027,6 +2027,10 @@ static int append_squash_message(struct strbuf *buf, const char *body,\n \t\tif (opts->signoff)\n \t\t\tappend_signoff(buf, 0, 0);\n \n+\t\tif (opts->trailer_args.nr &&\n+\t\t\tamend_strbuf_with_trailers(buf, &opts->trailer_args))\n+\t\t\treturn error(_(\"unable to add trailers to commit message\"));\n+\n \t\tif ((command == TODO_FIXUP) &&\n \t\t    (flag & TODO_REPLACE_FIXUP_MSG) &&\n \t\t    (file_exists(rebase_path_fixup_msg()) ||\ndiff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\nindex ca0619655e..d0526ea0e9 100755\n--- a/t/t3440-rebase-trailer.sh\n+++ b/t/t3440-rebase-trailer.sh\n@@ -97,6 +97,33 @@ test_expect_success 'rebase -m --trailer adds trailer after conflicts' '\n \texpect_trailer_msg HEAD^ \"third\"\n '\n \n+test_expect_success '--trailer handles fixup commands in todo list' '\n+\tgit checkout -B fixup-trailer HEAD &&\n+\ttest_commit fixup-base base &&\n+\ttest_commit fixup-second second &&\n+\tfirst_short=$(git rev-parse --short fixup-base) &&\n+\tsecond_short=$(git rev-parse --short fixup-second) &&\n+\tcat >todo <<EOF &&\n+pick $first_short fixup-base\n+fixup $second_short fixup-second\n+EOF\n+\t(\n+\t\tset_replace_editor todo &&\n+\t\tgit rebase -i --trailer \"$REVIEWED_BY_TRAILER\" HEAD~2\n+\t) &&\n+\texpect_trailer_msg HEAD \"fixup-base\" &&\n+\tgit reset --hard fixup-second &&\n+\tcat >todo <<EOF &&\n+pick $first_short fixup-base\n+fixup -C $second_short fixup-second\n+EOF\n+\t(\n+\t\tset_replace_editor todo &&\n+\t\tgit rebase -i --trailer \"$REVIEWED_BY_TRAILER\" HEAD~2\n+\t) &&\n+\texpect_trailer_msg HEAD \"fixup-second\"\n+'\n+\n test_expect_success 'rebase --root --trailer updates every commit' '\n \tgit checkout first &&\n \tgit -c trailer.review.key=Reviewed-by rebase --root \\\n-- \n2.51.0\n\n"},{"id":"528732","messageId":"199e2b43582.47bdac39780183.537796020815726330@linux.beauty","threadId":"64318","inReplyTo":"20251014122452.1851103-1-me@linux.beauty","subject":"Re: [PATCH v4 00/29] rebase: support --trailer","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-14T12:31:25Z","receivedAt":"2025-10-14T12:31:35Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Hi Phillip & Junio,\n\nI apologize for the delay in sending out v4. I will respond to\nand update the code in the following patchset (if needed)\n as soon as possible. \n\nMy apologies!\n\n ---- On Tue, 14 Oct 2025 20:24:13 +0800  Li Chen <me@linux.beauty> wrote --- \n > From: Li Chen <chenl311@chinatelecom.cn>\n > \n > This patch series teaches git rebase a new\n > --trailer <text> option and, as a prerequisite, moves all trailer\n > handling out of the external interpret-trailers helper and into the\n > builtin code path, as suggested by Phillip Wood.\n > \n > Patch 0 switches trailer.c to an in-memory implementation\n > (amend_strbuf_with_trailers()). It removes every fork/exec.\n > \n > Patch 1~8 fix all reviewer comments from v3 for patch 0. \n > \n > Patch 9 builds on that helper to implement\n > git rebase --trailer. When the option is given we:\n > force the merge backend (apply/am backend lacks a message filter),\n > automatically enable --force-rebase so that fast-forwarded\n > commits are rewritten, and append the requested trailer(s) to every\n > rewritten commit.\n > State is stored in $state_dir/trailer so an interrupted rebase can\n > resume safely. A dedicated test-suite (t3440) exercises plain,\n > conflict, --root, invalid-input scenarios and etc.\n > \n > The rest patches address all reviewer comments from v3 for patch 9. \n > \n > All t/*.sh testcases have run successfully.\n > \n > v4: fix all reviewer comments in v3. [2]\n > v3: merges the remaining trailer paths into one in-process helper, dropping the\n >     duplicate code, as pointed by Junio and Phillip [1]\n > v2: fix issues pointed by Phillip \n > RFC link: https://lore.kernel.org/git/196a5ac1393.f5b4db7d187309.2451613571977217927@linux.beauty/\n > \n > Comments welcome!\n > \n > [1]: https://lore.kernel.org/git/xmqq8qlzkukw.fsf@gitster.g/\n > [2]: https://lore.kernel.org/git/20250803150059.402017-1-me@linux.beauty/\n > \n > Li Chen (29):\n >   trailer: append trailers in-process and drop the fork to\n >     `interpret-trailers`\n >   trailer: restore interpret_trailers helper\n >   trailer: drop --trailer prefix handling in amend helper\n >   trailer: move config_head and arg_head to if storage\n >   trailer: use bool for had_trailer_before\n >   interpret-trailers: buffer stdout output\n >   trailer: mirror interpret-trailers output flow\n >   trailer: handle trailer append failures gently\n >   rebase: support --trailer\n >   rebase: inline trailer state paths\n >   rebase: reuse buffer for trailer args\n >   rebase: drop redundant strbuf_release call\n >   rebase: skip stripping of --trailer option prefix\n >   rebase: die on invalid trailer args\n >   rebase: validate trailers with configured separators\n >   sequencer: add trailers to message before writing file\n >   tests: t3440: create expect files at point of use\n >   tests: t3440: check apply backend error includes option\n >   tests: t3440: use test_commit_message for trailer checks\n >   tests: t3440: drop redundant resets and pass branch to rebase where\n >     needed\n >   tests: t3440: assert trailer on HEAD after conflict rebase\n >   rebase: persist --trailer options across restarts\n >   tests: t3440: remove redundant --keep-empty\n >   tests: t3440: use helper for trailer checks\n >   tests: t3440: test --trailer without values\n >   tests: t3440: convert ex.com to example.com\n >   tests: t3440: ensure trailers persist after rebase continue\n >   tests: t3440: exercise trailer config mapping\n >   sequencer: honor --trailer with fixup -C\n > \n >  Documentation/git-rebase.adoc |   7 ++\n >  builtin/commit.c              |   2 +-\n >  builtin/interpret-trailers.c  |  94 +++++-------------------\n >  builtin/rebase.c              |  50 +++++++++++++\n >  builtin/tag.c                 |   3 +-\n >  sequencer.c                   |  34 +++++++++\n >  sequencer.h                   |   4 +-\n >  t/meson.build                 |   1 +\n >  t/t3440-rebase-trailer.sh     | 134 ++++++++++++++++++++++++++++++++++\n >  trailer.c                     | 130 ++++++++++++++++++++++++++++++---\n >  trailer.h                     |  23 +++++-\n >  wrapper.c                     |  16 ++++\n >  wrapper.h                     |   6 ++\n >  13 files changed, 411 insertions(+), 93 deletions(-)\n >  create mode 100755 t/t3440-rebase-trailer.sh\n > \n > -- \n > 2.51.0\n > \n > \n\nRegards,\n\nLi​\n\n"},{"id":"528735","messageId":"69565243-0d5b-4a11-9826-c4d36affbfba@app.fastmail.com","threadId":"64318","inReplyTo":"20251014122452.1851103-26-me@linux.beauty","subject":"Re: [PATCH v4 25/29] tests: t3440: test --trailer without values","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-10-14T13:22:46Z","receivedAt":"2025-10-14T13:23:12Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Tue, Oct 14, 2025, at 14:24, Li Chen wrote:\n> Added a regression test to ensure git\n> rebase --trailer accepts trailers without\n> values while preserving the separator’s\n> trailing space in the recorded message.\n\nSee “imperative-mood” in `Documentation/SubmittingPatches`.\nSomething like:\n\n    Add a regression test to ensure\n\n>\n> Signed-off-by: Li Chen <chenl311@chinatelecom.cn>\n> ---\n>  t/t3440-rebase-trailer.sh | 10 ++++++++++\n>  1 file changed, 10 insertions(+)\n>\n> diff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh\n> index 36f11f579e..df121efd0e 100755\n> --- a/t/t3440-rebase-trailer.sh\n> +++ b/t/t3440-rebase-trailer.sh\n> @@ -50,6 +50,16 @@ test_expect_success 'reject trailer with missing key\n> before separator' '\n>  \ttest_grep \"missing key before separator\" err\n>  '\n>\n> +test_expect_success 'allow trailer with missing value after separator'\n> '\n> +\tgit rebase -m --trailer \"Acked-by:\" HEAD~1 third &&\n> +\tcat >expect <<-\\EOF &&\n> +\tthird\n> +\n> +\tAcked-by:\n\nThis adds a trailing space to the source which will make\n`ci/check-whitespace.sh` fail.  I think you are supposed to do something\nsimilar to what is done in `t/t4124-apply-ws-rule.sh`.  Namely to use\nsome placeholder character like `_`:\n\n    Acked-by:_\n\nTogether with:\n\n    sed -e \"s/_/ /g\"\n\nI could also imagine that a variable like `${SP}` might have worked\ntogether with `-EOF` similar to single quote:\n\n   t/test-lib.sh:SQ=\\'\n\nBut `t/test-lib.sh` does not seem to have that.  (Although it does have\n`LF` (line feed)).\n\n> +\tEOF\n>[snip]\n"},{"id":"528759","messageId":"a0fae0cf-a1c0-4086-bcc0-a66f1aedf512@app.fastmail.com","threadId":"64318","inReplyTo":"20251014122452.1851103-2-me@linux.beauty","subject":"Re: [PATCH v4 01/29] trailer: append trailers in-process and drop the fork to `interpret-trailers`","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-10-14T20:43:09Z","receivedAt":"2025-10-14T20:43:31Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Tue, Oct 14, 2025, at 14:24, Li Chen wrote:\n> From: Li Chen <chenl311@chinatelecom.cn>\n>\n> Route all trailer insertion through trailer_process() and make\n> builtin/interpret-trailers just do file I/O before calling into it.\n> amend_file_with_trailers() now shares the same code path.\n>\n> This removes the fork/exec and tempfile juggling, cutting overhead and\n> simplifying error handling. No functional change is intended. It also\n\nWhy “is intended” instead of “No functional change.”?\n\n> centralizes logic to prepare for follow-up rebase --trailer patch.\n>\n> Signed-off-by: Li Chen <chenl311@chinatelecom.cn>\n> ---\n>  builtin/interpret-trailers.c | 116 ++++++++------------------------\n>  trailer.c                    | 125 ++++++++++++++++++++++++++++++++---\n>  trailer.h                    |  18 ++++-\n>  3 files changed, 157 insertions(+), 102 deletions(-)\n>[...]\n>  \tnew_trailers_clear(&trailers);\n> diff --git a/trailer.c b/trailer.c\n> index 911a81ed99..8aec466b5f 100644\n> --- a/trailer.c\n> +++ b/trailer.c\n> @@ -1224,14 +1224,121 @@ void trailer_iterator_release(struct\n> trailer_iterator *iter)\n>  \tstrbuf_release(&iter->key);\n>  }\n>\n> -int amend_file_with_trailers(const char *path, const struct strvec\n> *trailer_args)\n> +static int amend_strbuf_with_trailers(struct strbuf *buf,\n> +\t\t\t\t\t\t\t\t\t  const struct strvec *trailer_args)\n\nThis needs Clang formatting.\n\n>  {\n> -\tstruct child_process run_trailer = CHILD_PROCESS_INIT;\n>[snip]\n"},{"id":"528760","messageId":"cbe93380-e145-4ebd-a213-928b8c3ba085@app.fastmail.com","threadId":"64318","inReplyTo":"20251014122452.1851103-10-me@linux.beauty","subject":"Re: [PATCH v4 09/29] rebase: support --trailer","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-10-14T20:43:33Z","receivedAt":"2025-10-14T20:43:54Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Tue, Oct 14, 2025, at 14:24, Li Chen wrote:\n> From: Li Chen <chenl311@chinatelecom.cn>\n>\n> Implement a new `--trailer <text>` option for `git rebase`\n> (support merge backend only now), which appends arbitrary\n> trailer lines to each rebased commit message.\n>\n> Reject it if the user passes an option that requires the\n> apply backend (git am) since it lacks message‑filter/trailer\n> hook. otherwise we can just use the merge backend.\n>\n> Automatically set REBASE_FORCE when any trailer is supplied.\n>\n> And reject invalid input before user edit the interactive file.\n\ns/edit/edits/\n\n>\n> Signed-off-by: Li Chen <chenl311@chinatelecom.cn>\n> ---\n>  Documentation/git-rebase.adoc |  7 +++\n>  builtin/rebase.c              | 89 +++++++++++++++++++++++++++++++++\n>  sequencer.c                   | 13 +++++\n>  sequencer.h                   |  4 +-\n>  t/meson.build                 |  1 +\n>  t/t3440-rebase-trailer.sh     | 94 +++++++++++++++++++++++++++++++++++\n>  6 files changed, 207 insertions(+), 1 deletion(-)\n>  create mode 100755 t/t3440-rebase-trailer.sh\n>\n> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc\n> index 005caf6164..b2003b70d7 100644\n> --- a/Documentation/git-rebase.adoc\n> +++ b/Documentation/git-rebase.adoc\n> @@ -488,6 +488,13 @@ See also INCOMPATIBLE OPTIONS below.\n>  \tthat if `--interactive` is given then only commits marked to be\n>  \tpicked, edited or reworded will have the trailer added.\n>  +\n> +--trailer <trailer>::\n> +       Append the given trailer line(s) to every rebased commit\n> +       message, processed via linkgit:git-interpret-trailers[1].\n> +       When this option is present *rebase automatically implies*\n> +       `--force-rebase` so that fast‑forwarded commits are also\n> +       rewritten.\n> +\n\nYou’ve cut off the second paragraph of `--signoff`.  This should be\nadded after `See also` below.\n\nProbably also with an `=`:\n\n    --trailer=<trailer>::\n\n>  See also INCOMPATIBLE OPTIONS below.\n>\n>[snip]\n> +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\n> +. ./test-lib.sh\n> +. \"$TEST_DIRECTORY\"/lib-rebase.sh # test_commit_message, helpers\n> +\n> +create_expect() {\n> +\tcat >\"$1\" <<-EOF\n> +\t\t$2\n> +\n> +\t\tReviewed-by: Dev <dev@example.com>\n\nOne level of indentation seems enough?\n\n> +\tEOF\n> +}\n>[snip]\n\nLong line.\n\n> +\tgit cat-file commit HEAD | grep \"^Bug: 456\" &&\n> +\tgit cat-file commit HEAD | grep -v \"^Bug: 123\"\n> +'\n>[snip]\n"},{"id":"528761","messageId":"0995ba92-1feb-4103-b6bd-b792c13a01d2@app.fastmail.com","threadId":"64318","inReplyTo":"20251014122452.1851103-17-me@linux.beauty","subject":"Re: [PATCH v4 16/29] sequencer: add trailers to message before writing file","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-10-14T20:43:49Z","receivedAt":"2025-10-14T20:44:11Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Tue, Oct 14, 2025, at 14:24, Li Chen wrote:\n> Added trailer processing to the in-memory commit message\n> within do_pick_commit, ensuring fixup/squash commands\n> remain untouched before the message is written.\n>\n> Signed-off-by: Li Chen <chenl311@chinatelecom.cn>\n> ---\n>  sequencer.c | 19 ++++++++-----------\n>  trailer.c   |  4 ++--\n>  trailer.h   |  3 +++\n>  3 files changed, 13 insertions(+), 13 deletions(-)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index 5103ae786c..552e629e4f 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n>[snip]\n> -static int amend_strbuf_with_trailers(struct strbuf *buf,\n> -\t\t\t\t\t\t\t\t\t  const struct strvec *trailer_args)\n\nWeird indentation.  This looks like it needs clang-format.\n\n> +int amend_strbuf_with_trailers(struct strbuf *buf,\n> +       const struct strvec *trailer_args)\n>  {\n>[snip]\n"},{"id":"528762","messageId":"4eb06e60-e088-462b-9f1b-ca800af39b94@app.fastmail.com","threadId":"64318","inReplyTo":"20251014122452.1851103-18-me@linux.beauty","subject":"Re: [PATCH v4 17/29] tests: t3440: create expect files at point of use","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-10-14T20:41:33Z","receivedAt":"2025-10-14T20:44:27Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"Now you start to change the test suite/file that you created for this\nseries.  There shouldn’t be a need to do a test file-only patch/commit\nfor a fresh series.\n\nI saw in one of your patches that you removed `--keep-empty` from a test\nbecause “that is the default”.  I also saw Phillip’s comment somewhere\nthat said the same thing.\n\nThe goal with maturing series is not to add patches on top in each round\n(if that’s what you are doing).  It is to recreate them as if the series\nwas perfectly written to begin with; if one patch introduces\n`--trailers` and a test file, then there should be no need with\nfollow-up patches that improve the test file style, refactors it, and\nso on.\n\n> [PATCH v4 17/29] tests: t3440: create expect files at point of use\n\n`t3440` is enough.\n\nOn Tue, Oct 14, 2025, at 14:24, Li Chen wrote:\n> Created the expected trailer files within\n> the individual rebase tests that use them,\n> simplifying the shared history setup and\n> avoiding unused fixtures.\n\nThe max line length (for prose) is 72 and I don’t know of a minimum\n(41 here).\n\n>\n> Signed-off-by: Li Chen <chenl311@chinatelecom.cn>\n> ---\n>  t/t3440-rebase-trailer.sh | 12 ++++--------\n>  1 file changed, 4 insertions(+), 8 deletions(-)\n>[snip]\n"},{"id":"528811","messageId":"199e82ac06b.22809060320824.5985600477588171363@linux.beauty","threadId":"64318","inReplyTo":"4eb06e60-e088-462b-9f1b-ca800af39b94@app.fastmail.com","subject":"Re: [PATCH v4 17/29] tests: t3440: create expect files at point of use","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-15T13:58:59Z","receivedAt":"2025-10-15T13:59:14Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Hi Kristoffer,\n\nThanks for the review suggestions! I'll address them in the next version.\n\n ---- On Wed, 15 Oct 2025 04:41:33 +0800  Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com> wrote --- \n > Now you start to change the test suite/file that you created for this\n > series.  There shouldn’t be a need to do a test file-only patch/commit\n > for a fresh series.\n > \n > I saw in one of your patches that you removed `--keep-empty` from a test\n > because “that is the default”.  I also saw Phillip’s comment somewhere\n > that said the same thing.\n > \n > The goal with maturing series is not to add patches on top in each round\n > (if that’s what you are doing).  It is to recreate them as if the series\n > was perfectly written to begin with; if one patch introduces\n > `--trailers` and a test file, then there should be no need with\n > follow-up patches that improve the test file style, refactors it, and\n > so on.\n\nThanks for the tip. I split the changes into separate commits to ease review,\n as Phillip suggested in https://lore.kernel.org/git/d4c9f082-52be-48d9-b817-fcb8a72e1bd7@gmail.com/.\n\nIt seems I may have overdone it? If so, I'll try for a better balance in the next version.\n\nLi​\n\n"},{"id":"528813","messageId":"b4331d08-5105-4788-880e-af710cdcdd59@app.fastmail.com","threadId":"64318","inReplyTo":"199e82ac06b.22809060320824.5985600477588171363@linux.beauty","subject":"Re: [PATCH v4 17/29] tests: t3440: create expect files at point of use","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-10-15T14:02:00Z","receivedAt":"2025-10-15T14:02:22Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Wed, Oct 15, 2025, at 15:58, Li Chen wrote:\n>[snip]\n>  > (if that’s what you are doing).  It is to recreate them as if the\n> series\n>  > was perfectly written to begin with; if one patch introduces\n>  > `--trailers` and a test file, then there should be no need with\n>  > follow-up patches that improve the test file style, refactors it, and\n>  > so on.\n>\n> Thanks for the tip. I split the changes into separate commits to ease\n> review,\n>  as Phillip suggested in\n> https://lore.kernel.org/git/d4c9f082-52be-48d9-b817-fcb8a72e1bd7@gmail.com/.\n>\n> It seems I may have overdone it? If so, I'll try for a better balance\n> in the next version.\n\nThank you. It seems like I should have skimmed the previous rounds\nmore carefully. :)\n\nCheers\n"},{"id":"529247","messageId":"19a0633b726.5e87d7b93115820.5703222217950198481@linux.beauty","threadId":"64318","inReplyTo":"a0fae0cf-a1c0-4086-bcc0-a66f1aedf512@app.fastmail.com","subject":"Re: [PATCH v4 01/29] trailer: append trailers in-process and drop the fork to `interpret-trailers`","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-21T09:57:23Z","receivedAt":"2025-10-21T09:57:38Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Hi Kristoffer\n\n\n ---- On Wed, 15 Oct 2025 04:43:09 +0800  Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com> wrote --- \n > On Tue, Oct 14, 2025, at 14:24, Li Chen wrote:\n > > From: Li Chen <chenl311@chinatelecom.cn>\n > >\n > > Route all trailer insertion through trailer_process() and make\n > > builtin/interpret-trailers just do file I/O before calling into it.\n > > amend_file_with_trailers() now shares the same code path.\n > >\n > > This removes the fork/exec and tempfile juggling, cutting overhead and\n > > simplifying error handling. No functional change is intended. It also\n > \n > Why “is intended” instead of “No functional change.”?\n > \n > > centralizes logic to prepare for follow-up rebase --trailer patch.\n > >\n > > Signed-off-by: Li Chen <chenl311@chinatelecom.cn>\n > > ---\n > >  builtin/interpret-trailers.c | 116 ++++++++------------------------\n > >  trailer.c                    | 125 ++++++++++++++++++++++++++++++++---\n > >  trailer.h                    |  18 ++++-\n > >  3 files changed, 157 insertions(+), 102 deletions(-)\n > >[...]\n > >      new_trailers_clear(&trailers);\n > > diff --git a/trailer.c b/trailer.c\n > > index 911a81ed99..8aec466b5f 100644\n > > --- a/trailer.c\n > > +++ b/trailer.c\n > > @@ -1224,14 +1224,121 @@ void trailer_iterator_release(struct\n > > trailer_iterator *iter)\n > >      strbuf_release(&iter->key);\n > >  }\n > >\n > > -int amend_file_with_trailers(const char *path, const struct strvec\n > > *trailer_args)\n > > +static int amend_strbuf_with_trailers(struct strbuf *buf,\n > > +                                      const struct strvec *trailer_args)\n > \n > This needs Clang formatting.\n\nI apologize for the 4-space indentation in my previous codes due to my\neditor settings, which is not align with this project(treat tab as 8 space)\nI will use clang-format in the next version.\n\nRegards,\n\nLi​\n\n"},{"id":"529348","messageId":"19a0a0ed648.8a2243b33661009.8946324448572979174@linux.beauty","threadId":"64318","inReplyTo":"cbe93380-e145-4ebd-a213-928b8c3ba085@app.fastmail.com","subject":"Re: [PATCH v4 09/29] rebase: support --trailer","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-22T03:55:35Z","receivedAt":"2025-10-22T03:55:49Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Hi Kristoffer\n\n\n ---- On Wed, 15 Oct 2025 04:43:33 +0800  Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com> wrote --- \n > On Tue, Oct 14, 2025, at 14:24, Li Chen wrote:\n > > From: Li Chen <chenl311@chinatelecom.cn>\n > >\n > > Implement a new `--trailer <text>` option for `git rebase`\n > > (support merge backend only now), which appends arbitrary\n > > trailer lines to each rebased commit message.\n > >\n > > Reject it if the user passes an option that requires the\n > > apply backend (git am) since it lacks message‑filter/trailer\n > > hook. otherwise we can just use the merge backend.\n > >\n > > Automatically set REBASE_FORCE when any trailer is supplied.\n > >\n > > And reject invalid input before user edit the interactive file.\n > \n > s/edit/edits/\n > \n > >\n > > Signed-off-by: Li Chen <chenl311@chinatelecom.cn>\n > > ---\n > >  Documentation/git-rebase.adoc |  7 +++\n > >  builtin/rebase.c              | 89 +++++++++++++++++++++++++++++++++\n > >  sequencer.c                   | 13 +++++\n > >  sequencer.h                   |  4 +-\n > >  t/meson.build                 |  1 +\n > >  t/t3440-rebase-trailer.sh     | 94 +++++++++++++++++++++++++++++++++++\n > >  6 files changed, 207 insertions(+), 1 deletion(-)\n > >  create mode 100755 t/t3440-rebase-trailer.sh\n > >\n > > diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc\n > > index 005caf6164..b2003b70d7 100644\n > > --- a/Documentation/git-rebase.adoc\n > > +++ b/Documentation/git-rebase.adoc\n > > @@ -488,6 +488,13 @@ See also INCOMPATIBLE OPTIONS below.\n > >      that if `--interactive` is given then only commits marked to be\n > >      picked, edited or reworded will have the trailer added.\n > >  +\n > > +--trailer <trailer>::\n > > +       Append the given trailer line(s) to every rebased commit\n > > +       message, processed via linkgit:git-interpret-trailers[1].\n > > +       When this option is present *rebase automatically implies*\n > > +       `--force-rebase` so that fast‑forwarded commits are also\n > > +       rewritten.\n > > +\n > \n > You’ve cut off the second paragraph of `--signoff`.  This should be\n > added after `See also` below.\n \nThanks for catching this.\n\n > Probably also with an `=`:\n > \n >     --trailer=<trailer>::\n > \n\nIt would be added in v6; but both = and should work.\n\nRegards,\n\nLi​\n\n"},{"id":"529491","messageId":"bdba181a-915b-48d7-8e24-84fd08436576@gmail.com","threadId":"64318","inReplyTo":"199e82ac06b.22809060320824.5985600477588171363@linux.beauty","subject":"Re: [PATCH v4 17/29] tests: t3440: create expect files at point of use","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-10-23T09:04:34Z","receivedAt":"2025-10-23T09:04:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 15/10/2025 14:58, Li Chen wrote:\n> Hi Kristoffer,\n> \n> Thanks for the review suggestions! I'll address them in the next version.\n> \n>   ---- On Wed, 15 Oct 2025 04:41:33 +0800  Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com> wrote ---\n>   > Now you start to change the test suite/file that you created for this\n>   > series.  There shouldn’t be a need to do a test file-only patch/commit\n>   > for a fresh series.\n>   >\n>   > I saw in one of your patches that you removed `--keep-empty` from a test\n>   > because “that is the default”.  I also saw Phillip’s comment somewhere\n>   > that said the same thing.\n>   >\n>   > The goal with maturing series is not to add patches on top in each round\n>   > (if that’s what you are doing).  It is to recreate them as if the series\n>   > was perfectly written to begin with; if one patch introduces\n>   > `--trailers` and a test file, then there should be no need with\n>   > follow-up patches that improve the test file style, refactors it, and\n>   > so on.\n> \n> Thanks for the tip. I split the changes into separate commits to ease review,\n>   as Phillip suggested in https://lore.kernel.org/git/d4c9f082-52be-48d9-b817-fcb8a72e1bd7@gmail.com/.\n> \n> It seems I may have overdone it? If so, I'll try for a better balance in the next version.\nI asked that you did not refactor code at the same time as you moved it. \nI was expecting a handful of patches, not twenty-nine. The point that \nKristoffer makes about this patch is perfectly valid - you add a new \ntest and then correct it in a later patch. Instead you should correct \nthe test where it is introduced as Kirstoffer suggested. Looking at the \nfirst patch in this series there seems to have been some \nmiscommunication because it has exactly the same problem as V3. The code \nthat is moved from builtin/interpret-trailers.c to trailer.c is heavily \nrefactored at the same time. Variable names are changed and the code is \nrearranged so that \"git diff --color-moved \n--color-moved-ws=ignore-indentation-change\" detects barely any moved \nlines. I'll try and leave some more detailed feedback on the first few \npatches of V5 in the next few days.\n\nThanks\n\nPhillip\n\n"},{"id":"529778","messageId":"19a2a5aea22.54000694646975.5864990720344586426@linux.beauty","threadId":"64318","inReplyTo":"bdba181a-915b-48d7-8e24-84fd08436576@gmail.com","subject":"Re: [PATCH v4 17/29] tests: t3440: create expect files at point of use","fromName":"Li Chen","fromEmail":"me@linux.beauty","sentAt":"2025-10-28T10:26:32Z","receivedAt":"2025-10-28T10:26:47Z","isPatch":true,"sender":{"key":"me@linux.beauty","avatar":"https://avatars.githubusercontent.com/u/37442588?v=4"},"body":"Hi Phillip,\n\n ---- On Thu, 23 Oct 2025 17:04:34 +0800  Phillip Wood <phillip.wood123@gmail.com> wrote --- \n > On 15/10/2025 14:58, Li Chen wrote:\n > > Hi Kristoffer,\n > > \n > > Thanks for the review suggestions! I'll address them in the next version.\n > > \n > >   ---- On Wed, 15 Oct 2025 04:41:33 +0800  Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com> wrote ---\n > >   > Now you start to change the test suite/file that you created for this\n > >   > series.  There shouldn’t be a need to do a test file-only patch/commit\n > >   > for a fresh series.\n > >   >\n > >   > I saw in one of your patches that you removed `--keep-empty` from a test\n > >   > because “that is the default”.  I also saw Phillip’s comment somewhere\n > >   > that said the same thing.\n > >   >\n > >   > The goal with maturing series is not to add patches on top in each round\n > >   > (if that’s what you are doing).  It is to recreate them as if the series\n > >   > was perfectly written to begin with; if one patch introduces\n > >   > `--trailers` and a test file, then there should be no need with\n > >   > follow-up patches that improve the test file style, refactors it, and\n > >   > so on.\n > > \n > > Thanks for the tip. I split the changes into separate commits to ease review,\n > >   as Phillip suggested in https://lore.kernel.org/git/d4c9f082-52be-48d9-b817-fcb8a72e1bd7@gmail.com/.\n > > \n > > It seems I may have overdone it? If so, I'll try for a better balance in the next version.\n > I asked that you did not refactor code at the same time as you moved it. \n > I was expecting a handful of patches, not twenty-nine. The point that \n > Kristoffer makes about this patch is perfectly valid - you add a new \n > test and then correct it in a later patch. Instead you should correct \n > the test where it is introduced as Kirstoffer suggested. Looking at the \n > first patch in this series there seems to have been some \n > miscommunication because it has exactly the same problem as V3. The code \n > that is moved from builtin/interpret-trailers.c to trailer.c is heavily \n > refactored at the same time. Variable names are changed and the code is \n > rearranged so that \"git diff --color-moved \n > --color-moved-ws=ignore-indentation-change\" detects barely any moved \n > lines. I'll try and leave some more detailed feedback on the first few \n > patches of V5 in the next few days.\n\nI mistakenly misunderstood that you meant changes between each patchset version should be reflected by adding new patches. \nNow I understand that you mean refactoring the original code needs to be reflected in new patches for review. Thank you very \nThank you for telling me about --color-moved, and I found that git log also has this parameter. This option is very amazing.\n I will do as you requested in the next version. \n\nI sincerely apologize for the misunderstanding and wasted time.\n\nRegards,\nLi​\n\n"},{"id":"530131","messageId":"2405ed1a-8ae7-4b5c-abab-e81c8cd4ad10@gmail.com","threadId":"64318","inReplyTo":"19a2a5aea22.54000694646975.5864990720344586426@linux.beauty","subject":"Re: [PATCH v4 17/29] tests: t3440: create expect files at point of use","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-11-03T16:20:29Z","receivedAt":"2025-11-03T16:20:35Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Li\n\nOn 28/10/2025 10:26, Li Chen wrote:\n> \n> I mistakenly misunderstood that you meant changes between each patchset version should be reflected by adding new patches.\n> Now I understand that you mean refactoring the original code needs to be reflected in new patches for review. Thank you very\n> Thank you for telling me about --color-moved, and I found that git log also has this parameter. This option is very amazing.\n>   I will do as you requested in the next version.\n> \n> I sincerely apologize for the misunderstanding and wasted time.\n\nDon't worry, I left some more detailled comments on v5 at \nhttps://lore.kernel.org/git/7d12b046-365f-441c-af8e-8a39d61efbbd@gmail.com \nplease do feel free to let me know if you have any questions.\n\nThanks\n\nPhillip\n\n"}]}