trailer: libify a couple of functions

Move create_in_place_tempfile() and process_trailers() from builtin/interpret-trailers.c into trailer.c and expose it via trailer.h. This reverts most of ae0ec2e0e0b (trailer: move interpret_trailers() to interpret-trailers.c, 2024-03-01) and lets other call sites reuse the same trailer rewriting logic. Signed-off-by: Li Chen <me@linux.beauty> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Li Chen committed Mar 6, 2026 at 14:53 UTC a4fd4c523444f6b7d11b7af4dc6d790ac4fd8ec5
3 files changed +87 -70
builtin/interpret-trailers.c
+1 -70
@@ -93,39 +93,6 @@ static int parse_opt_parse(const struct option *opt, const char *arg,
93 return 0;
94 }
95
96 -
97 -static struct tempfile *create_in_place_tempfile(const char *file)
98 -{
99 - struct tempfile *tempfile = NULL;
100 - struct stat st;
101 - struct strbuf filename_template = STRBUF_INIT;
102 - const char *tail;
103 -
104 - if (stat(file, &st)) {
105 - error_errno(_("could not stat %s"), file);
106 - return NULL;
107 - }
108 - if (!S_ISREG(st.st_mode)) {
109 - error(_("file %s is not a regular file"), file);
110 - return NULL;
111 - }
112 - if (!(st.st_mode & S_IWUSR)) {
113 - error(_("file %s is not writable by user"), file);
114 - return NULL;
115 - }
116 - /* Create temporary file in the same directory as the original */
117 - tail = find_last_dir_sep(file);
118 - if (tail)
119 - strbuf_add(&filename_template, file, tail - file + 1);
120 - strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
121 -
122 - tempfile = mks_tempfile_m(filename_template.buf, st.st_mode);
123 -
124 - strbuf_release(&filename_template);
125 -
126 - return tempfile;
127 -}
128 -
96 static void read_input_file(struct strbuf *sb, const char *file)
97 {
98 if (file) {
@@ -138,42 +105,6 @@ static void read_input_file(struct strbuf *sb, const char *file)
105 strbuf_complete_line(sb);
106 }
107
141 -static void process_trailers(const struct process_trailer_options *opts,
142 - struct list_head *new_trailer_head,
143 - struct strbuf *input, struct strbuf *out)
144 -{
145 - LIST_HEAD(head);
146 - struct trailer_block *trailer_block;
147 -
148 - trailer_block = parse_trailers(opts, input->buf, &head);
149 -
150 - /* Print the lines before the trailer block */
151 - if (!opts->only_trailers)
152 - strbuf_add(out, input->buf, trailer_block_start(trailer_block));
153 -
154 - if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block))
155 - strbuf_addch(out, '\n');
156 -
157 - if (!opts->only_input) {
158 - LIST_HEAD(config_head);
159 - LIST_HEAD(arg_head);
160 - parse_trailers_from_config(&config_head);
161 - parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
162 - list_splice(&config_head, &arg_head);
163 - process_trailers_lists(&head, &arg_head);
164 - }
165 -
166 - /* Print trailer block. */
167 - format_trailers(opts, &head, out);
168 - free_trailers(&head);
169 -
170 - /* Print the lines after the trailer block as is. */
171 - if (!opts->only_trailers)
172 - strbuf_add(out, input->buf + trailer_block_end(trailer_block),
173 - input->len - trailer_block_end(trailer_block));
174 - trailer_block_release(trailer_block);
175 -}
176 -
108 static void interpret_trailers(const struct process_trailer_options *opts,
109 struct list_head *new_trailer_head,
110 const char *file)
@@ -188,7 +119,7 @@ static void interpret_trailers(const struct process_trailer_options *opts,
119 read_input_file(&input, file);
120
121 if (opts->in_place) {
191 - tempfile = create_in_place_tempfile(file);
122 + tempfile = trailer_create_in_place_tempfile(file);
123 if (!tempfile)
124 die(NULL);
125 fd = tempfile->fd;
trailer.c
+70
@@ -9,6 +9,8 @@
9 #include "commit.h"
10 #include "trailer.h"
11 #include "list.h"
12 +#include "tempfile.h"
13 +
14 /*
15 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
16 */
@@ -1224,6 +1226,38 @@ void trailer_iterator_release(struct trailer_iterator *iter)
1226 strbuf_release(&iter->key);
1227 }
1228
1229 +struct tempfile *trailer_create_in_place_tempfile(const char *file)
1230 +{
1231 + struct tempfile *tempfile = NULL;
1232 + struct stat st;
1233 + struct strbuf filename_template = STRBUF_INIT;
1234 + const char *tail;
1235 +
1236 + if (stat(file, &st)) {
1237 + error_errno(_("could not stat %s"), file);
1238 + return NULL;
1239 + }
1240 + if (!S_ISREG(st.st_mode)) {
1241 + error(_("file %s is not a regular file"), file);
1242 + return NULL;
1243 + }
1244 + if (!(st.st_mode & S_IWUSR)) {
1245 + error(_("file %s is not writable by user"), file);
1246 + return NULL;
1247 + }
1248 + /* Create temporary file in the same directory as the original */
1249 + tail = find_last_dir_sep(file);
1250 + if (tail)
1251 + strbuf_add(&filename_template, file, tail - file + 1);
1252 + strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
1253 +
1254 + tempfile = mks_tempfile_m(filename_template.buf, st.st_mode);
1255 +
1256 + strbuf_release(&filename_template);
1257 +
1258 + return tempfile;
1259 +}
1260 +
1261 int amend_file_with_trailers(const char *path, const struct strvec *trailer_args)
1262 {
1263 struct child_process run_trailer = CHILD_PROCESS_INIT;
@@ -1235,3 +1269,39 @@ int amend_file_with_trailers(const char *path, const struct strvec *trailer_args
1269 strvec_pushv(&run_trailer.args, trailer_args->v);
1270 return run_command(&run_trailer);
1271 }
1272 +
1273 +void process_trailers(const struct process_trailer_options *opts,
1274 + struct list_head *new_trailer_head,
1275 + struct strbuf *input, struct strbuf *out)
1276 +{
1277 + LIST_HEAD(head);
1278 + struct trailer_block *trailer_block;
1279 +
1280 + trailer_block = parse_trailers(opts, input->buf, &head);
1281 +
1282 + /* Print the lines before the trailer block */
1283 + if (!opts->only_trailers)
1284 + strbuf_add(out, input->buf, trailer_block_start(trailer_block));
1285 +
1286 + if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block))
1287 + strbuf_addch(out, '\n');
1288 +
1289 + if (!opts->only_input) {
1290 + LIST_HEAD(config_head);
1291 + LIST_HEAD(arg_head);
1292 + parse_trailers_from_config(&config_head);
1293 + parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
1294 + list_splice(&config_head, &arg_head);
1295 + process_trailers_lists(&head, &arg_head);
1296 + }
1297 +
1298 + /* Print trailer block. */
1299 + format_trailers(opts, &head, out);
1300 + free_trailers(&head);
1301 +
1302 + /* Print the lines after the trailer block as is. */
1303 + if (!opts->only_trailers)
1304 + strbuf_add(out, input->buf + trailer_block_end(trailer_block),
1305 + input->len - trailer_block_end(trailer_block));
1306 + trailer_block_release(trailer_block);
1307 +}
trailer.h
+16
@@ -202,4 +202,20 @@ void trailer_iterator_release(struct trailer_iterator *iter);
202 */
203 int amend_file_with_trailers(const char *path, const struct strvec *trailer_args);
204
205 +/*
206 + * Create a tempfile ""git-interpret-trailers-XXXXXX" in the same
207 + * directory as file.
208 + */
209 +struct tempfile *trailer_create_in_place_tempfile(const char *file);
210 +
211 +/*
212 + * Rewrite the contents of input by processing its trailer block according to
213 + * opts and (optionally) appending trailers from new_trailer_head.
214 + *
215 + * The rewritten message is appended to out (callers should strbuf_reset()
216 + * first if needed).
217 + */
218 +void process_trailers(const struct process_trailer_options *opts,
219 + struct list_head *new_trailer_head,
220 + struct strbuf *input, struct strbuf *out);
221 #endif /* TRAILER_H */