git/list[1] front-page[2] threads[3] people[4] search[5] about
wed 2026-10-07 17:26 UTC

[PATCH v8 4/6] trailer: append trailers without fork/exec

From
PWPhillip Wood <phillip.wood123@gmail.com>
Date
Mar 6, 2026, 14:53 UTC
Message-ID
<1f24917eb64091fd0a756ae5c5d26db1ca5d6e9a.1772808594.git.phillip.wood@dunelm.org.uk>
In-Reply-To
<cover.1772808594.git.phillip.wood@dunelm.org.uk>
From: Li Chen <me@linux.beauty>

Introduce amend_strbuf_with_trailers() to apply trailer additions to a message buffer via process_trailers(), avoiding the need to run git interpret-trailers as a child process.

Update amend_file_with_trailers() to use the in-process helper and rewrite the target file via tempfile+rename, preserving the previous in-place semantics. As the trailers are no longer added in a separate process and trailer_config_init() die()s on missing config values it is called early on in cmd_commit() and cmd_tag() so that they die() early before writing the message file. The trailer arguments are now also sanity checked.

Keep existing callers unchanged by continuing to accept argv-style --trailer=<trailer> entries and stripping the prefix before feeding the in-process implementation.

Signed-off-by: Li Chen <me@linux.beauty>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c |   3 ++
 builtin/tag.c    |   3 ++
 trailer.c        | 135 +++++++++++++++++++++++++++++++++++++++++++----
 trailer.h        |  20 +++++--
 4 files changed, 148 insertions(+), 13 deletions(-)
diff --git a/builtin/commit.c b/builtin/commit.c
index 9e3a09d532b..eb9013995c9 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1820,6 +1820,9 @@ int cmd_commit(int argc,
 	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
 					  builtin_commit_usage,
 					  prefix, current_head, &s);
+	if (trailer_args.nr)
+		trailer_config_init();
+
 	if (verbose == -1)
 		verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
 
diff --git a/builtin/tag.c b/builtin/tag.c
index aeb04c487fe..68b581a9c26 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -568,6 +568,9 @@ int cmd_tag(int argc,
 	if (cmdmode == 'l')
 		setup_auto_pager("tag", 1);
 
+	if (trailer_args.nr)
+		trailer_config_init();
+
 	if (opt.sign == -1)
 		opt.sign = cmdmode ? 0 : config_sign_tag > 0;
 
diff --git a/trailer.c b/trailer.c
index 163018483a5..5eab4fa549d 100644
--- a/trailer.c
+++ b/trailer.c
@@ -7,6 +7,7 @@
 #include "string-list.h"
 #include "run-command.h"
 #include "commit.h"
+#include "strvec.h"
 #include "trailer.h"
 #include "list.h"
 #include "tempfile.h"
@@ -774,6 +775,35 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head,
 	free(cl_separators);
 }
 
+int validate_trailer_args(const struct strvec *cli_args)
+{
+	char *cl_separators;
+	int ret = 0;
+
+	trailer_config_init();
+
+	cl_separators = xstrfmt("=%s", separators);
+
+	for (size_t i = 0; i < cli_args->nr; i++) {
+		const char *txt = cli_args->v[i];
+		ssize_t separator_pos;
+
+		if (!*txt) {
+			ret = error(_("empty --trailer argument"));
+			goto out;
+		}
+		separator_pos = find_separator(txt, cl_separators);
+		if (separator_pos == 0) {
+			ret = error(_("invalid trailer '%s': missing key before separator"),
+				    txt);
+			goto out;
+		}
+	}
+out:
+	free(cl_separators);
+	return ret;
+}
+
 static const char *next_line(const char *str)
 {
 	const char *nl = strchrnul(str, '\n');
@@ -1258,16 +1288,101 @@ struct tempfile *trailer_create_in_place_tempfile(const char *file)
 	return tempfile;
 }
 
-int amend_file_with_trailers(const char *path, const struct strvec *trailer_args)
-{
-	struct child_process run_trailer = CHILD_PROCESS_INIT;
-
-	run_trailer.git_cmd = 1;
-	strvec_pushl(&run_trailer.args, "interpret-trailers",
-		     "--in-place", "--no-divider",
-		     path, NULL);
-	strvec_pushv(&run_trailer.args, trailer_args->v);
-	return run_command(&run_trailer);
+int amend_strbuf_with_trailers(struct strbuf *buf,
+				const struct strvec *trailer_args)
+{
+	struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+	LIST_HEAD(new_trailer_head);
+	struct strbuf out = STRBUF_INIT;
+	size_t i;
+	int ret = 0;
+
+	opts.no_divider = 1;
+
+	for (i = 0; i < trailer_args->nr; i++) {
+		const char *text = trailer_args->v[i];
+		struct new_trailer_item *item;
+
+		if (!*text) {
+			ret = error(_("empty --trailer argument"));
+			goto out;
+		}
+		item = xcalloc(1, sizeof(*item));
+		item->text = xstrdup(text);
+		list_add_tail(&item->list, &new_trailer_head);
+	}
+
+	process_trailers(&opts, &new_trailer_head, buf, &out);
+
+	strbuf_swap(buf, &out);
+out:
+	strbuf_release(&out);
+	free_trailers(&new_trailer_head);
+
+	return ret;
+}
+
+static int write_file_in_place(const char *path, const struct strbuf *buf)
+{
+	struct tempfile *tempfile = trailer_create_in_place_tempfile(path);
+	if (!tempfile)
+		return -1;
+
+	if (write_in_full(tempfile->fd, buf->buf, buf->len) < 0)
+		return error_errno(_("could not write to temporary file"));
+
+	if (rename_tempfile(&tempfile, path))
+		return error_errno(_("could not rename temporary file to %s"), path);
+
+	return 0;
+}
+
+int amend_file_with_trailers(const char *path,
+			     const struct strvec *trailer_args)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strvec stripped_trailer_args = STRVEC_INIT;
+	int ret = 0;
+	size_t i;
+
+	if (!trailer_args)
+		BUG("amend_file_with_trailers called with NULL trailer_args");
+	if (!trailer_args->nr)
+		return 0;
+
+	for (i = 0; i < trailer_args->nr; i++) {
+		const char *txt = trailer_args->v[i];
+
+		/*
+		 * Historically amend_file_with_trailers() passed its arguments
+		 * to "git interpret-trailers", which expected argv entries in
+		 * "--trailer=<trailer>" form. Continue to accept those for
+		 * existing callers, but pass only the value portion to the
+		 * in-process implementation.
+		 */
+		skip_prefix(txt, "--trailer=", &txt);
+		if (!*txt) {
+			ret = error(_("empty --trailer argument"));
+			goto out;
+		}
+		strvec_push(&stripped_trailer_args, txt);
+	}
+
+	if (validate_trailer_args(&stripped_trailer_args)) {
+		ret = -1;
+		goto out;
+	}
+	if (strbuf_read_file(&buf, path, 0) < 0)
+		ret = error_errno(_("could not read '%s'"), path);
+	else
+		amend_strbuf_with_trailers(&buf, &stripped_trailer_args);
+
+	if (!ret)
+		ret = write_file_in_place(path, &buf);
+out:
+	strvec_clear(&stripped_trailer_args);
+	strbuf_release(&buf);
+	return ret;
 }
 
 void process_trailers(const struct process_trailer_options *opts,
diff --git a/trailer.h b/trailer.h
index 7fd2564e035..3c5d9a6e199 100644
--- a/trailer.h
+++ b/trailer.h
@@ -68,6 +68,8 @@ void parse_trailers_from_config(struct list_head *config_head);
 void parse_trailers_from_command_line_args(struct list_head *arg_head,
 					   struct list_head *new_trailer_head);
 
+int validate_trailer_args(const struct strvec *cli_args);
+
 void process_trailers_lists(struct list_head *head,
 			    struct list_head *arg_head);
 
@@ -196,9 +198,21 @@ int trailer_iterator_advance(struct trailer_iterator *iter);
 void trailer_iterator_release(struct trailer_iterator *iter);
 
 /*
- * Augment a file to add trailers to it by running git-interpret-trailers.
- * This calls run_command() and its return value is the same (i.e. 0 for
- * success, various non-zero for other errors). See run-command.h.
+ * Append trailers specified in trailer_args to buf in-place.
+ *
+ * Each element of trailer_args should be in the same format as the value
+ * accepted by --trailer=<trailer> (i.e., without the --trailer= prefix).
+ */
+int amend_strbuf_with_trailers(struct strbuf *buf,
+				const struct strvec *trailer_args);
+
+/*
+ * Augment a file by appending trailers specified in trailer_args.
+ *
+ * Each element of trailer_args should be an argv-style --trailer=<trailer>
+ * option (i.e., including the --trailer= prefix).
+ *
+ * Returns 0 on success or a non-zero error code on failure.
  */
 int amend_file_with_trailers(const char *path, const struct strvec *trailer_args);
 
-- 
2.52.0.362.g884e03848a9
Previous: Phillip WoodNext: Phillip Wood
Message 26 of 32 in “rebase: support --trailer”
  1. 0/5 rebase: support --trailerLi Chen, Feb 24, 2026
  2. 1/5 interpret-trailers: factor trailer rewritingLi Chen, Feb 24, 2026
  3. 2/5 trailer: move process_trailers to trailer.hLi Chen, Feb 24, 2026
  4. 3/5 trailer: append trailers without fork/execLi Chen, Feb 24, 2026
  5. 4/5 commit, tag: parse --trailer with OPT_STRVECLi Chen, Feb 24, 2026
  6. 5/5 rebase: support --trailerLi Chen, Feb 24, 2026
  7. Junio C HamanoFeb 26, 2026
  8. Phillip WoodFeb 26, 2026
  9. Kristoffer HaugsbakkFeb 26, 2026
  10. Phillip WoodMar 2, 2026
  11. phillip.wood123@gmail.comMar 2, 2026
  12. Phillip WoodMar 2, 2026
  13. Phillip WoodMar 2, 2026
  14. Li ChenMar 2, 2026
  15. Phillip WoodMar 3, 2026
  16. Kristoffer HaugsbakkMar 3, 2026
  17. Junio C HamanoMar 3, 2026
  18. Phillip WoodMar 4, 2026
  19. Phillip WoodMar 4, 2026
  20. Junio C HamanoMar 4, 2026
  21. Li ChenMar 5, 2026
  22. 0/6 rebase: support --trailerPhillip Wood, Mar 6, 2026
  23. 1/6 interpret-trailers: factor trailer rewritingPhillip Wood, Mar 6, 2026
  24. 2/6 interpret-trailers: refactor create_in_place_tempfile()Phillip Wood, Mar 6, 2026
  25. 3/6 trailer: libify a couple of functionsPhillip Wood, Mar 6, 2026
  26. 4/6 trailer: append trailers without fork/execPhillip Wood, Mar 6, 2026
  27. 5/6 commit, tag: parse --trailer with OPT_STRVECPhillip Wood, Mar 6, 2026
  28. 6/6 rebase: support --trailerPhillip Wood, Mar 6, 2026
  29. Phillip WoodMar 6, 2026
  30. Junio C HamanoMar 6, 2026
  31. Junio C HamanoMar 6, 2026
  32. Phillip WoodMar 9, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.