|
7 | 7 | #include "string-list.h" |
8 | 8 | #include "run-command.h" |
9 | 9 | #include "commit.h" |
| 10 | +#include "strvec.h" |
10 | 11 | #include "trailer.h" |
11 | 12 | #include "list.h" |
12 | 13 | #include "tempfile.h" |
@@ -774,6 +775,35 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head, |
774 | 775 | free(cl_separators); |
775 | 776 | } |
776 | 777 |
|
| 778 | +int validate_trailer_args(const struct strvec *cli_args) |
| 779 | +{ |
| 780 | + char *cl_separators; |
| 781 | + int ret = 0; |
| 782 | + |
| 783 | + trailer_config_init(); |
| 784 | + |
| 785 | + cl_separators = xstrfmt("=%s", separators); |
| 786 | + |
| 787 | + for (size_t i = 0; i < cli_args->nr; i++) { |
| 788 | + const char *txt = cli_args->v[i]; |
| 789 | + ssize_t separator_pos; |
| 790 | + |
| 791 | + if (!*txt) { |
| 792 | + ret = error(_("empty --trailer argument")); |
| 793 | + goto out; |
| 794 | + } |
| 795 | + separator_pos = find_separator(txt, cl_separators); |
| 796 | + if (separator_pos == 0) { |
| 797 | + ret = error(_("invalid trailer '%s': missing key before separator"), |
| 798 | + txt); |
| 799 | + goto out; |
| 800 | + } |
| 801 | + } |
| 802 | +out: |
| 803 | + free(cl_separators); |
| 804 | + return ret; |
| 805 | +} |
| 806 | + |
777 | 807 | static const char *next_line(const char *str) |
778 | 808 | { |
779 | 809 | const char *nl = strchrnul(str, '\n'); |
@@ -1258,16 +1288,101 @@ struct tempfile *trailer_create_in_place_tempfile(const char *file) |
1258 | 1288 | return tempfile; |
1259 | 1289 | } |
1260 | 1290 |
|
1261 | | -int amend_file_with_trailers(const char *path, const struct strvec *trailer_args) |
| 1291 | +int amend_strbuf_with_trailers(struct strbuf *buf, |
| 1292 | + const struct strvec *trailer_args) |
1262 | 1293 | { |
1263 | | - struct child_process run_trailer = CHILD_PROCESS_INIT; |
1264 | | - |
1265 | | - run_trailer.git_cmd = 1; |
1266 | | - strvec_pushl(&run_trailer.args, "interpret-trailers", |
1267 | | - "--in-place", "--no-divider", |
1268 | | - path, NULL); |
1269 | | - strvec_pushv(&run_trailer.args, trailer_args->v); |
1270 | | - return run_command(&run_trailer); |
| 1294 | + struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; |
| 1295 | + LIST_HEAD(new_trailer_head); |
| 1296 | + struct strbuf out = STRBUF_INIT; |
| 1297 | + size_t i; |
| 1298 | + int ret = 0; |
| 1299 | + |
| 1300 | + opts.no_divider = 1; |
| 1301 | + |
| 1302 | + for (i = 0; i < trailer_args->nr; i++) { |
| 1303 | + const char *text = trailer_args->v[i]; |
| 1304 | + struct new_trailer_item *item; |
| 1305 | + |
| 1306 | + if (!*text) { |
| 1307 | + ret = error(_("empty --trailer argument")); |
| 1308 | + goto out; |
| 1309 | + } |
| 1310 | + item = xcalloc(1, sizeof(*item)); |
| 1311 | + item->text = xstrdup(text); |
| 1312 | + list_add_tail(&item->list, &new_trailer_head); |
| 1313 | + } |
| 1314 | + |
| 1315 | + process_trailers(&opts, &new_trailer_head, buf, &out); |
| 1316 | + |
| 1317 | + strbuf_swap(buf, &out); |
| 1318 | +out: |
| 1319 | + strbuf_release(&out); |
| 1320 | + free_trailers(&new_trailer_head); |
| 1321 | + |
| 1322 | + return ret; |
| 1323 | +} |
| 1324 | + |
| 1325 | +static int write_file_in_place(const char *path, const struct strbuf *buf) |
| 1326 | +{ |
| 1327 | + struct tempfile *tempfile = trailer_create_in_place_tempfile(path); |
| 1328 | + if (!tempfile) |
| 1329 | + return -1; |
| 1330 | + |
| 1331 | + if (write_in_full(tempfile->fd, buf->buf, buf->len) < 0) |
| 1332 | + return error_errno(_("could not write to temporary file")); |
| 1333 | + |
| 1334 | + if (rename_tempfile(&tempfile, path)) |
| 1335 | + return error_errno(_("could not rename temporary file to %s"), path); |
| 1336 | + |
| 1337 | + return 0; |
| 1338 | +} |
| 1339 | + |
| 1340 | +int amend_file_with_trailers(const char *path, |
| 1341 | + const struct strvec *trailer_args) |
| 1342 | +{ |
| 1343 | + struct strbuf buf = STRBUF_INIT; |
| 1344 | + struct strvec stripped_trailer_args = STRVEC_INIT; |
| 1345 | + int ret = 0; |
| 1346 | + size_t i; |
| 1347 | + |
| 1348 | + if (!trailer_args) |
| 1349 | + BUG("amend_file_with_trailers called with NULL trailer_args"); |
| 1350 | + if (!trailer_args->nr) |
| 1351 | + return 0; |
| 1352 | + |
| 1353 | + for (i = 0; i < trailer_args->nr; i++) { |
| 1354 | + const char *txt = trailer_args->v[i]; |
| 1355 | + |
| 1356 | + /* |
| 1357 | + * Historically amend_file_with_trailers() passed its arguments |
| 1358 | + * to "git interpret-trailers", which expected argv entries in |
| 1359 | + * "--trailer=<trailer>" form. Continue to accept those for |
| 1360 | + * existing callers, but pass only the value portion to the |
| 1361 | + * in-process implementation. |
| 1362 | + */ |
| 1363 | + skip_prefix(txt, "--trailer=", &txt); |
| 1364 | + if (!*txt) { |
| 1365 | + ret = error(_("empty --trailer argument")); |
| 1366 | + goto out; |
| 1367 | + } |
| 1368 | + strvec_push(&stripped_trailer_args, txt); |
| 1369 | + } |
| 1370 | + |
| 1371 | + if (validate_trailer_args(&stripped_trailer_args)) { |
| 1372 | + ret = -1; |
| 1373 | + goto out; |
| 1374 | + } |
| 1375 | + if (strbuf_read_file(&buf, path, 0) < 0) |
| 1376 | + ret = error_errno(_("could not read '%s'"), path); |
| 1377 | + else |
| 1378 | + amend_strbuf_with_trailers(&buf, &stripped_trailer_args); |
| 1379 | + |
| 1380 | + if (!ret) |
| 1381 | + ret = write_file_in_place(path, &buf); |
| 1382 | +out: |
| 1383 | + strvec_clear(&stripped_trailer_args); |
| 1384 | + strbuf_release(&buf); |
| 1385 | + return ret; |
1271 | 1386 | } |
1272 | 1387 |
|
1273 | 1388 | void process_trailers(const struct process_trailer_options *opts, |
|
0 commit comments