From: Li Chen <me@linux.beauty>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>,
Phillip Wood <phillip.wood@dunelm.org.uk>,
Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>,
Li Chen <me@linux.beauty>
Subject: [PATCH v7 3/5] trailer: append trailers without fork/exec
Date: Tue, 24 Feb 2026 15:05:49 +0800 [thread overview]
Message-ID: <20260224070552.148591-4-me@linux.beauty> (raw)
In-Reply-To: <20260224070552.148591-1-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.
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>
---
v7:
Drop wrapper.c/h and validate trailer args via validate_trailer_args().
Rewrite the target file via tempfile+rename to preserve --in-place semantics.
builtin/interpret-trailers.c | 4 +-
trailer.c | 162 +++++++++++++++++++++++++++++++++--
trailer.h | 27 +++++-
3 files changed, 179 insertions(+), 14 deletions(-)
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 1354109e0f..d4aff68746 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -144,8 +144,6 @@ static void interpret_trailers(const struct process_trailer_options *opts,
struct strbuf out = STRBUF_INIT;
FILE *outfile = stdout;
- trailer_config_init();
-
read_input_file(&input, file);
if (opts->in_place)
@@ -203,6 +201,8 @@ int cmd_interpret_trailers(int argc,
git_interpret_trailers_usage,
options);
+ trailer_config_init();
+
if (argc) {
int i;
for (i = 0; i < argc; i++)
diff --git a/trailer.c b/trailer.c
index 0c9200506d..8e87d185d9 100644
--- a/trailer.c
+++ b/trailer.c
@@ -7,8 +7,11 @@
#include "string-list.h"
#include "run-command.h"
#include "commit.h"
+#include "strvec.h"
+#include "tempfile.h"
#include "trailer.h"
#include "list.h"
+
/*
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
@@ -772,6 +775,30 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head,
free(cl_separators);
}
+void validate_trailer_args(const struct strvec *cli_args)
+{
+ char *cl_separators;
+
+ 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)
+ die(_("empty --trailer argument"));
+
+ separator_pos = find_separator(txt, cl_separators);
+ if (separator_pos == 0)
+ die(_("invalid trailer '%s': missing key before separator"),
+ txt);
+ }
+
+ free(cl_separators);
+}
+
static const char *next_line(const char *str)
{
const char *nl = strchrnul(str, '\n');
@@ -1224,16 +1251,133 @@ void trailer_iterator_release(struct trailer_iterator *iter)
strbuf_release(&iter->key);
}
-int amend_file_with_trailers(const char *path, const struct strvec *trailer_args)
+static void new_trailer_items_clear(struct list_head *items)
+{
+ while (!list_empty(items)) {
+ struct new_trailer_item *item =
+ list_first_entry(items, struct new_trailer_item, list);
+ list_del(&item->list);
+ free(item);
+ }
+}
+
+void 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;
+
+ 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)
+ die(_("empty --trailer argument"));
+ item = xcalloc(1, sizeof(*item));
+ item->text = text;
+ list_add_tail(&item->list, &new_trailer_head);
+ }
+
+ trailer_config_init();
+ process_trailers(&opts, &new_trailer_head, buf, &out);
+
+ strbuf_swap(buf, &out);
+ strbuf_release(&out);
+
+ new_trailer_items_clear(&new_trailer_head);
+}
+
+static int write_file_in_place(const char *path, const struct strbuf *buf)
+{
+ struct stat st;
+ struct strbuf filename_template = STRBUF_INIT;
+ const char *tail;
+ struct tempfile *tempfile;
+ FILE *outfile;
+
+ if (stat(path, &st))
+ return error_errno(_("could not stat %s"), path);
+ if (!S_ISREG(st.st_mode))
+ return error(_("file %s is not a regular file"), path);
+ if (!(st.st_mode & S_IWUSR))
+ return error(_("file %s is not writable by user"), path);
+
+ /* Create temporary file in the same directory as the original */
+ tail = strrchr(path, '/');
+ if (tail)
+ strbuf_add(&filename_template, path, tail - path + 1);
+ strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
+
+ tempfile = mks_tempfile_sm(filename_template.buf, 0, st.st_mode);
+ strbuf_release(&filename_template);
+ if (!tempfile)
+ return error_errno(_("could not create temporary file"));
+
+ outfile = fdopen_tempfile(tempfile, "w");
+ if (!outfile) {
+ int saved_errno = errno;
+ delete_tempfile(&tempfile);
+ errno = saved_errno;
+ return error_errno(_("could not open temporary file"));
+ }
+
+ if (buf->len && fwrite(buf->buf, 1, buf->len, outfile) < buf->len) {
+ int saved_errno = errno;
+ delete_tempfile(&tempfile);
+ errno = saved_errno;
+ 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 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);
+ 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)
+ die(_("empty --trailer argument"));
+ strvec_push(&stripped_trailer_args, txt);
+ }
+
+ 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);
+
+ 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 531fa1a13f..d05dab050b 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);
+void validate_trailer_args(const struct strvec *cli_args);
+
void process_trailers_lists(struct list_head *head,
struct list_head *arg_head);
@@ -196,12 +198,31 @@ 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).
+ */
+void 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);
+/*
+ * Rewrite the contents of input by processing its trailer block according to
+ * opts and (optionally) appending trailers from new_trailer_head.
+ *
+ * The rewritten message is appended to out (callers should strbuf_reset()
+ * first if needed).
+ */
void process_trailers(const struct process_trailer_options *opts,
struct list_head *new_trailer_head,
struct strbuf *input, struct strbuf *out);
--
2.52.0
next prev parent reply other threads:[~2026-02-24 7:06 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-24 7:05 [PATCH v7 0/5] rebase: support --trailer Li Chen
2026-02-24 7:05 ` [PATCH v7 1/5] interpret-trailers: factor trailer rewriting Li Chen
2026-03-02 14:56 ` Phillip Wood
2026-03-02 15:00 ` Li Chen
2026-02-24 7:05 ` [PATCH v7 2/5] trailer: move process_trailers to trailer.h Li Chen
2026-03-02 14:56 ` phillip.wood123
2026-02-24 7:05 ` Li Chen [this message]
2026-03-02 14:56 ` [PATCH v7 3/5] trailer: append trailers without fork/exec Phillip Wood
2026-02-24 7:05 ` [PATCH v7 4/5] commit, tag: parse --trailer with OPT_STRVEC Li Chen
2026-03-02 14:56 ` Phillip Wood
2026-02-24 7:05 ` [PATCH v7 5/5] rebase: support --trailer Li Chen
2026-03-03 15:05 ` Phillip Wood
2026-03-03 20:36 ` Kristoffer Haugsbakk
2026-03-03 21:18 ` Junio C Hamano
2026-03-04 15:53 ` Phillip Wood
2026-03-04 17:22 ` Junio C Hamano
2026-02-26 16:52 ` [PATCH v7 0/5] " Junio C Hamano
2026-02-26 18:15 ` Phillip Wood
2026-02-26 21:12 ` Kristoffer Haugsbakk
2026-03-04 14:29 ` Phillip Wood
2026-03-05 13:49 ` Li Chen
2026-03-06 14:55 ` Phillip Wood
2026-03-06 14:53 ` [PATCH v8 0/6] " Phillip Wood
2026-03-06 14:53 ` [PATCH v8 1/6] interpret-trailers: factor trailer rewriting Phillip Wood
2026-03-06 21:04 ` Junio C Hamano
2026-03-09 10:36 ` Phillip Wood
2026-03-06 14:53 ` [PATCH v8 2/6] interpret-trailers: refactor create_in_place_tempfile() Phillip Wood
2026-03-06 21:05 ` Junio C Hamano
2026-03-06 14:53 ` [PATCH v8 3/6] trailer: libify a couple of functions Phillip Wood
2026-03-06 14:53 ` [PATCH v8 4/6] trailer: append trailers without fork/exec Phillip Wood
2026-03-06 14:53 ` [PATCH v8 5/6] commit, tag: parse --trailer with OPT_STRVEC Phillip Wood
2026-03-06 14:53 ` [PATCH v8 6/6] rebase: support --trailer Phillip Wood
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260224070552.148591-4-me@linux.beauty \
--to=me@linux.beauty \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=phillip.wood@dunelm.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox