{"thread":{"id":"58704","subject":"[PATCH 0/8] run-command: remove run_command_v_*()","startedAt":"2022-10-27T16:30:58Z","lastAt":"2022-10-31T15:30:22Z","messageCount":49,"participants":["René Scharfe","Junio C Hamano","Jeff King","Ævar Arnfjörð Bjarmason","Taylor Blau"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"465871","messageId":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","threadId":"58704","inReplyTo":null,"subject":"[PATCH 0/8] run-command: remove run_command_v_*()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:30:36Z","receivedAt":"2022-10-27T16:30:58Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Replace the convenience functions run_command_v_opt() et. al. and use\nstruct child_process and run_command() directly instead, for an overall\ncode reduction and a simpler and more flexible API that allows creating\nargument lists without magic numbers and reduced risk of memory leaks.\n\nThis is a broken-out and polished version of the original scratch at\nhttps://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\n\nÆvar Arnfjörð Bjarmason (1):\n  merge: remove always-the-same \"verbose\" arguments\n\nRené Scharfe (7):\n  bisect--helper: factor out do_bisect_run()\n  use child_process members \"args\" and \"env\" directly\n  use child_process member \"args\" instead of string array variable\n  replace and remove run_command_v_opt_cd_env()\n  replace and remove run_command_v_opt_tr2()\n  replace and remove run_command_v_opt_cd_env_tr2()\n  replace and remove run_command_v_opt()\n\n add-interactive.c        |   9 ++-\n bisect.c                 |  12 ++--\n builtin/add.c            |  19 +++--\n builtin/am.c             |  12 ++--\n builtin/bisect--helper.c |  68 +++++++++---------\n builtin/clone.c          |  41 ++++++-----\n builtin/difftool.c       |  24 ++++---\n builtin/fetch.c          |   9 ++-\n builtin/gc.c             |  55 ++++++++++-----\n builtin/merge-index.c    |   4 +-\n builtin/merge.c          |  53 ++++++--------\n builtin/pull.c           | 147 +++++++++++++++++++--------------------\n builtin/remote.c         |  40 +++++------\n compat/mingw.c           |  11 +--\n diff.c                   |  27 ++++---\n fsmonitor-ipc.c          |  10 ++-\n git.c                    |  15 ++--\n ll-merge.c               |   7 +-\n merge.c                  |  18 ++---\n run-command.c            |  35 ----------\n run-command.h            |  33 +--------\n scalar.c                 |  13 ++--\n sequencer.c              |  32 ++++-----\n shell.c                  |  17 +++--\n t/helper/test-fake-ssh.c |   7 +-\n t/helper/test-trace2.c   |   4 +-\n tmp-objdir.h             |   4 +-\n 27 files changed, 344 insertions(+), 382 deletions(-)\n\n--\n2.38.1\n"},{"id":"465872","messageId":"0cae80c0-2576-8be7-48f7-06a0a135961f@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 1/8] merge: remove always-the-same \"verbose\" arguments","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:35:02Z","receivedAt":"2022-10-27T16:35:25Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nSimplify the code that builds the arguments for the \"read-tree\"\ninvocation in reset_hard() and read_empty() to remove the \"verbose\"\nparameter.\n\nBefore 172b6428d06 (do not overwrite untracked during merge from\nunborn branch, 2010-11-14) there was a \"reset_hard()\" function that\nwould be called in two places, one of those passed a \"verbose=1\", the\nother a \"verbose=0\".\n\nAfter 172b6428d06 when read_empty() was split off from reset_hard()\nboth of these functions only had one caller. The \"verbose\" in\nread_empty() would always be false, and the one in reset_hard() would\nalways be true.\n\nThere was never a good reason for the code to act this way, it\nhappened because the read_empty() function was a copy/pasted and\nadjusted version of reset_hard().\n\nSince we're no longer conditionally adding the \"-v\" parameter\nhere (and we'd only add it for \"reset_hard()\" we'll be able to move to\na simpler and safer run-command API in the subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n builtin/merge.c | 13 +++++--------\n 1 file changed, 5 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 5900b81729..3bb49d805b 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -345,14 +345,12 @@ static int save_state(struct object_id *stash)\n \treturn rc;\n }\n\n-static void read_empty(const struct object_id *oid, int verbose)\n+static void read_empty(const struct object_id *oid)\n {\n \tint i = 0;\n \tconst char *args[7];\n\n \targs[i++] = \"read-tree\";\n-\tif (verbose)\n-\t\targs[i++] = \"-v\";\n \targs[i++] = \"-m\";\n \targs[i++] = \"-u\";\n \targs[i++] = empty_tree_oid_hex();\n@@ -363,14 +361,13 @@ static void read_empty(const struct object_id *oid, int verbose)\n \t\tdie(_(\"read-tree failed\"));\n }\n\n-static void reset_hard(const struct object_id *oid, int verbose)\n+static void reset_hard(const struct object_id *oid)\n {\n \tint i = 0;\n \tconst char *args[6];\n\n \targs[i++] = \"read-tree\";\n-\tif (verbose)\n-\t\targs[i++] = \"-v\";\n+\targs[i++] = \"-v\";\n \targs[i++] = \"--reset\";\n \targs[i++] = \"-u\";\n \targs[i++] = oid_to_hex(oid);\n@@ -385,7 +382,7 @@ static void restore_state(const struct object_id *head,\n {\n \tstruct strvec args = STRVEC_INIT;\n\n-\treset_hard(head, 1);\n+\treset_hard(head);\n\n \tif (is_null_oid(stash))\n \t\tgoto refresh_cache;\n@@ -1470,7 +1467,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t       check_trust_level);\n\n \t\tremote_head_oid = &remoteheads->item->object.oid;\n-\t\tread_empty(remote_head_oid, 0);\n+\t\tread_empty(remote_head_oid);\n \t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head_oid, NULL, 0,\n \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n \t\tgoto done;\n--\n2.38.1\n"},{"id":"465873","messageId":"033ebd37-40c7-01bc-e9bb-29d738532125@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 2/8] bisect--helper: factor out do_bisect_run()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:35:53Z","receivedAt":"2022-10-27T16:36:14Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Deduplicate the code for reporting and starting the bisect run command\nby moving it to a short helper function.  Use a string array instead of\na strvec to prepare the arguments, for simplicity.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n builtin/bisect--helper.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 28ef7ec2a4..70d1e9d1ad 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -1142,8 +1142,14 @@ static int get_first_good(const char *refname UNUSED,\n \treturn 1;\n }\n\n-static int verify_good(const struct bisect_terms *terms,\n-\t\t       const char **quoted_argv)\n+static int do_bisect_run(const char *command)\n+{\n+\tconst char *argv[] = { command, NULL };\n+\tprintf(_(\"running %s\\n\"), command);\n+\treturn run_command_v_opt(argv, RUN_USING_SHELL);\n+}\n+\n+static int verify_good(const struct bisect_terms *terms, const char *command)\n {\n \tint rc;\n \tenum bisect_error res;\n@@ -1163,8 +1169,7 @@ static int verify_good(const struct bisect_terms *terms,\n \tif (res != BISECT_OK)\n \t\treturn -1;\n\n-\tprintf(_(\"running %s\\n\"), quoted_argv[0]);\n-\trc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);\n+\trc = do_bisect_run(command);\n\n \tres = bisect_checkout(&current_rev, no_checkout);\n \tif (res != BISECT_OK)\n@@ -1177,7 +1182,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n {\n \tint res = BISECT_OK;\n \tstruct strbuf command = STRBUF_INIT;\n-\tstruct strvec run_args = STRVEC_INIT;\n \tconst char *new_state;\n \tint temporary_stdout_fd, saved_stdout;\n \tint is_first_run = 1;\n@@ -1192,11 +1196,8 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \t\treturn BISECT_FAILED;\n \t}\n\n-\tstrvec_push(&run_args, command.buf);\n-\n \twhile (1) {\n-\t\tprintf(_(\"running %s\\n\"), command.buf);\n-\t\tres = run_command_v_opt(run_args.v, RUN_USING_SHELL);\n+\t\tres = do_bisect_run(command.buf);\n\n \t\t/*\n \t\t * Exit code 126 and 127 can either come from the shell\n@@ -1206,7 +1207,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \t\t * missing or non-executable script.\n \t\t */\n \t\tif (is_first_run && (res == 126 || res == 127)) {\n-\t\t\tint rc = verify_good(terms, run_args.v);\n+\t\t\tint rc = verify_good(terms, command.buf);\n \t\t\tis_first_run = 0;\n \t\t\tif (rc < 0) {\n \t\t\t\terror(_(\"unable to verify '%s' on good\"\n@@ -1273,7 +1274,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \t}\n\n \tstrbuf_release(&command);\n-\tstrvec_clear(&run_args);\n \treturn res;\n }\n\n--\n2.38.1\n"},{"id":"465874","messageId":"672e4176-81b4-bb9c-5a14-e558d24b0c0d@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 3/8] use child_process members \"args\" and \"env\" directly","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:36:45Z","receivedAt":"2022-10-27T16:36:58Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Build argument list and environment of child processes by using\nstruct child_process and populating its members \"args\" and \"env\"\ndirectly instead of maintaining separate strvecs and letting\nrun_command_v_opt() and friends populate these members.  This is\nsimpler, shorter and slightly more efficient.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n add-interactive.c        |   9 ++-\n builtin/add.c            |  19 +++--\n builtin/bisect--helper.c |  35 +++++-----\n builtin/clone.c          |  33 ++++-----\n builtin/gc.c             |  14 ++--\n builtin/merge.c          |  10 +--\n builtin/pull.c           | 147 +++++++++++++++++++--------------------\n builtin/remote.c         |  26 ++++---\n diff.c                   |  27 ++++---\n git.c                    |  15 ++--\n merge.c                  |  18 ++---\n scalar.c                 |  13 ++--\n sequencer.c              |  25 +++----\n 13 files changed, 185 insertions(+), 206 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex f071b2a1b4..ecc5ae1b24 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -997,18 +997,17 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,\n \tcount = list_and_choose(s, files, opts);\n \topts->flags = 0;\n \tif (count > 0) {\n-\t\tstruct strvec args = STRVEC_INIT;\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\tstrvec_pushl(&args, \"git\", \"diff\", \"-p\", \"--cached\",\n+\t\tstrvec_pushl(&cmd.args, \"git\", \"diff\", \"-p\", \"--cached\",\n \t\t\t     oid_to_hex(!is_initial ? &oid :\n \t\t\t\t\ts->r->hash_algo->empty_tree),\n \t\t\t     \"--\", NULL);\n \t\tfor (i = 0; i < files->items.nr; i++)\n \t\t\tif (files->selected[i])\n-\t\t\t\tstrvec_push(&args,\n+\t\t\t\tstrvec_push(&cmd.args,\n \t\t\t\t\t    files->items.items[i].string);\n-\t\tres = run_command_v_opt(args.v, 0);\n-\t\tstrvec_clear(&args);\n+\t\tres = run_command(&cmd);\n \t}\n\n \tputchar('\\n');\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f84372964c..626c71ec6a 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -240,8 +240,8 @@ static int refresh(int verbose, const struct pathspec *pathspec)\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst struct pathspec *pathspec)\n {\n-\tint status, i;\n-\tstruct strvec argv = STRVEC_INIT;\n+\tint i;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint use_builtin_add_i =\n \t\tgit_env_bool(\"GIT_TEST_ADD_I_USE_BUILTIN\", -1);\n\n@@ -272,19 +272,18 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n \t}\n\n-\tstrvec_push(&argv, \"add--interactive\");\n+\tstrvec_push(&cmd.args, \"add--interactive\");\n \tif (patch_mode)\n-\t\tstrvec_push(&argv, patch_mode);\n+\t\tstrvec_push(&cmd.args, patch_mode);\n \tif (revision)\n-\t\tstrvec_push(&argv, revision);\n-\tstrvec_push(&argv, \"--\");\n+\t\tstrvec_push(&cmd.args, revision);\n+\tstrvec_push(&cmd.args, \"--\");\n \tfor (i = 0; i < pathspec->nr; i++)\n \t\t/* pass original pathspec, to be re-parsed */\n-\t\tstrvec_push(&argv, pathspec->items[i].original);\n+\t\tstrvec_push(&cmd.args, pathspec->items[i].original);\n\n-\tstatus = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&argv);\n-\treturn status;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n int interactive_add(const char **argv, const char *prefix, int patch)\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 70d1e9d1ad..5c63ba6994 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -220,18 +220,17 @@ static int bisect_reset(const char *commit)\n \t}\n\n \tif (!ref_exists(\"BISECT_HEAD\")) {\n-\t\tstruct strvec argv = STRVEC_INIT;\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\tstrvec_pushl(&argv, \"checkout\", branch.buf, \"--\", NULL);\n-\t\tif (run_command_v_opt(argv.v, RUN_GIT_CMD)) {\n+\t\tcmd.git_cmd = 1;\n+\t\tstrvec_pushl(&cmd.args, \"checkout\", branch.buf, \"--\", NULL);\n+\t\tif (run_command(&cmd)) {\n \t\t\terror(_(\"could not check out original\"\n \t\t\t\t\" HEAD '%s'. Try 'git bisect\"\n \t\t\t\t\" reset <commit>'.\"), branch.buf);\n \t\t\tstrbuf_release(&branch);\n-\t\t\tstrvec_clear(&argv);\n \t\t\treturn -1;\n \t\t}\n-\t\tstrvec_clear(&argv);\n \t}\n\n \tstrbuf_release(&branch);\n@@ -1098,40 +1097,38 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n\n static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)\n {\n-\tstruct strvec args = STRVEC_INIT;\n-\tint flags = RUN_COMMAND_NO_STDIN, res = 0;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n\n \tif (bisect_next_check(terms, NULL) != 0)\n \t\treturn BISECT_FAILED;\n\n+\tcmd.no_stdin = 1;\n \tif (!argc) {\n \t\tif ((getenv(\"DISPLAY\") || getenv(\"SESSIONNAME\") || getenv(\"MSYSTEM\") ||\n \t\t     getenv(\"SECURITYSESSIONID\")) && exists_in_PATH(\"gitk\")) {\n-\t\t\tstrvec_push(&args, \"gitk\");\n+\t\t\tstrvec_push(&cmd.args, \"gitk\");\n \t\t} else {\n-\t\t\tstrvec_push(&args, \"log\");\n-\t\t\tflags |= RUN_GIT_CMD;\n+\t\t\tstrvec_push(&cmd.args, \"log\");\n+\t\t\tcmd.git_cmd = 1;\n \t\t}\n \t} else {\n \t\tif (argv[0][0] == '-') {\n-\t\t\tstrvec_push(&args, \"log\");\n-\t\t\tflags |= RUN_GIT_CMD;\n+\t\t\tstrvec_push(&cmd.args, \"log\");\n+\t\t\tcmd.git_cmd = 1;\n \t\t} else if (strcmp(argv[0], \"tig\") && !starts_with(argv[0], \"git\"))\n-\t\t\tflags |= RUN_GIT_CMD;\n+\t\t\tcmd.git_cmd = 1;\n\n-\t\tstrvec_pushv(&args, argv);\n+\t\tstrvec_pushv(&cmd.args, argv);\n \t}\n\n-\tstrvec_pushl(&args, \"--bisect\", \"--\", NULL);\n+\tstrvec_pushl(&cmd.args, \"--bisect\", \"--\", NULL);\n\n \tstrbuf_read_file(&sb, git_path_bisect_names(), 0);\n-\tsq_dequote_to_strvec(sb.buf, &args);\n+\tsq_dequote_to_strvec(sb.buf, &cmd.args);\n \tstrbuf_release(&sb);\n\n-\tres = run_command_v_opt(args.v, flags);\n-\tstrvec_clear(&args);\n-\treturn res;\n+\treturn run_command(&cmd);\n }\n\n static int get_first_good(const char *refname UNUSED,\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 547d6464b3..56e7775dae 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -653,9 +653,9 @@ static void update_head(const struct ref *our, const struct ref *remote,\n\n static int git_sparse_checkout_init(const char *repo)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint result = 0;\n-\tstrvec_pushl(&argv, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n+\tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n\n \t/*\n \t * We must apply the setting in the current process\n@@ -663,12 +663,12 @@ static int git_sparse_checkout_init(const char *repo)\n \t */\n \tcore_apply_sparse_checkout = 1;\n\n-\tif (run_command_v_opt(argv.v, RUN_GIT_CMD)) {\n+\tcmd.git_cmd = 1;\n+\tif (run_command(&cmd)) {\n \t\terror(_(\"failed to initialize sparse-checkout\"));\n \t\tresult = 1;\n \t}\n\n-\tstrvec_clear(&argv);\n \treturn result;\n }\n\n@@ -733,37 +733,38 @@ static int checkout(int submodule_progress, int filter_submodules)\n \t\t\t   oid_to_hex(&oid), \"1\", NULL);\n\n \tif (!err && (option_recurse_submodules.nr > 0)) {\n-\t\tstruct strvec args = STRVEC_INIT;\n-\t\tstrvec_pushl(&args, \"submodule\", \"update\", \"--require-init\", \"--recursive\", NULL);\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\t\tstrvec_pushl(&cmd.args, \"submodule\", \"update\", \"--require-init\",\n+\t\t\t     \"--recursive\", NULL);\n\n \t\tif (option_shallow_submodules == 1)\n-\t\t\tstrvec_push(&args, \"--depth=1\");\n+\t\t\tstrvec_push(&cmd.args, \"--depth=1\");\n\n \t\tif (max_jobs != -1)\n-\t\t\tstrvec_pushf(&args, \"--jobs=%d\", max_jobs);\n+\t\t\tstrvec_pushf(&cmd.args, \"--jobs=%d\", max_jobs);\n\n \t\tif (submodule_progress)\n-\t\t\tstrvec_push(&args, \"--progress\");\n+\t\t\tstrvec_push(&cmd.args, \"--progress\");\n\n \t\tif (option_verbosity < 0)\n-\t\t\tstrvec_push(&args, \"--quiet\");\n+\t\t\tstrvec_push(&cmd.args, \"--quiet\");\n\n \t\tif (option_remote_submodules) {\n-\t\t\tstrvec_push(&args, \"--remote\");\n-\t\t\tstrvec_push(&args, \"--no-fetch\");\n+\t\t\tstrvec_push(&cmd.args, \"--remote\");\n+\t\t\tstrvec_push(&cmd.args, \"--no-fetch\");\n \t\t}\n\n \t\tif (filter_submodules && filter_options.choice)\n-\t\t\tstrvec_pushf(&args, \"--filter=%s\",\n+\t\t\tstrvec_pushf(&cmd.args, \"--filter=%s\",\n \t\t\t\t     expand_list_objects_filter_spec(&filter_options));\n\n \t\tif (option_single_branch >= 0)\n-\t\t\tstrvec_push(&args, option_single_branch ?\n+\t\t\tstrvec_push(&cmd.args, option_single_branch ?\n \t\t\t\t\t       \"--single-branch\" :\n \t\t\t\t\t       \"--no-single-branch\");\n\n-\t\terr = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\t\tstrvec_clear(&args);\n+\t\tcmd.git_cmd = 1;\n+\t\terr = run_command(&cmd);\n \t}\n\n \treturn err;\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 243ee85d28..87ad0077d8 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -1910,20 +1910,16 @@ static char *schtasks_task_name(const char *frequency)\n static int schtasks_remove_task(enum schedule_priority schedule)\n {\n \tconst char *cmd = \"schtasks\";\n-\tint result;\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process child = CHILD_PROCESS_INIT;\n \tconst char *frequency = get_frequency(schedule);\n \tchar *name = schtasks_task_name(frequency);\n\n \tget_schedule_cmd(&cmd, NULL);\n-\tstrvec_split(&args, cmd);\n-\tstrvec_pushl(&args, \"/delete\", \"/tn\", name, \"/f\", NULL);\n-\n-\tresult = run_command_v_opt(args.v, 0);\n-\n-\tstrvec_clear(&args);\n+\tstrvec_split(&child.args, cmd);\n+\tstrvec_pushl(&child.args, \"/delete\", \"/tn\", name, \"/f\", NULL);\n \tfree(name);\n-\treturn result;\n+\n+\treturn run_command(&child);\n }\n\n static int schtasks_remove_tasks(void)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 3bb49d805b..864808f51a 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -380,22 +380,22 @@ static void reset_hard(const struct object_id *oid)\n static void restore_state(const struct object_id *head,\n \t\t\t  const struct object_id *stash)\n {\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \treset_hard(head);\n\n \tif (is_null_oid(stash))\n \t\tgoto refresh_cache;\n\n-\tstrvec_pushl(&args, \"stash\", \"apply\", \"--index\", \"--quiet\", NULL);\n-\tstrvec_push(&args, oid_to_hex(stash));\n+\tstrvec_pushl(&cmd.args, \"stash\", \"apply\", \"--index\", \"--quiet\", NULL);\n+\tstrvec_push(&cmd.args, oid_to_hex(stash));\n\n \t/*\n \t * It is OK to ignore error here, for example when there was\n \t * nothing to restore.\n \t */\n-\trun_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n+\tcmd.git_cmd = 1;\n+\trun_command(&cmd);\n\n refresh_cache:\n \tif (discard_cache() < 0 || read_cache() < 0)\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 403a24d7ca..b21edd767a 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -515,76 +515,75 @@ static void parse_repo_refspecs(int argc, const char **argv, const char **repo,\n  */\n static int run_fetch(const char *repo, const char **refspecs)\n {\n-\tstruct strvec args = STRVEC_INIT;\n-\tint ret;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_pushl(&args, \"fetch\", \"--update-head-ok\", NULL);\n+\tstrvec_pushl(&cmd.args, \"fetch\", \"--update-head-ok\", NULL);\n\n \t/* Shared options */\n-\targv_push_verbosity(&args);\n+\targv_push_verbosity(&cmd.args);\n \tif (opt_progress)\n-\t\tstrvec_push(&args, opt_progress);\n+\t\tstrvec_push(&cmd.args, opt_progress);\n\n \t/* Options passed to git-fetch */\n \tif (opt_all)\n-\t\tstrvec_push(&args, opt_all);\n+\t\tstrvec_push(&cmd.args, opt_all);\n \tif (opt_append)\n-\t\tstrvec_push(&args, opt_append);\n+\t\tstrvec_push(&cmd.args, opt_append);\n \tif (opt_upload_pack)\n-\t\tstrvec_push(&args, opt_upload_pack);\n-\targv_push_force(&args);\n+\t\tstrvec_push(&cmd.args, opt_upload_pack);\n+\targv_push_force(&cmd.args);\n \tif (opt_tags)\n-\t\tstrvec_push(&args, opt_tags);\n+\t\tstrvec_push(&cmd.args, opt_tags);\n \tif (opt_prune)\n-\t\tstrvec_push(&args, opt_prune);\n+\t\tstrvec_push(&cmd.args, opt_prune);\n \tif (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)\n \t\tswitch (recurse_submodules_cli) {\n \t\tcase RECURSE_SUBMODULES_ON:\n-\t\t\tstrvec_push(&args, \"--recurse-submodules=on\");\n+\t\t\tstrvec_push(&cmd.args, \"--recurse-submodules=on\");\n \t\t\tbreak;\n \t\tcase RECURSE_SUBMODULES_OFF:\n-\t\t\tstrvec_push(&args, \"--recurse-submodules=no\");\n+\t\t\tstrvec_push(&cmd.args, \"--recurse-submodules=no\");\n \t\t\tbreak;\n \t\tcase RECURSE_SUBMODULES_ON_DEMAND:\n-\t\t\tstrvec_push(&args, \"--recurse-submodules=on-demand\");\n+\t\t\tstrvec_push(&cmd.args, \"--recurse-submodules=on-demand\");\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tBUG(\"submodule recursion option not understood\");\n \t\t}\n \tif (max_children)\n-\t\tstrvec_push(&args, max_children);\n+\t\tstrvec_push(&cmd.args, max_children);\n \tif (opt_dry_run)\n-\t\tstrvec_push(&args, \"--dry-run\");\n+\t\tstrvec_push(&cmd.args, \"--dry-run\");\n \tif (opt_keep)\n-\t\tstrvec_push(&args, opt_keep);\n+\t\tstrvec_push(&cmd.args, opt_keep);\n \tif (opt_depth)\n-\t\tstrvec_push(&args, opt_depth);\n+\t\tstrvec_push(&cmd.args, opt_depth);\n \tif (opt_unshallow)\n-\t\tstrvec_push(&args, opt_unshallow);\n+\t\tstrvec_push(&cmd.args, opt_unshallow);\n \tif (opt_update_shallow)\n-\t\tstrvec_push(&args, opt_update_shallow);\n+\t\tstrvec_push(&cmd.args, opt_update_shallow);\n \tif (opt_refmap)\n-\t\tstrvec_push(&args, opt_refmap);\n+\t\tstrvec_push(&cmd.args, opt_refmap);\n \tif (opt_ipv4)\n-\t\tstrvec_push(&args, opt_ipv4);\n+\t\tstrvec_push(&cmd.args, opt_ipv4);\n \tif (opt_ipv6)\n-\t\tstrvec_push(&args, opt_ipv6);\n+\t\tstrvec_push(&cmd.args, opt_ipv6);\n \tif (opt_show_forced_updates > 0)\n-\t\tstrvec_push(&args, \"--show-forced-updates\");\n+\t\tstrvec_push(&cmd.args, \"--show-forced-updates\");\n \telse if (opt_show_forced_updates == 0)\n-\t\tstrvec_push(&args, \"--no-show-forced-updates\");\n+\t\tstrvec_push(&cmd.args, \"--no-show-forced-updates\");\n \tif (set_upstream)\n-\t\tstrvec_push(&args, set_upstream);\n-\tstrvec_pushv(&args, opt_fetch.v);\n+\t\tstrvec_push(&cmd.args, set_upstream);\n+\tstrvec_pushv(&cmd.args, opt_fetch.v);\n\n \tif (repo) {\n-\t\tstrvec_push(&args, repo);\n-\t\tstrvec_pushv(&args, refspecs);\n+\t\tstrvec_push(&cmd.args, repo);\n+\t\tstrvec_pushv(&cmd.args, refspecs);\n \t} else if (*refspecs)\n \t\tBUG(\"refspecs without repo?\");\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE);\n-\tstrvec_clear(&args);\n-\treturn ret;\n+\tcmd.git_cmd = 1;\n+\tcmd.close_object_store = 1;\n+\treturn run_command(&cmd);\n }\n\n /**\n@@ -653,52 +652,50 @@ static int update_submodules(void)\n  */\n static int run_merge(void)\n {\n-\tint ret;\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_pushl(&args, \"merge\", NULL);\n+\tstrvec_pushl(&cmd.args, \"merge\", NULL);\n\n \t/* Shared options */\n-\targv_push_verbosity(&args);\n+\targv_push_verbosity(&cmd.args);\n \tif (opt_progress)\n-\t\tstrvec_push(&args, opt_progress);\n+\t\tstrvec_push(&cmd.args, opt_progress);\n\n \t/* Options passed to git-merge */\n \tif (opt_diffstat)\n-\t\tstrvec_push(&args, opt_diffstat);\n+\t\tstrvec_push(&cmd.args, opt_diffstat);\n \tif (opt_log)\n-\t\tstrvec_push(&args, opt_log);\n+\t\tstrvec_push(&cmd.args, opt_log);\n \tif (opt_signoff)\n-\t\tstrvec_push(&args, opt_signoff);\n+\t\tstrvec_push(&cmd.args, opt_signoff);\n \tif (opt_squash)\n-\t\tstrvec_push(&args, opt_squash);\n+\t\tstrvec_push(&cmd.args, opt_squash);\n \tif (opt_commit)\n-\t\tstrvec_push(&args, opt_commit);\n+\t\tstrvec_push(&cmd.args, opt_commit);\n \tif (opt_edit)\n-\t\tstrvec_push(&args, opt_edit);\n+\t\tstrvec_push(&cmd.args, opt_edit);\n \tif (cleanup_arg)\n-\t\tstrvec_pushf(&args, \"--cleanup=%s\", cleanup_arg);\n+\t\tstrvec_pushf(&cmd.args, \"--cleanup=%s\", cleanup_arg);\n \tif (opt_ff)\n-\t\tstrvec_push(&args, opt_ff);\n+\t\tstrvec_push(&cmd.args, opt_ff);\n \tif (opt_verify)\n-\t\tstrvec_push(&args, opt_verify);\n+\t\tstrvec_push(&cmd.args, opt_verify);\n \tif (opt_verify_signatures)\n-\t\tstrvec_push(&args, opt_verify_signatures);\n-\tstrvec_pushv(&args, opt_strategies.v);\n-\tstrvec_pushv(&args, opt_strategy_opts.v);\n+\t\tstrvec_push(&cmd.args, opt_verify_signatures);\n+\tstrvec_pushv(&cmd.args, opt_strategies.v);\n+\tstrvec_pushv(&cmd.args, opt_strategy_opts.v);\n \tif (opt_gpg_sign)\n-\t\tstrvec_push(&args, opt_gpg_sign);\n+\t\tstrvec_push(&cmd.args, opt_gpg_sign);\n \tif (opt_autostash == 0)\n-\t\tstrvec_push(&args, \"--no-autostash\");\n+\t\tstrvec_push(&cmd.args, \"--no-autostash\");\n \telse if (opt_autostash == 1)\n-\t\tstrvec_push(&args, \"--autostash\");\n+\t\tstrvec_push(&cmd.args, \"--autostash\");\n \tif (opt_allow_unrelated_histories > 0)\n-\t\tstrvec_push(&args, \"--allow-unrelated-histories\");\n+\t\tstrvec_push(&cmd.args, \"--allow-unrelated-histories\");\n\n-\tstrvec_push(&args, \"FETCH_HEAD\");\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n-\treturn ret;\n+\tstrvec_push(&cmd.args, \"FETCH_HEAD\");\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n /**\n@@ -879,43 +876,41 @@ static int get_rebase_newbase_and_upstream(struct object_id *newbase,\n static int run_rebase(const struct object_id *newbase,\n \t\tconst struct object_id *upstream)\n {\n-\tint ret;\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_push(&args, \"rebase\");\n+\tstrvec_push(&cmd.args, \"rebase\");\n\n \t/* Shared options */\n-\targv_push_verbosity(&args);\n+\targv_push_verbosity(&cmd.args);\n\n \t/* Options passed to git-rebase */\n \tif (opt_rebase == REBASE_MERGES)\n-\t\tstrvec_push(&args, \"--rebase-merges\");\n+\t\tstrvec_push(&cmd.args, \"--rebase-merges\");\n \telse if (opt_rebase == REBASE_INTERACTIVE)\n-\t\tstrvec_push(&args, \"--interactive\");\n+\t\tstrvec_push(&cmd.args, \"--interactive\");\n \tif (opt_diffstat)\n-\t\tstrvec_push(&args, opt_diffstat);\n-\tstrvec_pushv(&args, opt_strategies.v);\n-\tstrvec_pushv(&args, opt_strategy_opts.v);\n+\t\tstrvec_push(&cmd.args, opt_diffstat);\n+\tstrvec_pushv(&cmd.args, opt_strategies.v);\n+\tstrvec_pushv(&cmd.args, opt_strategy_opts.v);\n \tif (opt_gpg_sign)\n-\t\tstrvec_push(&args, opt_gpg_sign);\n+\t\tstrvec_push(&cmd.args, opt_gpg_sign);\n \tif (opt_signoff)\n-\t\tstrvec_push(&args, opt_signoff);\n+\t\tstrvec_push(&cmd.args, opt_signoff);\n \tif (opt_autostash == 0)\n-\t\tstrvec_push(&args, \"--no-autostash\");\n+\t\tstrvec_push(&cmd.args, \"--no-autostash\");\n \telse if (opt_autostash == 1)\n-\t\tstrvec_push(&args, \"--autostash\");\n+\t\tstrvec_push(&cmd.args, \"--autostash\");\n \tif (opt_verify_signatures &&\n \t    !strcmp(opt_verify_signatures, \"--verify-signatures\"))\n \t\twarning(_(\"ignoring --verify-signatures for rebase\"));\n\n-\tstrvec_push(&args, \"--onto\");\n-\tstrvec_push(&args, oid_to_hex(newbase));\n+\tstrvec_push(&cmd.args, \"--onto\");\n+\tstrvec_push(&cmd.args, oid_to_hex(newbase));\n\n-\tstrvec_push(&args, oid_to_hex(upstream));\n+\tstrvec_push(&cmd.args, oid_to_hex(upstream));\n\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n-\treturn ret;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n static int get_can_ff(struct object_id *orig_head,\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 910f7b9316..11304c096a 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -1508,37 +1508,35 @@ static int update(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"prune remotes after fetching\")),\n \t\tOPT_END()\n \t};\n-\tstruct strvec fetch_argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint default_defined = 0;\n-\tint retval;\n\n \targc = parse_options(argc, argv, prefix, options,\n \t\t\t     builtin_remote_update_usage,\n \t\t\t     PARSE_OPT_KEEP_ARGV0);\n\n-\tstrvec_push(&fetch_argv, \"fetch\");\n+\tstrvec_push(&cmd.args, \"fetch\");\n\n \tif (prune != -1)\n-\t\tstrvec_push(&fetch_argv, prune ? \"--prune\" : \"--no-prune\");\n+\t\tstrvec_push(&cmd.args, prune ? \"--prune\" : \"--no-prune\");\n \tif (verbose)\n-\t\tstrvec_push(&fetch_argv, \"-v\");\n-\tstrvec_push(&fetch_argv, \"--multiple\");\n+\t\tstrvec_push(&cmd.args, \"-v\");\n+\tstrvec_push(&cmd.args, \"--multiple\");\n \tif (argc < 2)\n-\t\tstrvec_push(&fetch_argv, \"default\");\n+\t\tstrvec_push(&cmd.args, \"default\");\n \tfor (i = 1; i < argc; i++)\n-\t\tstrvec_push(&fetch_argv, argv[i]);\n+\t\tstrvec_push(&cmd.args, argv[i]);\n\n-\tif (strcmp(fetch_argv.v[fetch_argv.nr-1], \"default\") == 0) {\n+\tif (strcmp(cmd.args.v[cmd.args.nr-1], \"default\") == 0) {\n \t\tgit_config(get_remote_default, &default_defined);\n \t\tif (!default_defined) {\n-\t\t\tstrvec_pop(&fetch_argv);\n-\t\t\tstrvec_push(&fetch_argv, \"--all\");\n+\t\t\tstrvec_pop(&cmd.args);\n+\t\t\tstrvec_push(&cmd.args, \"--all\");\n \t\t}\n \t}\n\n-\tretval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&fetch_argv);\n-\treturn retval;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n static int remove_all_fetch_refspecs(const char *key)\ndiff --git a/diff.c b/diff.c\nindex 648f6717a5..8451c230d9 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4278,35 +4278,34 @@ static void run_external_diff(const char *pgm,\n \t\t\t      const char *xfrm_msg,\n \t\t\t      struct diff_options *o)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n-\tstruct strvec env = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n\n-\tstrvec_push(&argv, pgm);\n-\tstrvec_push(&argv, name);\n+\tstrvec_push(&cmd.args, pgm);\n+\tstrvec_push(&cmd.args, name);\n\n \tif (one && two) {\n-\t\tadd_external_diff_name(o->repo, &argv, name, one);\n+\t\tadd_external_diff_name(o->repo, &cmd.args, name, one);\n \t\tif (!other)\n-\t\t\tadd_external_diff_name(o->repo, &argv, name, two);\n+\t\t\tadd_external_diff_name(o->repo, &cmd.args, name, two);\n \t\telse {\n-\t\t\tadd_external_diff_name(o->repo, &argv, other, two);\n-\t\t\tstrvec_push(&argv, other);\n-\t\t\tstrvec_push(&argv, xfrm_msg);\n+\t\t\tadd_external_diff_name(o->repo, &cmd.args, other, two);\n+\t\t\tstrvec_push(&cmd.args, other);\n+\t\t\tstrvec_push(&cmd.args, xfrm_msg);\n \t\t}\n \t}\n\n-\tstrvec_pushf(&env, \"GIT_DIFF_PATH_COUNTER=%d\", ++o->diff_path_counter);\n-\tstrvec_pushf(&env, \"GIT_DIFF_PATH_TOTAL=%d\", q->nr);\n+\tstrvec_pushf(&cmd.env, \"GIT_DIFF_PATH_COUNTER=%d\",\n+\t\t     ++o->diff_path_counter);\n+\tstrvec_pushf(&cmd.env, \"GIT_DIFF_PATH_TOTAL=%d\", q->nr);\n\n \tdiff_free_filespec_data(one);\n \tdiff_free_filespec_data(two);\n-\tif (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v))\n+\tcmd.use_shell = 1;\n+\tif (run_command(&cmd))\n \t\tdie(_(\"external diff died, stopping at %s\"), name);\n\n \tremove_tempfile();\n-\tstrvec_clear(&argv);\n-\tstrvec_clear(&env);\n }\n\n static int similarity_index(struct diff_filepair *p)\ndiff --git a/git.c b/git.c\nindex ee7758dcb0..6662548986 100644\n--- a/git.c\n+++ b/git.c\n@@ -787,7 +787,7 @@ static int run_argv(int *argcp, const char ***argv)\n \t\tif (!done_alias)\n \t\t\thandle_builtin(*argcp, *argv);\n \t\telse if (get_builtin(**argv)) {\n-\t\t\tstruct strvec args = STRVEC_INIT;\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \t\t\tint i;\n\n \t\t\t/*\n@@ -804,18 +804,21 @@ static int run_argv(int *argcp, const char ***argv)\n\n \t\t\tcommit_pager_choice();\n\n-\t\t\tstrvec_push(&args, \"git\");\n+\t\t\tstrvec_push(&cmd.args, \"git\");\n \t\t\tfor (i = 0; i < *argcp; i++)\n-\t\t\t\tstrvec_push(&args, (*argv)[i]);\n+\t\t\t\tstrvec_push(&cmd.args, (*argv)[i]);\n\n-\t\t\ttrace_argv_printf(args.v, \"trace: exec:\");\n+\t\t\ttrace_argv_printf(cmd.args.v, \"trace: exec:\");\n\n \t\t\t/*\n \t\t\t * if we fail because the command is not found, it is\n \t\t\t * OK to return. Otherwise, we just pass along the status code.\n \t\t\t */\n-\t\t\ti = run_command_v_opt_tr2(args.v, RUN_SILENT_EXEC_FAILURE |\n-\t\t\t\t\t\t  RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, \"git_alias\");\n+\t\t\tcmd.silent_exec_failure = 1;\n+\t\t\tcmd.clean_on_exit = 1;\n+\t\t\tcmd.wait_after_clean = 1;\n+\t\t\tcmd.trace2_child_class = \"git_alias\";\n+\t\t\ti = run_command(&cmd);\n \t\t\tif (i >= 0 || errno != ENOENT)\n \t\t\t\texit(i);\n \t\t\tdie(\"could not execute builtin %s\", **argv);\ndiff --git a/merge.c b/merge.c\nindex 2382ff66d3..445b4f19aa 100644\n--- a/merge.c\n+++ b/merge.c\n@@ -19,22 +19,22 @@ int try_merge_command(struct repository *r,\n \t\t      const char **xopts, struct commit_list *common,\n \t\t      const char *head_arg, struct commit_list *remotes)\n {\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint i, ret;\n \tstruct commit_list *j;\n\n-\tstrvec_pushf(&args, \"merge-%s\", strategy);\n+\tstrvec_pushf(&cmd.args, \"merge-%s\", strategy);\n \tfor (i = 0; i < xopts_nr; i++)\n-\t\tstrvec_pushf(&args, \"--%s\", xopts[i]);\n+\t\tstrvec_pushf(&cmd.args, \"--%s\", xopts[i]);\n \tfor (j = common; j; j = j->next)\n-\t\tstrvec_push(&args, merge_argument(j->item));\n-\tstrvec_push(&args, \"--\");\n-\tstrvec_push(&args, head_arg);\n+\t\tstrvec_push(&cmd.args, merge_argument(j->item));\n+\tstrvec_push(&cmd.args, \"--\");\n+\tstrvec_push(&cmd.args, head_arg);\n \tfor (j = remotes; j; j = j->next)\n-\t\tstrvec_push(&args, merge_argument(j->item));\n+\t\tstrvec_push(&cmd.args, merge_argument(j->item));\n\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n+\tcmd.git_cmd = 1;\n+\tret = run_command(&cmd);\n\n \tdiscard_index(r->index);\n \tif (repo_read_index(r) < 0)\ndiff --git a/scalar.c b/scalar.c\nindex 6de9c0ee52..03f9e480dd 100644\n--- a/scalar.c\n+++ b/scalar.c\n@@ -69,21 +69,18 @@ static void setup_enlistment_directory(int argc, const char **argv,\n\n static int run_git(const char *arg, ...)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tva_list args;\n \tconst char *p;\n-\tint res;\n\n \tva_start(args, arg);\n-\tstrvec_push(&argv, arg);\n+\tstrvec_push(&cmd.args, arg);\n \twhile ((p = va_arg(args, const char *)))\n-\t\tstrvec_push(&argv, p);\n+\t\tstrvec_push(&cmd.args, p);\n \tva_end(args);\n\n-\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\n-\tstrvec_clear(&argv);\n-\treturn res;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n struct scalar_config {\ndiff --git a/sequencer.c b/sequencer.c\nindex debb2ecbaf..8ab0225f0f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3183,18 +3183,15 @@ static int rollback_is_safe(void)\n\n static int reset_merge(const struct object_id *oid)\n {\n-\tint ret;\n-\tstruct strvec argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_pushl(&argv, \"reset\", \"--merge\", NULL);\n+\tcmd.git_cmd = 1;\n+\tstrvec_pushl(&cmd.args, \"reset\", \"--merge\", NULL);\n\n \tif (!is_null_oid(oid))\n-\t\tstrvec_push(&argv, oid_to_hex(oid));\n+\t\tstrvec_push(&cmd.args, oid_to_hex(oid));\n\n-\tret = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&argv);\n-\n-\treturn ret;\n+\treturn run_command(&cmd);\n }\n\n static int rollback_single_pick(struct repository *r)\n@@ -4867,14 +4864,14 @@ static int pick_commits(struct repository *r,\n\n static int continue_single_pick(struct repository *r, struct replay_opts *opts)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n-\tint ret;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \tif (!refs_ref_exists(get_main_ref_store(r), \"CHERRY_PICK_HEAD\") &&\n \t    !refs_ref_exists(get_main_ref_store(r), \"REVERT_HEAD\"))\n \t\treturn error(_(\"no cherry-pick or revert in progress\"));\n\n-\tstrvec_push(&argv, \"commit\");\n+\tcmd.git_cmd = 1;\n+\tstrvec_push(&cmd.args, \"commit\");\n\n \t/*\n \t * continue_single_pick() handles the case of recovering from a\n@@ -4887,11 +4884,9 @@ static int continue_single_pick(struct repository *r, struct replay_opts *opts)\n \t\t * Include --cleanup=strip as well because we don't want the\n \t\t * \"# Conflicts:\" messages.\n \t\t */\n-\t\tstrvec_pushl(&argv, \"--no-edit\", \"--cleanup=strip\", NULL);\n+\t\tstrvec_pushl(&cmd.args, \"--no-edit\", \"--cleanup=strip\", NULL);\n\n-\tret = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&argv);\n-\treturn ret;\n+\treturn run_command(&cmd);\n }\n\n static int commit_staged_changes(struct repository *r,\n--\n2.38.1\n"},{"id":"465875","messageId":"0e889c96-6004-96e4-9505-19ce1e7f9900@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 4/8] use child_process member \"args\" instead of string array variable","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:37:52Z","receivedAt":"2022-10-27T16:38:09Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Use run_command() with a struct child_process variable and populate its\n\"args\" member directly instead of building a string array and passing it\nto run_command_v_opt().  This avoids the use of magic index numbers.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n bisect.c                 | 12 ++++++------\n builtin/am.c             | 12 +++++-------\n builtin/difftool.c       | 17 +++++++++--------\n builtin/merge.c          | 32 ++++++++++++--------------------\n builtin/remote.c         | 14 ++++++++------\n compat/mingw.c           | 11 +++++++----\n ll-merge.c               |  7 ++++---\n sequencer.c              |  7 ++++---\n t/helper/test-fake-ssh.c |  7 ++++---\n 9 files changed, 59 insertions(+), 60 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex fd581b85a7..ec7487e683 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -22,8 +22,6 @@ static struct oid_array skipped_revs;\n\n static struct object_id *current_bad_oid;\n\n-static const char *argv_checkout[] = {\"checkout\", \"-q\", NULL, \"--\", NULL};\n-\n static const char *term_bad;\n static const char *term_good;\n\n@@ -729,20 +727,22 @@ static int is_expected_rev(const struct object_id *oid)\n enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n \t\t\t\t  int no_checkout)\n {\n-\tchar bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n \tstruct commit *commit;\n \tstruct pretty_print_context pp = {0};\n \tstruct strbuf commit_msg = STRBUF_INIT;\n\n-\toid_to_hex_r(bisect_rev_hex, bisect_rev);\n \tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n\n-\targv_checkout[2] = bisect_rev_hex;\n \tif (no_checkout) {\n \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n \t} else {\n-\t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\tcmd.git_cmd = 1;\n+\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n+\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n+\t\tif (run_command(&cmd))\n \t\t\t/*\n \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n \t\t\t * and errors in the child process, signaled by res > 0\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 39fea24833..20aea0d248 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -2187,14 +2187,12 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n \tint len;\n\n \tif (!is_null_oid(&state->orig_commit)) {\n-\t\tconst char *av[4] = { \"show\", NULL, \"--\", NULL };\n-\t\tchar *new_oid_str;\n-\t\tint ret;\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\tav[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));\n-\t\tret = run_command_v_opt(av, RUN_GIT_CMD);\n-\t\tfree(new_oid_str);\n-\t\treturn ret;\n+\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n+\t\t\t     \"--\", NULL);\n+\t\tcmd.git_cmd = 1;\n+\t\treturn run_command(&cmd);\n \t}\n\n \tswitch (sub_mode) {\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 4b10ad1a36..22bcc3444b 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -360,8 +360,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \tstruct pair_entry *entry;\n \tstruct index_state wtindex;\n \tstruct checkout lstate, rstate;\n-\tint flags = RUN_GIT_CMD, err = 0;\n-\tconst char *helper_argv[] = { \"difftool--helper\", NULL, NULL, NULL };\n+\tint err = 0;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct hashmap wt_modified, tmp_modified;\n \tint indices_loaded = 0;\n\n@@ -563,16 +563,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t}\n\n \tstrbuf_setlen(&ldir, ldir_len);\n-\thelper_argv[1] = ldir.buf;\n \tstrbuf_setlen(&rdir, rdir_len);\n-\thelper_argv[2] = rdir.buf;\n\n \tif (extcmd) {\n-\t\thelper_argv[0] = extcmd;\n-\t\tflags = 0;\n-\t} else\n+\t\tstrvec_push(&cmd.args, extcmd);\n+\t} else {\n+\t\tstrvec_push(&cmd.args, \"difftool--helper\");\n+\t\tcmd.git_cmd = 1;\n \t\tsetenv(\"GIT_DIFFTOOL_DIRDIFF\", \"true\", 1);\n-\tret = run_command_v_opt(helper_argv, flags);\n+\t}\n+\tstrvec_pushl(&cmd.args, ldir.buf, rdir.buf, NULL);\n+\tret = run_command(&cmd);\n\n \t/* TODO: audit for interaction with sparse-index. */\n \tensure_full_index(&wtindex);\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 864808f51a..b3f75f55c8 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -347,33 +347,25 @@ static int save_state(struct object_id *stash)\n\n static void read_empty(const struct object_id *oid)\n {\n-\tint i = 0;\n-\tconst char *args[7];\n-\n-\targs[i++] = \"read-tree\";\n-\targs[i++] = \"-m\";\n-\targs[i++] = \"-u\";\n-\targs[i++] = empty_tree_oid_hex();\n-\targs[i++] = oid_to_hex(oid);\n-\targs[i] = NULL;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tstrvec_pushl(&cmd.args, \"read-tree\", \"-m\", \"-u\", empty_tree_oid_hex(),\n+\t\t     oid_to_hex(oid), NULL);\n+\tcmd.git_cmd = 1;\n\n-\tif (run_command_v_opt(args, RUN_GIT_CMD))\n+\tif (run_command(&cmd))\n \t\tdie(_(\"read-tree failed\"));\n }\n\n static void reset_hard(const struct object_id *oid)\n {\n-\tint i = 0;\n-\tconst char *args[6];\n-\n-\targs[i++] = \"read-tree\";\n-\targs[i++] = \"-v\";\n-\targs[i++] = \"--reset\";\n-\targs[i++] = \"-u\";\n-\targs[i++] = oid_to_hex(oid);\n-\targs[i] = NULL;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tstrvec_pushl(&cmd.args, \"read-tree\", \"-v\", \"--reset\", \"-u\",\n+\t\t     oid_to_hex(oid), NULL);\n+\tcmd.git_cmd = 1;\n\n-\tif (run_command_v_opt(args, RUN_GIT_CMD))\n+\tif (run_command(&cmd))\n \t\tdie(_(\"read-tree failed\"));\n }\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 11304c096a..12632676cd 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -92,13 +92,15 @@ static int verbose;\n\n static int fetch_remote(const char *name)\n {\n-\tconst char *argv[] = { \"fetch\", name, NULL, NULL };\n-\tif (verbose) {\n-\t\targv[1] = \"-v\";\n-\t\targv[2] = name;\n-\t}\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tstrvec_push(&cmd.args, \"fetch\");\n+\tif (verbose)\n+\t\tstrvec_push(&cmd.args, \"-v\");\n+\tstrvec_push(&cmd.args, name);\n+\tcmd.git_cmd = 1;\n \tprintf_ln(_(\"Updating %s\"), name);\n-\tif (run_command_v_opt(argv, RUN_GIT_CMD))\n+\tif (run_command(&cmd))\n \t\treturn error(_(\"Could not fetch %s\"), name);\n \treturn 0;\n }\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex 901375d584..d614f156df 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -196,16 +196,19 @@ static int read_yes_no_answer(void)\n static int ask_yes_no_if_possible(const char *format, ...)\n {\n \tchar question[4096];\n-\tconst char *retry_hook[] = { NULL, NULL, NULL };\n+\tconst char *retry_hook;\n \tva_list args;\n\n \tva_start(args, format);\n \tvsnprintf(question, sizeof(question), format, args);\n \tva_end(args);\n\n-\tif ((retry_hook[0] = mingw_getenv(\"GIT_ASK_YESNO\"))) {\n-\t\tretry_hook[1] = question;\n-\t\treturn !run_command_v_opt(retry_hook, 0);\n+\tretry_hook = mingw_getenv(\"GIT_ASK_YESNO\");\n+\tif (retry_hook) {\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\tstrvec_pushl(&cmd.args, retry_hook, question, NULL);\n+\t\treturn !run_command(&cmd);\n \t}\n\n \tif (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))\ndiff --git a/ll-merge.c b/ll-merge.c\nindex 8955d7e1f6..d5f0c62bd8 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -193,7 +193,7 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,\n \tstruct strbuf cmd = STRBUF_INIT;\n \tstruct strbuf_expand_dict_entry dict[6];\n \tstruct strbuf path_sq = STRBUF_INIT;\n-\tconst char *args[] = { NULL, NULL };\n+\tstruct child_process child = CHILD_PROCESS_INIT;\n \tint status, fd, i;\n \tstruct stat st;\n \tenum ll_merge_result ret;\n@@ -219,8 +219,9 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,\n\n \tstrbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);\n\n-\targs[0] = cmd.buf;\n-\tstatus = run_command_v_opt(args, RUN_USING_SHELL);\n+\tchild.use_shell = 1;\n+\tstrvec_push(&child.args, cmd.buf);\n+\tstatus = run_command(&child);\n \tfd = open(temp[1], O_RDONLY);\n \tif (fd < 0)\n \t\tgoto bad;\ndiff --git a/sequencer.c b/sequencer.c\nindex 8ab0225f0f..643744fb9b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3555,12 +3555,13 @@ static int error_failed_squash(struct repository *r,\n\n static int do_exec(struct repository *r, const char *command_line)\n {\n-\tconst char *child_argv[] = { NULL, NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint dirty, status;\n\n \tfprintf(stderr, _(\"Executing: %s\\n\"), command_line);\n-\tchild_argv[0] = command_line;\n-\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n+\tcmd.use_shell = 1;\n+\tstrvec_push(&cmd.args, command_line);\n+\tstatus = run_command(&cmd);\n\n \t/* force re-reading of the cache */\n \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0)\ndiff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c\nindex 12beee99ad..2e576bcc11 100644\n--- a/t/helper/test-fake-ssh.c\n+++ b/t/helper/test-fake-ssh.c\n@@ -8,7 +8,7 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf buf = STRBUF_INIT;\n \tFILE *f;\n \tint i;\n-\tconst char *child_argv[] = { NULL, NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \t/* First, print all parameters into $TRASH_DIRECTORY/ssh-output */\n \tif (!trash_directory)\n@@ -25,6 +25,7 @@ int cmd_main(int argc, const char **argv)\n \t/* Now, evaluate the *last* parameter */\n \tif (argc < 2)\n \t\treturn 0;\n-\tchild_argv[0] = argv[argc - 1];\n-\treturn run_command_v_opt(child_argv, RUN_USING_SHELL);\n+\tcmd.use_shell = 1;\n+\tstrvec_push(&cmd.args, argv[argc - 1]);\n+\treturn run_command(&cmd);\n }\n--\n2.38.1\n"},{"id":"465876","messageId":"e72a1abe-3098-e4fb-f064-a8b5c8f14239@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 5/8] replace and remove run_command_v_opt_cd_env()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:38:41Z","receivedAt":"2022-10-27T16:38:52Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"run_command_v_opt_cd_env() is only used in an example in a comment.  Use\nthe struct child_process member \"env\" and run_command() directly instead\nand then remove the unused convenience function.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n run-command.c | 7 +------\n run-command.h | 4 +---\n tmp-objdir.h  | 4 ++--\n 3 files changed, 4 insertions(+), 11 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex 5ec3a46dcc..1c9ed510f8 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1006,7 +1006,7 @@ int run_command(struct child_process *cmd)\n\n int run_command_v_opt(const char **argv, int opt)\n {\n-\treturn run_command_v_opt_cd_env(argv, opt, NULL, NULL);\n+\treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, NULL);\n }\n\n int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n@@ -1014,11 +1014,6 @@ int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n \treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class);\n }\n\n-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)\n-{\n-\treturn run_command_v_opt_cd_env_tr2(argv, opt, dir, env, NULL);\n-}\n-\n int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \t\t\t\t const char *const *env, const char *tr2_class)\n {\ndiff --git a/run-command.h b/run-command.h\nindex 0e85e5846a..a5e210fd1a 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -151,8 +151,7 @@ struct child_process {\n\n /**\n  * The functions: child_process_init, start_command, finish_command,\n- * run_command, run_command_v_opt, run_command_v_opt_cd_env, child_process_clear\n- * do the following:\n+ * run_command, run_command_v_opt, child_process_clear do the following:\n  *\n  * - If a system call failed, errno is set and -1 is returned. A diagnostic\n  *   is printed.\n@@ -250,7 +249,6 @@ int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class);\n  * env (the environment) is to be formatted like environ: \"VAR=VALUE\".\n  * To unset an environment variable use just \"VAR\".\n  */\n-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);\n int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \t\t\t\t const char *const *env, const char *tr2_class);\n\ndiff --git a/tmp-objdir.h b/tmp-objdir.h\nindex 76efc7edee..615b188573 100644\n--- a/tmp-objdir.h\n+++ b/tmp-objdir.h\n@@ -11,8 +11,8 @@\n  * Example:\n  *\n  *\tstruct tmp_objdir *t = tmp_objdir_create(\"incoming\");\n- *\tif (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&\n- *\t    !tmp_objdir_migrate(t))\n+ *\tstrvec_pushv(&cmd.env, tmp_objdir_env(t));\n+ *\tif (!run_command(&cmd)) && !tmp_objdir_migrate(t))\n  *\t\tprintf(\"success!\\n\");\n  *\telse\n  *\t\tdie(\"failed...tmp_objdir will clean up for us\");\n--\n2.38.1\n"},{"id":"465877","messageId":"65f44b18-b23e-5d1a-2b57-6d326c05f4f5@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 6/8] replace and remove run_command_v_opt_tr2()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:39:28Z","receivedAt":"2022-10-27T16:39:50Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"The convenience function run_command_v_opt_tr2() is only used by a\nsingle caller.  Use struct child_process and run_command() directly\ninstead and remove the underused function.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n fsmonitor-ipc.c | 10 +++++++---\n run-command.c   |  5 -----\n run-command.h   |  1 -\n 3 files changed, 7 insertions(+), 9 deletions(-)\n\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex c0f42301c8..19d772f0f3 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -54,10 +54,14 @@ enum ipc_active_state fsmonitor_ipc__get_state(void)\n\n static int spawn_daemon(void)\n {\n-\tconst char *args[] = { \"fsmonitor--daemon\", \"start\", NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\treturn run_command_v_opt_tr2(args, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,\n-\t\t\t\t    \"fsmonitor\");\n+\tcmd.git_cmd = 1;\n+\tcmd.no_stdin = 1;\n+\tcmd.trace2_child_class = \"fsmonitor\";\n+\tstrvec_pushl(&cmd.args, \"fsmonitor--daemon\", \"start\", NULL);\n+\n+\treturn run_command(&cmd);\n }\n\n int fsmonitor_ipc__send_query(const char *since_token,\ndiff --git a/run-command.c b/run-command.c\nindex 1c9ed510f8..32fa4b0ed6 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1009,11 +1009,6 @@ int run_command_v_opt(const char **argv, int opt)\n \treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, NULL);\n }\n\n-int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n-{\n-\treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class);\n-}\n-\n int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \t\t\t\t const char *const *env, const char *tr2_class)\n {\ndiff --git a/run-command.h b/run-command.h\nindex a5e210fd1a..482da7f60c 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -244,7 +244,6 @@ int run_auto_maintenance(int quiet);\n  * corresponds to the member .env.\n  */\n int run_command_v_opt(const char **argv, int opt);\n-int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class);\n /*\n  * env (the environment) is to be formatted like environ: \"VAR=VALUE\".\n  * To unset an environment variable use just \"VAR\".\n--\n2.38.1\n"},{"id":"465878","messageId":"f26f7974-3e87-2ddf-6eeb-5d97f80a4e0d@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 7/8] replace and remove run_command_v_opt_cd_env_tr2()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:40:15Z","receivedAt":"2022-10-27T16:40:30Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"The convenience function run_command_v_opt_cd_env_tr2() has no external\ncallers left.  Inline it and remove it from the API.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n run-command.c | 10 ----------\n run-command.h | 10 +---------\n 2 files changed, 1 insertion(+), 19 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex 32fa4b0ed6..923bad37fe 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1005,12 +1005,6 @@ int run_command(struct child_process *cmd)\n }\n\n int run_command_v_opt(const char **argv, int opt)\n-{\n-\treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, NULL);\n-}\n-\n-int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n-\t\t\t\t const char *const *env, const char *tr2_class)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstrvec_pushv(&cmd.args, argv);\n@@ -1022,10 +1016,6 @@ int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n \tcmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;\n \tcmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0;\n-\tcmd.dir = dir;\n-\tif (env)\n-\t\tstrvec_pushv(&cmd.env, (const char **)env);\n-\tcmd.trace2_child_class = tr2_class;\n \treturn run_command(&cmd);\n }\n\ndiff --git a/run-command.h b/run-command.h\nindex 482da7f60c..04bd07dc7a 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -233,23 +233,15 @@ int run_auto_maintenance(int quiet);\n #define RUN_CLOSE_OBJECT_STORE\t\t(1<<7)\n\n /**\n- * Convenience functions that encapsulate a sequence of\n+ * Convenience function that encapsulate a sequence of\n  * start_command() followed by finish_command(). The argument argv\n  * specifies the program and its arguments. The argument opt is zero\n  * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,\n  * `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`\n  * that correspond to the members .no_stdin, .git_cmd,\n  * .stdout_to_stderr, .silent_exec_failure of `struct child_process`.\n- * The argument dir corresponds the member .dir. The argument env\n- * corresponds to the member .env.\n  */\n int run_command_v_opt(const char **argv, int opt);\n-/*\n- * env (the environment) is to be formatted like environ: \"VAR=VALUE\".\n- * To unset an environment variable use just \"VAR\".\n- */\n-int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n-\t\t\t\t const char *const *env, const char *tr2_class);\n\n /**\n  * Execute the given command, sending \"in\" to its stdin, and capturing its\n--\n2.38.1\n"},{"id":"465879","messageId":"72e04965-56ec-73cb-4554-9e3bc8f10cb5@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH 8/8] replace and remove run_command_v_opt()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-27T16:41:22Z","receivedAt":"2022-10-27T16:41:39Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Replace the remaining calls of run_command_v_opt() with run_command()\ncalls and explict struct child_process variables.  This is more verbose,\nbut not by much overall.  The code becomes more flexible, e.g. it's easy\nto extend to conditionally add a new argument.\n\nThen remove the now unused function and its own flag names, simplifying\nthe run-command API.\n\nSuggested-by: Jeff King <peff@peff.net>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n builtin/bisect--helper.c | 15 ++++++++++-----\n builtin/clone.c          |  8 ++++++--\n builtin/difftool.c       |  7 +++++--\n builtin/fetch.c          |  9 ++++++---\n builtin/gc.c             | 41 +++++++++++++++++++++++++++++++---------\n builtin/merge-index.c    |  4 +++-\n run-command.c            | 15 ---------------\n run-command.h            | 22 +--------------------\n shell.c                  | 17 ++++++++++++-----\n t/helper/test-trace2.c   |  4 +++-\n 10 files changed, 78 insertions(+), 64 deletions(-)\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 5c63ba6994..1d2ce8a0e1 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -764,10 +764,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a\n \t\tstrbuf_read_file(&start_head, git_path_bisect_start(), 0);\n \t\tstrbuf_trim(&start_head);\n \t\tif (!no_checkout) {\n-\t\t\tconst char *argv[] = { \"checkout\", start_head.buf,\n-\t\t\t\t\t       \"--\", NULL };\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\t\tif (run_command_v_opt(argv, RUN_GIT_CMD)) {\n+\t\t\tcmd.git_cmd = 1;\n+\t\t\tstrvec_pushl(&cmd.args, \"checkout\", start_head.buf,\n+\t\t\t\t     \"--\", NULL);\n+\t\t\tif (run_command(&cmd)) {\n \t\t\t\tres = error(_(\"checking out '%s' failed.\"\n \t\t\t\t\t\t \" Try 'git bisect start \"\n \t\t\t\t\t\t \"<valid-branch>'.\"),\n@@ -1141,9 +1143,12 @@ static int get_first_good(const char *refname UNUSED,\n\n static int do_bisect_run(const char *command)\n {\n-\tconst char *argv[] = { command, NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n \tprintf(_(\"running %s\\n\"), command);\n-\treturn run_command_v_opt(argv, RUN_USING_SHELL);\n+\tcmd.use_shell = 1;\n+\tstrvec_push(&cmd.args, command);\n+\treturn run_command(&cmd);\n }\n\n static int verify_good(const struct bisect_terms *terms, const char *command)\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 56e7775dae..0e4348686b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -865,11 +865,15 @@ static void write_refspec_config(const char *src_ref_prefix,\n\n static void dissociate_from_references(void)\n {\n-\tstatic const char* argv[] = { \"repack\", \"-a\", \"-d\", NULL };\n \tchar *alternates = git_pathdup(\"objects/info/alternates\");\n\n \tif (!access(alternates, F_OK)) {\n-\t\tif (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\tcmd.git_cmd = 1;\n+\t\tcmd.no_stdin = 1;\n+\t\tstrvec_pushl(&cmd.args, \"repack\", \"-a\", \"-d\", NULL);\n+\t\tif (run_command(&cmd))\n \t\t\tdie(_(\"cannot repack to clean up\"));\n \t\tif (unlink(alternates) && errno != ENOENT)\n \t\t\tdie_errno(_(\"cannot unlink temporary alternates file\"));\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 22bcc3444b..d7f08c8a7f 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -44,8 +44,11 @@ static int difftool_config(const char *var, const char *value, void *cb)\n\n static int print_tool_help(void)\n {\n-\tconst char *argv[] = { \"mergetool\", \"--tool-help=diff\", NULL };\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tcmd.git_cmd = 1;\n+\tstrvec_pushl(&cmd.args, \"mergetool\", \"--tool-help=diff\", NULL);\n+\treturn run_command(&cmd);\n }\n\n static int parse_index_info(char *p, int *mode1, int *mode2,\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a0fca93bb6..dd060dd65a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1965,14 +1965,17 @@ static int fetch_multiple(struct string_list *list, int max_children)\n \t} else\n \t\tfor (i = 0; i < list->nr; i++) {\n \t\t\tconst char *name = list->items[i].string;\n-\t\t\tstrvec_push(&argv, name);\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\t\tstrvec_pushv(&cmd.args, argv.v);\n+\t\t\tstrvec_push(&cmd.args, name);\n \t\t\tif (verbosity >= 0)\n \t\t\t\tprintf(_(\"Fetching %s\\n\"), name);\n-\t\t\tif (run_command_v_opt(argv.v, RUN_GIT_CMD)) {\n+\t\t\tcmd.git_cmd = 1;\n+\t\t\tif (run_command(&cmd)) {\n \t\t\t\terror(_(\"could not fetch %s\"), name);\n \t\t\t\tresult = 1;\n \t\t\t}\n-\t\t\tstrvec_pop(&argv);\n \t\t}\n\n \tstrvec_clear(&argv);\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 87ad0077d8..962bab61f9 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -167,9 +167,11 @@ static void gc_config(void)\n struct maintenance_run_opts;\n static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)\n {\n-\tconst char *argv[] = { \"pack-refs\", \"--all\", \"--prune\", NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+\tcmd.git_cmd = 1;\n+\tstrvec_pushl(&cmd.args, \"pack-refs\", \"--all\", \"--prune\", NULL);\n+\treturn run_command(&cmd);\n }\n\n static int too_many_loose_objects(void)\n@@ -535,8 +537,14 @@ static void gc_before_repack(void)\n \tif (pack_refs && maintenance_task_pack_refs(NULL))\n \t\tdie(FAILED_RUN, \"pack-refs\");\n\n-\tif (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD))\n-\t\tdie(FAILED_RUN, reflog.v[0]);\n+\tif (prune_reflogs) {\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\tcmd.git_cmd = 1;\n+\t\tstrvec_pushv(&cmd.args, reflog.v);\n+\t\tif (run_command(&cmd))\n+\t\t\tdie(FAILED_RUN, reflog.v[0]);\n+\t}\n }\n\n int cmd_gc(int argc, const char **argv, const char *prefix)\n@@ -550,6 +558,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \tint daemonized = 0;\n \tint keep_largest_pack = -1;\n \ttimestamp_t dummy;\n+\tstruct child_process rerere_cmd = CHILD_PROCESS_INIT;\n\n \tstruct option builtin_gc_options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"suppress progress reporting\")),\n@@ -671,11 +680,17 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \tgc_before_repack();\n\n \tif (!repository_format_precious_objects) {\n-\t\tif (run_command_v_opt(repack.v,\n-\t\t\t\t      RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE))\n+\t\tstruct child_process repack_cmd = CHILD_PROCESS_INIT;\n+\n+\t\trepack_cmd.git_cmd = 1;\n+\t\trepack_cmd.close_object_store = 1;\n+\t\tstrvec_pushv(&repack_cmd.args, repack.v);\n+\t\tif (run_command(&repack_cmd))\n \t\t\tdie(FAILED_RUN, repack.v[0]);\n\n \t\tif (prune_expire) {\n+\t\t\tstruct child_process prune_cmd = CHILD_PROCESS_INIT;\n+\n \t\t\t/* run `git prune` even if using cruft packs */\n \t\t\tstrvec_push(&prune, prune_expire);\n \t\t\tif (quiet)\n@@ -683,18 +698,26 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \t\t\tif (has_promisor_remote())\n \t\t\t\tstrvec_push(&prune,\n \t\t\t\t\t    \"--exclude-promisor-objects\");\n-\t\t\tif (run_command_v_opt(prune.v, RUN_GIT_CMD))\n+\t\t\tprune_cmd.git_cmd = 1;\n+\t\t\tstrvec_pushv(&prune_cmd.args, prune.v);\n+\t\t\tif (run_command(&prune_cmd))\n \t\t\t\tdie(FAILED_RUN, prune.v[0]);\n \t\t}\n \t}\n\n \tif (prune_worktrees_expire) {\n+\t\tstruct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;\n+\n \t\tstrvec_push(&prune_worktrees, prune_worktrees_expire);\n-\t\tif (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD))\n+\t\tprune_worktrees_cmd.git_cmd = 1;\n+\t\tstrvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v);\n+\t\tif (run_command(&prune_worktrees_cmd))\n \t\t\tdie(FAILED_RUN, prune_worktrees.v[0]);\n \t}\n\n-\tif (run_command_v_opt(rerere.v, RUN_GIT_CMD))\n+\trerere_cmd.git_cmd = 1;\n+\tstrvec_pushv(&rerere_cmd.args, rerere.v);\n+\tif (run_command(&rerere_cmd))\n \t\tdie(FAILED_RUN, rerere.v[0]);\n\n \treport_garbage = report_pack_garbage;\ndiff --git a/builtin/merge-index.c b/builtin/merge-index.c\nindex c0383fe9df..012f52bd00 100644\n--- a/builtin/merge-index.c\n+++ b/builtin/merge-index.c\n@@ -12,6 +12,7 @@ static int merge_entry(int pos, const char *path)\n \tconst char *arguments[] = { pgm, \"\", \"\", \"\", path, \"\", \"\", \"\", NULL };\n \tchar hexbuf[4][GIT_MAX_HEXSZ + 1];\n \tchar ownbuf[4][60];\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \tif (pos >= active_nr)\n \t\tdie(\"git merge-index: %s not in the cache\", path);\n@@ -31,7 +32,8 @@ static int merge_entry(int pos, const char *path)\n \tif (!found)\n \t\tdie(\"git merge-index: %s not in the cache\", path);\n\n-\tif (run_command_v_opt(arguments, 0)) {\n+\tstrvec_pushv(&cmd.args, arguments);\n+\tif (run_command(&cmd)) {\n \t\tif (one_shot)\n \t\t\terr++;\n \t\telse {\ndiff --git a/run-command.c b/run-command.c\nindex 923bad37fe..23e100dffc 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1004,21 +1004,6 @@ int run_command(struct child_process *cmd)\n \treturn finish_command(cmd);\n }\n\n-int run_command_v_opt(const char **argv, int opt)\n-{\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tstrvec_pushv(&cmd.args, argv);\n-\tcmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;\n-\tcmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;\n-\tcmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;\n-\tcmd.silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;\n-\tcmd.use_shell = opt & RUN_USING_SHELL ? 1 : 0;\n-\tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n-\tcmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;\n-\tcmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0;\n-\treturn run_command(&cmd);\n-}\n-\n #ifndef NO_PTHREADS\n static pthread_t main_thread;\n static int main_thread_set;\ndiff --git a/run-command.h b/run-command.h\nindex 04bd07dc7a..fe2717ad11 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -151,7 +151,7 @@ struct child_process {\n\n /**\n  * The functions: child_process_init, start_command, finish_command,\n- * run_command, run_command_v_opt, child_process_clear do the following:\n+ * run_command, child_process_clear do the following:\n  *\n  * - If a system call failed, errno is set and -1 is returned. A diagnostic\n  *   is printed.\n@@ -223,26 +223,6 @@ int run_command(struct child_process *);\n  */\n int run_auto_maintenance(int quiet);\n\n-#define RUN_COMMAND_NO_STDIN\t\t(1<<0)\n-#define RUN_GIT_CMD\t\t\t(1<<1)\n-#define RUN_COMMAND_STDOUT_TO_STDERR\t(1<<2)\n-#define RUN_SILENT_EXEC_FAILURE\t\t(1<<3)\n-#define RUN_USING_SHELL\t\t\t(1<<4)\n-#define RUN_CLEAN_ON_EXIT\t\t(1<<5)\n-#define RUN_WAIT_AFTER_CLEAN\t\t(1<<6)\n-#define RUN_CLOSE_OBJECT_STORE\t\t(1<<7)\n-\n-/**\n- * Convenience function that encapsulate a sequence of\n- * start_command() followed by finish_command(). The argument argv\n- * specifies the program and its arguments. The argument opt is zero\n- * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,\n- * `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`\n- * that correspond to the members .no_stdin, .git_cmd,\n- * .stdout_to_stderr, .silent_exec_failure of `struct child_process`.\n- */\n-int run_command_v_opt(const char **argv, int opt);\n-\n /**\n  * Execute the given command, sending \"in\" to its stdin, and capturing its\n  * stdout and stderr in the \"out\" and \"err\" strbufs. Any of the three may\ndiff --git a/shell.c b/shell.c\nindex 7ff4109db7..af0d7c734f 100644\n--- a/shell.c\n+++ b/shell.c\n@@ -52,21 +52,24 @@ static void cd_to_homedir(void)\n static void run_shell(void)\n {\n \tint done = 0;\n-\tstatic const char *help_argv[] = { HELP_COMMAND, NULL };\n+\tstruct child_process help_cmd = CHILD_PROCESS_INIT;\n\n \tif (!access(NOLOGIN_COMMAND, F_OK)) {\n \t\t/* Interactive login disabled. */\n-\t\tconst char *argv[] = { NOLOGIN_COMMAND, NULL };\n+\t\tstruct child_process nologin_cmd = CHILD_PROCESS_INIT;\n \t\tint status;\n\n-\t\tstatus = run_command_v_opt(argv, 0);\n+\t\tstrvec_push(&nologin_cmd.args, NOLOGIN_COMMAND);\n+\t\tstatus = run_command(&nologin_cmd);\n \t\tif (status < 0)\n \t\t\texit(127);\n \t\texit(status);\n \t}\n\n \t/* Print help if enabled */\n-\trun_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);\n+\thelp_cmd.silent_exec_failure = 1;\n+\tstrvec_push(&help_cmd.args, HELP_COMMAND);\n+\trun_command(&help_cmd);\n\n \tdo {\n \t\tconst char *prog;\n@@ -125,9 +128,13 @@ static void run_shell(void)\n \t\t\t   !strcmp(prog, \"exit\") || !strcmp(prog, \"bye\")) {\n \t\t\tdone = 1;\n \t\t} else if (is_valid_cmd_name(prog)) {\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n \t\t\tfull_cmd = make_cmd(prog);\n \t\t\targv[0] = full_cmd;\n-\t\t\tcode = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);\n+\t\t\tcmd.silent_exec_failure = 1;\n+\t\t\tstrvec_pushv(&cmd.args, argv);\n+\t\t\tcode = run_command(&cmd);\n \t\t\tif (code == -1 && errno == ENOENT) {\n \t\t\t\tfprintf(stderr, \"unrecognized command '%s'\\n\", prog);\n \t\t\t}\ndiff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c\nindex a714130ece..94fd8ccf51 100644\n--- a/t/helper/test-trace2.c\n+++ b/t/helper/test-trace2.c\n@@ -132,6 +132,7 @@ static int ut_003error(int argc, const char **argv)\n  */\n static int ut_004child(int argc, const char **argv)\n {\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint result;\n\n \t/*\n@@ -141,7 +142,8 @@ static int ut_004child(int argc, const char **argv)\n \tif (!argc)\n \t\treturn 0;\n\n-\tresult = run_command_v_opt(argv, 0);\n+\tstrvec_pushv(&cmd.args, argv);\n+\tresult = run_command(&cmd);\n \texit(result);\n }\n\n--\n2.38.1\n"},{"id":"465889","messageId":"xmqq8rl1m89b.fsf@gitster.g","threadId":"58704","inReplyTo":"672e4176-81b4-bb9c-5a14-e558d24b0c0d@web.de","subject":"Re: [PATCH 3/8] use child_process members \"args\" and \"env\" directly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-10-27T18:28:00Z","receivedAt":"2022-10-27T18:29:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"René Scharfe <l.s.r@web.de> writes:\n\n> Build argument list and environment of child processes by using\n> struct child_process and populating its members \"args\" and \"env\"\n> directly instead of maintaining separate strvecs and letting\n> run_command_v_opt() and friends populate these members.  This is\n> simpler, shorter and slightly more efficient.\n\nNice.\n"},{"id":"465903","messageId":"Y1rl9arDRGay0CHm@coredump.intra.peff.net","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2022-10-27T20:11:33Z","receivedAt":"2022-10-27T20:11:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Oct 27, 2022 at 06:30:36PM +0200, René Scharfe wrote:\n\n> Replace the convenience functions run_command_v_opt() et. al. and use\n> struct child_process and run_command() directly instead, for an overall\n> code reduction and a simpler and more flexible API that allows creating\n> argument lists without magic numbers and reduced risk of memory leaks.\n> \n> This is a broken-out and polished version of the original scratch at\n> https://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\n\nI read through this and it all looks fine to me. I was a bit puzzled at\nthe layout of your series at first. In particular, the difference\nbetween cases in patch 4 versus the later ones.\n\nI think it is that in patch 4, these are all unambiguously positive\nbecause we are getting rid of magic numbers (or magically-sized arrays).\nWhereas in patches 5-8, there's nothing inherently wrong with the\ncall-sites; but as we get rid of the API wrappers, we convert them. So\nthey are collateral damage, so to speak, from the simplification of the\nAPI.\n\nThat makes sense to me, though I could point out that most of the sites\ncleaned up in patch 4 _could_ be converted to look like the ones that\nare converted in 5-8. Obviously that doesn't make sense to do, knowing\nthat 5-8 are coming. But if the point in splitting it this way is to\nshow that we could stop at patch 4, cleaning up call sites but not\nshrinking the run-command API, then I just want to point out that there\nis another way to do those cleanups. :)\n\nAll that said, you will be unsurprised to hear that I am on board with\nthe direction of 5-8, so this all looks good to me. I'm just laying out\nmy thought process while reviewing.\n\nI also think it would have been fine to just drop the whole API in one\npatch (i.e., squashing 5-8), which makes the intermediate diffs a bit\nshorter. But I do not mind at all having the cases broken out.\n\nThanks for cleaning this up.\n\n-Peff\n"},{"id":"465907","messageId":"221027.864jvpau2d.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"e72a1abe-3098-e4fb-f064-a8b5c8f14239@web.de","subject":"Re: [PATCH 5/8] replace and remove run_command_v_opt_cd_env()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-27T20:16:54Z","receivedAt":"2022-10-27T20:30:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 27 2022, René Scharfe wrote:\n\n> run_command_v_opt_cd_env() is only used in an example in a comment.\n\nIs it? I could have sworn that....\n\n....ah, it is used be before this series, but whereas I arranged it as:\n\n 1. Remove run_command_v_opt_cd_env() and its last user:\n    https://lore.kernel.org/git/patch-v2-07.10-3b3d3777232-20221017T170316Z-avarab@gmail.com/\n 2. Remove run_command_v_opt_tr2() and its last user:\n    https://lore.kernel.org/git/patch-v2-08.10-4f1a051823f-20221017T170316Z-avarab@gmail.com/\n\nYou are:\n\n 1. In your 3/8 you rewrite a bunch of callers, and one of those is the\n    odd one out in 3/8 using run_command_v_opt_cd_env().\n 2. There's an in-between unrelated cleanup in 4/8\n 3. This 5/8 commit, removing the now-stale run_command_v_opt_cd_env().\n 4. A nicely atomic 6/8 removing run_command_v_opt_tr2() and its last caller\n 5. Your 7/8 has the run_command_v_opt_cd_env_tr2(), but unlike this\n    commit you didn't bundle up the \"Use [...it...] directly\" like here.\n\nAnyway, I find the end state to be mostly OK, but FWIW I wouldn't mind\nif this were a bit less confusing.\n\nYou seem to have ended up with this because of grouping the '\"args\" and\n\"env\" directly' callers together.\n\nFWIW I'd be fine with just squashing most of this together, it's already\na ~200 line commit, the commit message could just call out these\nexceptions.\n\n*Or* do it more incrementally, but then the choice of not doing the last\ncallers of the odd ones out atomically seems a bit weird....\n\nAnyway, if you want to just keep it as it is I'm also fine with it. I\nmainly wrote the above while narrating the state of this to myself, to\nsee if there were any lurking issues...\n\n> Use\n> the struct child_process member \"env\" and run_command() directly instead\n> and then remove the unused convenience function.\n>\n> Signed-off-by: René Scharfe <l.s.r@web.de>\n> ---\n>  run-command.c | 7 +------\n>  run-command.h | 4 +---\n>  tmp-objdir.h  | 4 ++--\n>  3 files changed, 4 insertions(+), 11 deletions(-)\n>\n> diff --git a/run-command.c b/run-command.c\n> index 5ec3a46dcc..1c9ed510f8 100644\n> --- a/run-command.c\n> +++ b/run-command.c\n> @@ -1006,7 +1006,7 @@ int run_command(struct child_process *cmd)\n>\n>  int run_command_v_opt(const char **argv, int opt)\n>  {\n> -\treturn run_command_v_opt_cd_env(argv, opt, NULL, NULL);\n> +\treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, NULL);\n>  }\n>\n>  int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n> @@ -1014,11 +1014,6 @@ int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n>  \treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class);\n>  }\n>\n> -int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)\n> -{\n> -\treturn run_command_v_opt_cd_env_tr2(argv, opt, dir, env, NULL);\n> -}\n> -\n>  int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n>  \t\t\t\t const char *const *env, const char *tr2_class)\n>  {\n> diff --git a/run-command.h b/run-command.h\n> index 0e85e5846a..a5e210fd1a 100644\n> --- a/run-command.h\n> +++ b/run-command.h\n> @@ -151,8 +151,7 @@ struct child_process {\n>\n>  /**\n>   * The functions: child_process_init, start_command, finish_command,\n> - * run_command, run_command_v_opt, run_command_v_opt_cd_env, child_process_clear\n> - * do the following:\n> + * run_command, run_command_v_opt, child_process_clear do the following:\n>   *\n>   * - If a system call failed, errno is set and -1 is returned. A diagnostic\n>   *   is printed.\n> @@ -250,7 +249,6 @@ int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class);\n>   * env (the environment) is to be formatted like environ: \"VAR=VALUE\".\n>   * To unset an environment variable use just \"VAR\".\n>   */\n> -int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);\n>  int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n>  \t\t\t\t const char *const *env, const char *tr2_class);\n>\n> diff --git a/tmp-objdir.h b/tmp-objdir.h\n> index 76efc7edee..615b188573 100644\n> --- a/tmp-objdir.h\n> +++ b/tmp-objdir.h\n> @@ -11,8 +11,8 @@\n>   * Example:\n>   *\n>   *\tstruct tmp_objdir *t = tmp_objdir_create(\"incoming\");\n> - *\tif (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&\n> - *\t    !tmp_objdir_migrate(t))\n> + *\tstrvec_pushv(&cmd.env, tmp_objdir_env(t));\n> + *\tif (!run_command(&cmd)) && !tmp_objdir_migrate(t))\n>   *\t\tprintf(\"success!\\n\");\n>   *\telse\n>   *\t\tdie(\"failed...tmp_objdir will clean up for us\");\n\n"},{"id":"465909","messageId":"221027.86zgdh9cqg.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"0e889c96-6004-96e4-9505-19ce1e7f9900@web.de","subject":"Re: [PATCH 4/8] use child_process member \"args\" instead of string array variable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-27T21:09:46Z","receivedAt":"2022-10-27T21:29:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 27 2022, René Scharfe wrote:\n\n> @@ -729,20 +727,22 @@ static int is_expected_rev(const struct object_id *oid)\n>  enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n>  \t\t\t\t  int no_checkout)\n>  {\n> -\tchar bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n>  \tstruct commit *commit;\n>  \tstruct pretty_print_context pp = {0};\n>  \tstruct strbuf commit_msg = STRBUF_INIT;\n>\n> -\toid_to_hex_r(bisect_rev_hex, bisect_rev);\n>  \tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n>\n> -\targv_checkout[2] = bisect_rev_hex;\n>  \tif (no_checkout) {\n>  \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n>  \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n>  \t} else {\n> -\t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n> +\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> +\n> +\t\tcmd.git_cmd = 1;\n> +\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n> +\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n> +\t\tif (run_command(&cmd))\n>  \t\t\t/*\n>  \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n>  \t\t\t * and errors in the child process, signaled by res > 0\n\nPerhaps I went overboard with it in my version, but it's probably worth\nmentioning when converting some of these that the reason for the\npre-image of some is really not like the others.\n\nNow that we're on C99 it perhaps make s no difference, but the pre-image\nhere is explicitly trying to avoid dynamic initializer elements, per\n442c27dde78 (CodingGuidelines: mention dynamic C99 initializer elements,\n2022-10-10).\n\nWell, partially, some of it appears to just be based on a\nmisunderstanding of how our own APIs work, i.e. the use of\noid_to_hex_r() over oid_to_hex().\n\n> diff --git a/builtin/am.c b/builtin/am.c\n> index 39fea24833..20aea0d248 100644\n> --- a/builtin/am.c\n> +++ b/builtin/am.c\n> @@ -2187,14 +2187,12 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n>  \tint len;\n>\n>  \tif (!is_null_oid(&state->orig_commit)) {\n> -\t\tconst char *av[4] = { \"show\", NULL, \"--\", NULL };\n> -\t\tchar *new_oid_str;\n> -\t\tint ret;\n> +\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>\n> -\t\tav[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));\n> -\t\tret = run_command_v_opt(av, RUN_GIT_CMD);\n> -\t\tfree(new_oid_str);\n> -\t\treturn ret;\n> +\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n> +\t\t\t     \"--\", NULL);\n> +\t\tcmd.git_cmd = 1;\n> +\t\treturn run_command(&cmd);\n>  \t}\n\nThe same goes for this, FWIW I split this one out into its own commit (I\nleft the earlier one alone):\nhttps://lore.kernel.org/git/patch-v2-04.10-5cfd6a94ce3-20221017T170316Z-avarab@gmail.com/;\nIt uses the same pattern\n\n> diff --git a/builtin/difftool.c b/builtin/difftool.c\n> index 4b10ad1a36..22bcc3444b 100644\n> --- a/builtin/difftool.c\n> +++ b/builtin/difftool.c\n> @@ -360,8 +360,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n>  \tstruct pair_entry *entry;\n>  \tstruct index_state wtindex;\n>  \tstruct checkout lstate, rstate;\n> -\tint flags = RUN_GIT_CMD, err = 0;\n> -\tconst char *helper_argv[] = { \"difftool--helper\", NULL, NULL, NULL };\n> +\tint err = 0;\n> +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\nIn general, I like the disection of this series, but with this...\n\n>  \tstruct hashmap wt_modified, tmp_modified;\n>  \tint indices_loaded = 0;\n>\n> @@ -563,16 +563,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n>  \t}\n>\n>  \tstrbuf_setlen(&ldir, ldir_len);\n> -\thelper_argv[1] = ldir.buf;\n>  \tstrbuf_setlen(&rdir, rdir_len);\n> -\thelper_argv[2] = rdir.buf;\n>\n>  \tif (extcmd) {\n> -\t\thelper_argv[0] = extcmd;\n> -\t\tflags = 0;\n> -\t} else\n> +\t\tstrvec_push(&cmd.args, extcmd);\n> +\t} else {\n> +\t\tstrvec_push(&cmd.args, \"difftool--helper\");\n> +\t\tcmd.git_cmd = 1;\n\n...and the frequent occurance of just e.g. \"cmd.git_cmd = 1\" and nothing\nelse I'm wondering if we're not throwing the baby out with the bath\nwater in having no convenience wrappers or macros at all.\n\nA lot of your 3-lines would be 1 lines if we just had e.g. (untested,\nand could be a function not a macro, but you get the idea):\n\n\t#define run_command_git_simple(__VA_ARGS__) \\\n\t\tstruct child_process cmd = CHILD_PROCESS_INIT; \\\n\t\tcmd.git_cmd = 1; \\\n\t\tstrvec_pushl(&cmd.args, __VA_ARGS__); \\\n\t\trun_command(&cmd);\n\nBut maybe nobody except me thinks that's worthwhile...\n\n>  static void read_empty(const struct object_id *oid)\n>  {\n> -\tint i = 0;\n> -\tconst char *args[7];\n> -\n> -\targs[i++] = \"read-tree\";\n> -\targs[i++] = \"-m\";\n> -\targs[i++] = \"-u\";\n> -\targs[i++] = empty_tree_oid_hex();\n> -\targs[i++] = oid_to_hex(oid);\n> -\targs[i] = NULL;\n> +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> +\n> +\tstrvec_pushl(&cmd.args, \"read-tree\", \"-m\", \"-u\", empty_tree_oid_hex(),\n> +\t\t     oid_to_hex(oid), NULL);\n> +\tcmd.git_cmd = 1;\n>\n> -\tif (run_command_v_opt(args, RUN_GIT_CMD))\n> +\tif (run_command(&cmd))\n>  \t\tdie(_(\"read-tree failed\"));\n>  }\n>\n>  static void reset_hard(const struct object_id *oid)\n>  {\n> -\tint i = 0;\n> -\tconst char *args[6];\n> -\n> -\targs[i++] = \"read-tree\";\n> -\targs[i++] = \"-v\";\n> -\targs[i++] = \"--reset\";\n> -\targs[i++] = \"-u\";\n> -\targs[i++] = oid_to_hex(oid);\n> -\targs[i] = NULL;\n> +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> +\n> +\tstrvec_pushl(&cmd.args, \"read-tree\", \"-v\", \"--reset\", \"-u\",\n> +\t\t     oid_to_hex(oid), NULL);\n> +\tcmd.git_cmd = 1;\n>\n> -\tif (run_command_v_opt(args, RUN_GIT_CMD))\n> +\tif (run_command(&cmd))\n>  \t\tdie(_(\"read-tree failed\"));\n>  }\n\nTwo perfect examples, e.g. the former would just be:\n\n\tif (run_command_git_simple(\"read-tree\", \"-m\", \"-u\", empty_tree_oid_hex(),\n\t\t\t\t   oid_to_hex(oid), NULL))\n\t\tdie(...);\n"},{"id":"465911","messageId":"221028.86v8o4aoiq.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"033ebd37-40c7-01bc-e9bb-29d738532125@web.de","subject":"Re: [PATCH 2/8] bisect--helper: factor out do_bisect_run()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-27T22:26:51Z","receivedAt":"2022-10-27T22:29:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 27 2022, René Scharfe wrote:\n\n> Deduplicate the code for reporting and starting the bisect run command\n> by moving it to a short helper function.  Use a string array instead of\n> a strvec to prepare the arguments, for simplicity.\n>\n> Signed-off-by: René Scharfe <l.s.r@web.de>\n> ---\n>  builtin/bisect--helper.c | 22 +++++++++++-----------\n>  1 file changed, 11 insertions(+), 11 deletions(-)\n>\n> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\n> index 28ef7ec2a4..70d1e9d1ad 100644\n> --- a/builtin/bisect--helper.c\n> +++ b/builtin/bisect--helper.c\n> @@ -1142,8 +1142,14 @@ static int get_first_good(const char *refname UNUSED,\n>  \treturn 1;\n>  }\n>\n> -static int verify_good(const struct bisect_terms *terms,\n> -\t\t       const char **quoted_argv)\n> +static int do_bisect_run(const char *command)\n> +{\n> +\tconst char *argv[] = { command, NULL };\n> +\tprintf(_(\"running %s\\n\"), command);\n> +\treturn run_command_v_opt(argv, RUN_USING_SHELL);\n> +}\n> +\n> +static int verify_good(const struct bisect_terms *terms, const char *command)\n>  {\n>  \tint rc;\n>  \tenum bisect_error res;\n> @@ -1163,8 +1169,7 @@ static int verify_good(const struct bisect_terms *terms,\n>  \tif (res != BISECT_OK)\n>  \t\treturn -1;\n>\n> -\tprintf(_(\"running %s\\n\"), quoted_argv[0]);\n> -\trc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);\n> +\trc = do_bisect_run(command);\n>\n>  \tres = bisect_checkout(&current_rev, no_checkout);\n>  \tif (res != BISECT_OK)\n> [...]\n\nOkey, so we end up with a helper just for the convenience of doing that\nprintf at the start, that's fine.\n\nBut given the line count of some of the other changes, and\ne.g. including the free(), oid_to_hex_r() to oid_to_hex() etc. in later\ncommits I don't see why we can't just make it use run_command()\ndirectly.\n\nI.e. I think it makes sense as one commit, but the conversion is easy\nenough, easier than looking at the same code again later in the\nseries...\n"},{"id":"465912","messageId":"221028.86r0ysao1c.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"672e4176-81b4-bb9c-5a14-e558d24b0c0d@web.de","subject":"Re: [PATCH 3/8] use child_process members \"args\" and \"env\" directly","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-27T22:37:09Z","receivedAt":"2022-10-27T22:40:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 27 2022, René Scharfe wrote:\n\n> Build argument list and environment of child processes by using\n> struct child_process and populating its members \"args\" and \"env\"\n> directly instead of maintaining separate strvecs and letting\n> run_command_v_opt() and friends populate these members.  This is\n> simpler, shorter and slightly more efficient.\n\nGeneral comment: Here you make 2x of the flags in in run-command.h\norphans, we could just remove them here atomically. I.e. squash in part\nof 8/8.\n\n>  static int similarity_index(struct diff_filepair *p)\n> diff --git a/git.c b/git.c\n> index ee7758dcb0..6662548986 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -787,7 +787,7 @@ static int run_argv(int *argcp, const char ***argv)\n>  \t\tif (!done_alias)\n>  \t\t\thandle_builtin(*argcp, *argv);\n>  \t\telse if (get_builtin(**argv)) {\n> -\t\t\tstruct strvec args = STRVEC_INIT;\n> +\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>  \t\t\tint i;\n>\n>  \t\t\t/*\n> @@ -804,18 +804,21 @@ static int run_argv(int *argcp, const char ***argv)\n>\n>  \t\t\tcommit_pager_choice();\n>\n> -\t\t\tstrvec_push(&args, \"git\");\n> +\t\t\tstrvec_push(&cmd.args, \"git\");\n>  \t\t\tfor (i = 0; i < *argcp; i++)\n> -\t\t\t\tstrvec_push(&args, (*argv)[i]);\n> +\t\t\t\tstrvec_push(&cmd.args, (*argv)[i]);\n>\n> -\t\t\ttrace_argv_printf(args.v, \"trace: exec:\");\n> +\t\t\ttrace_argv_printf(cmd.args.v, \"trace: exec:\");\n>\n>  \t\t\t/*\n>  \t\t\t * if we fail because the command is not found, it is\n>  \t\t\t * OK to return. Otherwise, we just pass along the status code.\n>  \t\t\t */\n> -\t\t\ti = run_command_v_opt_tr2(args.v, RUN_SILENT_EXEC_FAILURE |\n> -\t\t\t\t\t\t  RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, \"git_alias\");\n\n^^ here, I didn't look carefully at the rest of the commits, but maybe\nthere's similar squashes elsewhere...\n"},{"id":"465913","messageId":"221028.86mt9gants.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"72e04965-56ec-73cb-4554-9e3bc8f10cb5@web.de","subject":"Re: [PATCH 8/8] replace and remove run_command_v_opt()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-27T22:41:53Z","receivedAt":"2022-10-27T22:44:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 27 2022, René Scharfe wrote:\n\n>  #ifndef NO_PTHREADS\n>  static pthread_t main_thread;\n>  static int main_thread_set;\n> diff --git a/run-command.h b/run-command.h\n> index 04bd07dc7a..fe2717ad11 100644\n> --- a/run-command.h\n> +++ b/run-command.h\n> @@ -151,7 +151,7 @@ struct child_process {\n>\n>  /**\n>   * The functions: child_process_init, start_command, finish_command,\n> - * run_command, run_command_v_opt, child_process_clear do the following:\n> + * run_command, child_process_clear do the following:\n>   *\n>   * - If a system call failed, errno is set and -1 is returned. A diagnostic\n>   *   is printed.\n\nA pre-existing issue mostly, but maybe worth cleaning up while we're\nfixing these docs in general.\n\nThis summary is incorrect, because the first bullet point is claiming\nthat these \"return -1\", but 2 functions on this list return void.\n\nIt looks to me if we just remove child_process_{init,clear} altgother\nfrom this it'll be correct. They have their own docs below, and this is\nreally describing how this now-simpler API works vis-a-vis the\nstart/run/finish functions.\n"},{"id":"465914","messageId":"221028.86ilk4ano3.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"f26f7974-3e87-2ddf-6eeb-5d97f80a4e0d@web.de","subject":"Re: [PATCH 7/8] replace and remove run_command_v_opt_cd_env_tr2()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-27T22:46:02Z","receivedAt":"2022-10-27T22:48:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 27 2022, René Scharfe wrote:\n\n> diff --git a/run-command.h b/run-command.h\n> index 482da7f60c..04bd07dc7a 100644\n> --- a/run-command.h\n> +++ b/run-command.h\n> @@ -233,23 +233,15 @@ int run_auto_maintenance(int quiet);\n>  #define RUN_CLOSE_OBJECT_STORE\t\t(1<<7)\n>\n>  /**\n> - * Convenience functions that encapsulate a sequence of\n> + * Convenience function that encapsulate a sequence of\n\nMaybe we shouldn't bother, because we remove this altogether in 8/8, but\nthis has a grammar error, should be:\n\n\tConvenience function that encapsulates a sequence of\n\nOr:\n\n\tA convenience function that encapsulates a sequence of\n\nNot:\n\n\tConvenience function that encapsulate a sequence of\n\nIf you're re-rolling I think just dropping this hunk is better.\n\nIt can just go away entirely in 8/8. The pre-image had bad grammar\nanyway, so not worth fixing just to remove it later.\n"},{"id":"465915","messageId":"221028.86edusan0k.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-27T21:46:50Z","receivedAt":"2022-10-27T23:02:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Oct 27 2022, René Scharfe wrote:\n\n> Replace the convenience functions run_command_v_opt() et. al. and use\n> struct child_process and run_command() directly instead, for an overall\n> code reduction and a simpler and more flexible API that allows creating\n> argument lists without magic numbers and reduced risk of memory leaks.\n>\n> This is a broken-out and polished version of the original scratch at\n> https://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\n>\n> Ævar Arnfjörð Bjarmason (1):\n>   merge: remove always-the-same \"verbose\" arguments\n>\n> René Scharfe (7):\n>   bisect--helper: factor out do_bisect_run()\n>   use child_process members \"args\" and \"env\" directly\n>   use child_process member \"args\" instead of string array variable\n>   replace and remove run_command_v_opt_cd_env()\n>   replace and remove run_command_v_opt_tr2()\n>   replace and remove run_command_v_opt_cd_env_tr2()\n>   replace and remove run_command_v_opt()\n\nEven though I had a an earlier alternate series series[1] for this I'd\nbe happy to see this version go in. I left some comments here and there,\nbut with/without a re-roll am happy to give this:\n\n\tReviewed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nI think I would have just gone for this in the first place, but thought\nthat people loved the convenience functions too much. It can be hard to\njudge sentiments in advance :)\n\nOne reason I hadn't re-submitted something is that there were\noutstanding new conflicts with \"seen\", and I see from applying this\ntopic & merging it that it conflicts somewhat heavily. Junio seems to be\non-board with this though, so maybe he won't mind.\n\nI didn't see any glaring instances where this made things worse, so\nmaybe we didn't need these convenience wrappers in the first place.\n\nBut from the earlier discussion it does seema bit like we tossed the\nvery notion out because some didn't like the duplicating of struct\nmembers with the flags (which I also doen't like).\n\nSo I came up with the below experiment on top, it's not an attempt to\nconvert all callers, just a demo.\n\nMaybe you think some ideas here are worth using, I probably won't pursue\nit (but maybe as ideas for some other future API).\n\nIt's a combination of stuff, some of which you might like, some not,\nnamely:\n\n- Have the API work the same way, but just have a run_commandl(&opt,\n  ...) in addition to the run_command(). It's just a trivial wrapper to\n  push stuff into &cmd.args for you.\n\n- I saw a few callers that could have perhaps used a similarly trivial\n  run_commandv(), but that doesn't benefit from LAST_ARG_MUST_BE_NULL,\n  so I didn't bother.\n\n- I wish C had a nicer syntax for not just declaring but squashing\n  together compile_time bracketed lists (think set operations). But the\n  below \"CHILD_PROCESS_INIT_LIST\" is a pretty good poor man's version.\n\n  I see gcc/clang nicely give us safety rails for that with\n  \"-Woverride-init\", for this sort of \"opts struct with internal stuff,\n  but also user options\" I think it works out very nicely.\n\n- We have quite a few callers that want \"on error, die\", so maybe we\n  should have something like that \"on_error\" sooner than later.\n\nOn clever (but perhaps overly clever) thing I didn't use, but played\nwith recently in another context, is that now with C99 you can also do:\n\n\tint run_commandl(struct child_process *cmd, ...);\n\t#define run_command(...) run_command_1(__VA_ARGS__, NULL)\n\nI.e. make the API itself support all of:\n\n\trun_command(&cmd);\n\trun_command(&cmd, \"reboot\");\n\trun_command(&cmd, \"reboot\", NULL);\n\nI haven't made up my mind on whether that's just overly clever, or\noutright insane :)\n\ndiff --git a/bisect.c b/bisect.c\nindex ec7487e6836..ef4f80650f7 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -740,9 +740,8 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \n \t\tcmd.git_cmd = 1;\n-\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n-\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n-\t\tif (run_command(&cmd))\n+\t\tif (run_commandl(&cmd, \"checkout\", \"-q\",\n+\t\t\t\t oid_to_hex(bisect_rev), \"--\", NULL))\n \t\t\t/*\n \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n \t\t\t * and errors in the child process, signaled by res > 0\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 20aea0d2487..3b7df32ce22 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -2189,10 +2189,9 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n \tif (!is_null_oid(&state->orig_commit)) {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \n-\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n-\t\t\t     \"--\", NULL);\n \t\tcmd.git_cmd = 1;\n-\t\treturn run_command(&cmd);\n+\t\treturn run_commandl(&cmd, \"show\", oid_to_hex(&state->orig_commit),\n+\t\t\t\t    \"--\", NULL);\n \t}\n \n \tswitch (sub_mode) {\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 1d2ce8a0e12..087d21c614a 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -764,12 +764,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a\n \t\tstrbuf_read_file(&start_head, git_path_bisect_start(), 0);\n \t\tstrbuf_trim(&start_head);\n \t\tif (!no_checkout) {\n-\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\n-\t\t\tcmd.git_cmd = 1;\n-\t\t\tstrvec_pushl(&cmd.args, \"checkout\", start_head.buf,\n-\t\t\t\t     \"--\", NULL);\n-\t\t\tif (run_command(&cmd)) {\n+\t\t\tstruct child_process cmd = {\n+\t\t\t\tCHILD_PROCESS_INIT_LIST,\n+\t\t\t\t.git_cmd = 1,\n+\t\t\t};\n+\t\t\tif (run_commandl(&cmd, \"checkout\", start_head.buf,\n+\t\t\t\t\t \"--\", NULL)) {\n \t\t\t\tres = error(_(\"checking out '%s' failed.\"\n \t\t\t\t\t\t \" Try 'git bisect start \"\n \t\t\t\t\t\t \"<valid-branch>'.\"),\n@@ -1147,8 +1147,7 @@ static int do_bisect_run(const char *command)\n \n \tprintf(_(\"running %s\\n\"), command);\n \tcmd.use_shell = 1;\n-\tstrvec_push(&cmd.args, command);\n-\treturn run_command(&cmd);\n+\treturn run_commandl(&cmd, command, NULL);\n }\n \n static int verify_good(const struct bisect_terms *terms, const char *command)\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 0e4348686b6..80d09e0fbf1 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -655,7 +655,6 @@ static int git_sparse_checkout_init(const char *repo)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint result = 0;\n-\tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n \n \t/*\n \t * We must apply the setting in the current process\n@@ -664,7 +663,7 @@ static int git_sparse_checkout_init(const char *repo)\n \tcore_apply_sparse_checkout = 1;\n \n \tcmd.git_cmd = 1;\n-\tif (run_command(&cmd)) {\n+\tif (run_commandl(&cmd, \"-C\", repo, \"sparse-checkout\", \"set\", NULL)) {\n \t\terror(_(\"failed to initialize sparse-checkout\"));\n \t\tresult = 1;\n \t}\n@@ -868,13 +867,14 @@ static void dissociate_from_references(void)\n \tchar *alternates = git_pathdup(\"objects/info/alternates\");\n \n \tif (!access(alternates, F_OK)) {\n-\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\n-\t\tcmd.git_cmd = 1;\n-\t\tcmd.no_stdin = 1;\n-\t\tstrvec_pushl(&cmd.args, \"repack\", \"-a\", \"-d\", NULL);\n-\t\tif (run_command(&cmd))\n-\t\t\tdie(_(\"cannot repack to clean up\"));\n+\t\tstruct child_process cmd = {\n+\t\t\tCHILD_PROCESS_INIT_LIST,\n+\t\t\t.git_cmd = 1,\n+\t\t\t.no_stdin = 1,\n+\t\t\t.on_error = CHILD_PROCESS_ON_ERROR_DIE,\n+\t\t};\n+\n+\t\trun_commandl(&cmd, \"repack\", \"-a\", \"-d\", NULL);\n \t\tif (unlink(alternates) && errno != ENOENT)\n \t\t\tdie_errno(_(\"cannot unlink temporary alternates file\"));\n \t}\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex d7f08c8a7fa..b4165b5a8ae 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -44,11 +44,12 @@ static int difftool_config(const char *var, const char *value, void *cb)\n \n static int print_tool_help(void)\n {\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\n-\tcmd.git_cmd = 1;\n-\tstrvec_pushl(&cmd.args, \"mergetool\", \"--tool-help=diff\", NULL);\n-\treturn run_command(&cmd);\n+\tstruct child_process cmd = {\n+\t\tCHILD_PROCESS_INIT_LIST,\n+\t\t.git_cmd = 1,\n+\t};\n+\t\t\n+\treturn run_commandl(&cmd, \"mergetool\", \"--tool-help=diff\", NULL);\n }\n \n static int parse_index_info(char *p, int *mode1, int *mode2,\ndiff --git a/git.c b/git.c\nindex 6662548986f..93179f44f78 100644\n--- a/git.c\n+++ b/git.c\n@@ -724,7 +724,13 @@ static void handle_builtin(int argc, const char **argv)\n \n static void execv_dashed_external(const char **argv)\n {\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tstruct child_process cmd = {\n+\t\tCHILD_PROCESS_INIT_LIST,\n+\t\t.clean_on_exit = 1,\n+\t\t.wait_after_clean = 1,\n+\t\t.silent_exec_failure = 1,\n+\t\t.trace2_child_class = \"dashed\",\n+\t};\n \tint status;\n \n \tif (get_super_prefix())\n@@ -736,10 +742,6 @@ static void execv_dashed_external(const char **argv)\n \n \tstrvec_pushf(&cmd.args, \"git-%s\", argv[0]);\n \tstrvec_pushv(&cmd.args, argv + 1);\n-\tcmd.clean_on_exit = 1;\n-\tcmd.wait_after_clean = 1;\n-\tcmd.silent_exec_failure = 1;\n-\tcmd.trace2_child_class = \"dashed\";\n \n \ttrace2_cmd_name(\"_run_dashed_\");\n \ndiff --git a/run-command.c b/run-command.c\nindex 23e100dffc4..4b20aa1b577 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -993,15 +993,45 @@ int finish_command_in_signal(struct child_process *cmd)\n \n int run_command(struct child_process *cmd)\n {\n-\tint code;\n+\tint starting = 1;\n+\tint code = 0;\n \n \tif (cmd->out < 0 || cmd->err < 0)\n \t\tBUG(\"run_command with a pipe can cause deadlock\");\n \n \tcode = start_command(cmd);\n \tif (code)\n+\t\tgoto error;\n+\tstarting = 0;\n+\tcode = finish_command(cmd);\n+\tif (!code)\n+\t\treturn 0;\n+error:\n+\tswitch (cmd->on_error) {\n+\tcase CHILD_PROCESS_ON_ERROR_DIE:\n+\t\tdie(starting ?\n+\t\t    _(\"start_command() for '%s' failed (error code %d)\") :\n+\t\t    _(\"'%s': got non-zero exit code '%d'\"),\n+\t\t    cmd->args.v[0], code);\n+\t\tbreak;\n+\tcase CHILD_PROCESS_ON_ERROR_RETURN:\n \t\treturn code;\n-\treturn finish_command(cmd);\n+\tdefault:\n+\t\tBUG(\"unreachable\");\n+\t}\n+}\n+\n+int run_commandl(struct child_process *cmd, ...)\n+{\n+\tva_list ap;\n+\tconst char *arg;\n+\n+\tva_start(ap, cmd);\n+\twhile ((arg = va_arg(ap, const char *)))\n+\t\tstrvec_push(&cmd->args, arg);\n+\tva_end(ap);\n+\n+\treturn run_command(cmd);\n }\n \n #ifndef NO_PTHREADS\ndiff --git a/run-command.h b/run-command.h\nindex fe2717ad11e..71e390350ed 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -15,7 +15,22 @@\n  * produces in the caller in order to process it.\n  */\n \n+enum child_process_on_error {\n+\t/**\n+\t * Return a status code from run_command(). Set to 0 so that\n+\t * it'll be { 0 } init'd. If it's specified in a\n+\t * CHILD_PROCESS_INIT_LIST initialization we don't want a\n+\t * \"-Woverride-init\" warning.\n+\t */\n+\tCHILD_PROCESS_ON_ERROR_RETURN = 0,\n \n+\t/**\n+\t * die() with some sensible message if run_command() would\n+\t * have returned a non-zero exit code.\n+\t */\n+\tCHILD_PROCESS_ON_ERROR_DIE,\n+};\n+\t\n /**\n  * This describes the arguments, redirections, and environment of a\n  * command to run in a sub-process.\n@@ -42,6 +57,10 @@\n  *\t\tredirected.\n  */\n struct child_process {\n+\t/**\n+\t * Error behavior, see \"enum child_process_on_error\" above.\n+\t */\n+\tenum child_process_on_error on_error;\n \n \t/**\n \t * The .args is a `struct strvec', use that API to manipulate\n@@ -144,10 +163,11 @@ struct child_process {\n \tvoid (*clean_on_exit_handler)(struct child_process *process);\n };\n \n-#define CHILD_PROCESS_INIT { \\\n+#define CHILD_PROCESS_INIT_LIST \\\n+\t/* .on_error = CHILD_PROCESS_ON_ERROR_RETURN */ \\\n \t.args = STRVEC_INIT, \\\n-\t.env = STRVEC_INIT, \\\n-}\n+\t.env = STRVEC_INIT\n+#define CHILD_PROCESS_INIT { CHILD_PROCESS_INIT_LIST }\n \n /**\n  * The functions: child_process_init, start_command, finish_command,\n@@ -218,6 +238,14 @@ int finish_command_in_signal(struct child_process *);\n  */\n int run_command(struct child_process *);\n \n+/**\n+ * Like run_command() but takes a variable number of arguments, which\n+ * will be appended with the equivalent of strvec_pushl(&cmd.args,\n+ * ...) before invoking run_command().\n+ */\n+LAST_ARG_MUST_BE_NULL\n+int run_commandl(struct child_process *cmd, ...);\n+\n /*\n  * Trigger an auto-gc\n  */\n"},{"id":"465941","messageId":"8428e83f-9deb-e928-8699-b5b13e8b7577@web.de","threadId":"58704","inReplyTo":"221028.86mt9gants.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 8/8] replace and remove run_command_v_opt()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-28T14:23:20Z","receivedAt":"2022-10-28T14:23:41Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 28.10.22 um 00:41 schrieb Ævar Arnfjörð Bjarmason:\n>\n> On Thu, Oct 27 2022, René Scharfe wrote:\n>\n>>  #ifndef NO_PTHREADS\n>>  static pthread_t main_thread;\n>>  static int main_thread_set;\n>> diff --git a/run-command.h b/run-command.h\n>> index 04bd07dc7a..fe2717ad11 100644\n>> --- a/run-command.h\n>> +++ b/run-command.h\n>> @@ -151,7 +151,7 @@ struct child_process {\n>>\n>>  /**\n>>   * The functions: child_process_init, start_command, finish_command,\n>> - * run_command, run_command_v_opt, child_process_clear do the following:\n>> + * run_command, child_process_clear do the following:\n>>   *\n>>   * - If a system call failed, errno is set and -1 is returned. A diagnostic\n>>   *   is printed.\n>\n> A pre-existing issue mostly, but maybe worth cleaning up while we're\n> fixing these docs in general.\n>\n> This summary is incorrect, because the first bullet point is claiming\n> that these \"return -1\", but 2 functions on this list return void.\n>\n> It looks to me if we just remove child_process_{init,clear} altgother\n> from this it'll be correct. They have their own docs below, and this is\n> really describing how this now-simpler API works vis-a-vis the\n> start/run/finish functions.\n\nTrue, and I added that mistake a while ago.  Oops.\n\n--- >8 ---\nSubject: [PATCH 9/8] run-command: fix return value comment\n\n483bbd4e4c (run-command: introduce child_process_init(), 2014-08-19) and\n2d71608ec0 (run-command: factor out child_process_clear(), 2015-10-24)\nadded help texts about child_process_init() and child_process_clear()\nwithout updating the immediately following documentation of return codes\nthat only applied to the preexisting functions.\n\n4c4066d95d (run-command: move doc to run-command.h, 2019-11-17) started\nto list the functions explicitly that this paragraph applies to, but\nstill wrongly included child_process_init() and child_process_clear().\nRemove their names from that list.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n run-command.h | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/run-command.h b/run-command.h\nindex fe2717ad11..7ed2c04427 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -150,8 +150,7 @@ struct child_process {\n }\n\n /**\n- * The functions: child_process_init, start_command, finish_command,\n- * run_command, child_process_clear do the following:\n+ * The functions start_command, finish_command, run_command do the following:\n  *\n  * - If a system call failed, errno is set and -1 is returned. A diagnostic\n  *   is printed.\n--\n2.38.1\n"},{"id":"465942","messageId":"6c30a1f6-8d89-31b9-faf8-c695c46173cd@web.de","threadId":"58704","inReplyTo":"221027.86zgdh9cqg.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 4/8] use child_process member \"args\" instead of string array variable","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-28T14:23:31Z","receivedAt":"2022-10-28T14:23:50Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 27.10.22 um 23:09 schrieb Ævar Arnfjörð Bjarmason:\n>\n> On Thu, Oct 27 2022, René Scharfe wrote:\n>\n>> @@ -729,20 +727,22 @@ static int is_expected_rev(const struct object_id *oid)\n>>  enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n>>  \t\t\t\t  int no_checkout)\n>>  {\n>> -\tchar bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n>>  \tstruct commit *commit;\n>>  \tstruct pretty_print_context pp = {0};\n>>  \tstruct strbuf commit_msg = STRBUF_INIT;\n>>\n>> -\toid_to_hex_r(bisect_rev_hex, bisect_rev);\n>>  \tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n>>\n>> -\targv_checkout[2] = bisect_rev_hex;\n>>  \tif (no_checkout) {\n>>  \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n>>  \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n>>  \t} else {\n>> -\t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n>> +\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>> +\n>> +\t\tcmd.git_cmd = 1;\n>> +\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n>> +\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n>> +\t\tif (run_command(&cmd))\n>>  \t\t\t/*\n>>  \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n>>  \t\t\t * and errors in the child process, signaled by res > 0\n>\n> Perhaps I went overboard with it in my version, but it's probably worth\n> mentioning when converting some of these that the reason for the\n> pre-image of some is really not like the others.\n>\n> Now that we're on C99 it perhaps make s no difference, but the pre-image\n> here is explicitly trying to avoid dynamic initializer elements, per\n> 442c27dde78 (CodingGuidelines: mention dynamic C99 initializer elements,\n> 2022-10-10).\n\nTrue, some cases could be converted to string array initializations,\nwhich also would get rid of magic numbers.  This would make the final\npatch to convert them to run_command() longer.\n\n> Well, partially, some of it appears to just be based on a\n> misunderstanding of how our own APIs work, i.e. the use of\n> oid_to_hex_r() over oid_to_hex().\n>\n>> diff --git a/builtin/am.c b/builtin/am.c\n>> index 39fea24833..20aea0d248 100644\n>> --- a/builtin/am.c\n>> +++ b/builtin/am.c\n>> @@ -2187,14 +2187,12 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n>>  \tint len;\n>>\n>>  \tif (!is_null_oid(&state->orig_commit)) {\n>> -\t\tconst char *av[4] = { \"show\", NULL, \"--\", NULL };\n>> -\t\tchar *new_oid_str;\n>> -\t\tint ret;\n>> +\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>>\n>> -\t\tav[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));\n>> -\t\tret = run_command_v_opt(av, RUN_GIT_CMD);\n>> -\t\tfree(new_oid_str);\n>> -\t\treturn ret;\n>> +\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n>> +\t\t\t     \"--\", NULL);\n>> +\t\tcmd.git_cmd = 1;\n>> +\t\treturn run_command(&cmd);\n>>  \t}\n>\n> The same goes for this, FWIW I split this one out into its own commit (I\n> left the earlier one alone):\n> https://lore.kernel.org/git/patch-v2-04.10-5cfd6a94ce3-20221017T170316Z-avarab@gmail.com/;\n> It uses the same pattern\n\nOK, I just chalked that up as \"slightly odd\" and bulldozed over them\nwithout a second thought.  Hmm.\n\n>\n>> diff --git a/builtin/difftool.c b/builtin/difftool.c\n>> index 4b10ad1a36..22bcc3444b 100644\n>> --- a/builtin/difftool.c\n>> +++ b/builtin/difftool.c\n>> @@ -360,8 +360,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n>>  \tstruct pair_entry *entry;\n>>  \tstruct index_state wtindex;\n>>  \tstruct checkout lstate, rstate;\n>> -\tint flags = RUN_GIT_CMD, err = 0;\n>> -\tconst char *helper_argv[] = { \"difftool--helper\", NULL, NULL, NULL };\n>> +\tint err = 0;\n>> +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>\n> In general, I like the disection of this series, but with this...\n>\n>>  \tstruct hashmap wt_modified, tmp_modified;\n>>  \tint indices_loaded = 0;\n>>\n>> @@ -563,16 +563,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n>>  \t}\n>>\n>>  \tstrbuf_setlen(&ldir, ldir_len);\n>> -\thelper_argv[1] = ldir.buf;\n>>  \tstrbuf_setlen(&rdir, rdir_len);\n>> -\thelper_argv[2] = rdir.buf;\n>>\n>>  \tif (extcmd) {\n>> -\t\thelper_argv[0] = extcmd;\n>> -\t\tflags = 0;\n>> -\t} else\n>> +\t\tstrvec_push(&cmd.args, extcmd);\n>> +\t} else {\n>> +\t\tstrvec_push(&cmd.args, \"difftool--helper\");\n>> +\t\tcmd.git_cmd = 1;\n>\n> ...and the frequent occurance of just e.g. \"cmd.git_cmd = 1\" and nothing\n> else I'm wondering if we're not throwing the baby out with the bath\n> water in having no convenience wrappers or macros at all.\n>\n> A lot of your 3-lines would be 1 lines if we just had e.g. (untested,\n> and could be a function not a macro, but you get the idea):\n>\n> \t#define run_command_git_simple(__VA_ARGS__) \\\n> \t\tstruct child_process cmd = CHILD_PROCESS_INIT; \\\n> \t\tcmd.git_cmd = 1; \\\n> \t\tstrvec_pushl(&cmd.args, __VA_ARGS__); \\\n> \t\trun_command(&cmd);\n>\n> But maybe nobody except me thinks that's worthwhile...\n\nI have similar temptations; you could see that in my scratch patch\nhttps://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\nwhich added run_git_or_die() in builtin/gc.c.  Why, oh why?  Perhaps\nbecause taking a blank form (CHILD_PROCESS_INIT), ticking boxes\n(.git_cmd = 1), filling out text fields (strvec_push(...)) and\nsubmitting it (run_command()) feels tedious and bureaucratic, Java-esque\neven.  And some patterns appear again and again.\n\nHow bad is that?  Is it bad at all?  I think overall we should try to\nreduce the number of external calls and make those we have to do\nself-documenting and leak-free.  A bit of tedium is OK; this API should\nbe used rarely and sparingly.  Still I get the urge to search for\npatterns and define shortcuts when I see all those similar calls..\n\nrun_command_git_simple as defined above wouldn't compile, but I get it.\nReducing the number of lines feels good, but it also makes the code less\nflexible -- adding a conditional parameter requires converting back to\nrun_command().\n\n>\n>>  static void read_empty(const struct object_id *oid)\n>>  {\n>> -\tint i = 0;\n>> -\tconst char *args[7];\n>> -\n>> -\targs[i++] = \"read-tree\";\n>> -\targs[i++] = \"-m\";\n>> -\targs[i++] = \"-u\";\n>> -\targs[i++] = empty_tree_oid_hex();\n>> -\targs[i++] = oid_to_hex(oid);\n>> -\targs[i] = NULL;\n>> +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>> +\n>> +\tstrvec_pushl(&cmd.args, \"read-tree\", \"-m\", \"-u\", empty_tree_oid_hex(),\n>> +\t\t     oid_to_hex(oid), NULL);\n>> +\tcmd.git_cmd = 1;\n>>\n>> -\tif (run_command_v_opt(args, RUN_GIT_CMD))\n>> +\tif (run_command(&cmd))\n>>  \t\tdie(_(\"read-tree failed\"));\n>>  }\n>>\n>>  static void reset_hard(const struct object_id *oid)\n>>  {\n>> -\tint i = 0;\n>> -\tconst char *args[6];\n>> -\n>> -\targs[i++] = \"read-tree\";\n>> -\targs[i++] = \"-v\";\n>> -\targs[i++] = \"--reset\";\n>> -\targs[i++] = \"-u\";\n>> -\targs[i++] = oid_to_hex(oid);\n>> -\targs[i] = NULL;\n>> +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>> +\n>> +\tstrvec_pushl(&cmd.args, \"read-tree\", \"-v\", \"--reset\", \"-u\",\n>> +\t\t     oid_to_hex(oid), NULL);\n>> +\tcmd.git_cmd = 1;\n>>\n>> -\tif (run_command_v_opt(args, RUN_GIT_CMD))\n>> +\tif (run_command(&cmd))\n>>  \t\tdie(_(\"read-tree failed\"));\n>>  }\n>\n> Two perfect examples, e.g. the former would just be:\n>\n> \tif (run_command_git_simple(\"read-tree\", \"-m\", \"-u\", empty_tree_oid_hex(),\n> \t\t\t\t   oid_to_hex(oid), NULL))\n> \t\tdie(...);\n"},{"id":"465943","messageId":"f443f3ee-95e6-edec-178a-9a06421e24f6@web.de","threadId":"58704","inReplyTo":"221028.86ilk4ano3.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 7/8] replace and remove run_command_v_opt_cd_env_tr2()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-28T14:23:26Z","receivedAt":"2022-10-28T14:23:52Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 28.10.22 um 00:46 schrieb Ævar Arnfjörð Bjarmason:\n>\n> On Thu, Oct 27 2022, René Scharfe wrote:\n>\n>> diff --git a/run-command.h b/run-command.h\n>> index 482da7f60c..04bd07dc7a 100644\n>> --- a/run-command.h\n>> +++ b/run-command.h\n>> @@ -233,23 +233,15 @@ int run_auto_maintenance(int quiet);\n>>  #define RUN_CLOSE_OBJECT_STORE\t\t(1<<7)\n>>\n>>  /**\n>> - * Convenience functions that encapsulate a sequence of\n>> + * Convenience function that encapsulate a sequence of\n>\n> Maybe we shouldn't bother, because we remove this altogether in 8/8, but\n> this has a grammar error, should be:\n>\n> \tConvenience function that encapsulates a sequence of\n>\n> Or:\n>\n> \tA convenience function that encapsulates a sequence of\n>\n> Not:\n>\n> \tConvenience function that encapsulate a sequence of\n\nAh, good catch.\n\n>\n> If you're re-rolling I think just dropping this hunk is better.\n>\n> It can just go away entirely in 8/8. The pre-image had bad grammar\n> anyway, so not worth fixing just to remove it later.\n"},{"id":"465944","messageId":"943608ab-340d-8cd3-8812-c0f65909000a@web.de","threadId":"58704","inReplyTo":"Y1rl9arDRGay0CHm@coredump.intra.peff.net","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-28T14:23:35Z","receivedAt":"2022-10-28T14:23:55Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 27.10.22 um 22:11 schrieb Jeff King:\n> On Thu, Oct 27, 2022 at 06:30:36PM +0200, René Scharfe wrote:\n>\n>> Replace the convenience functions run_command_v_opt() et. al. and use\n>> struct child_process and run_command() directly instead, for an overall\n>> code reduction and a simpler and more flexible API that allows creating\n>> argument lists without magic numbers and reduced risk of memory leaks.\n>>\n>> This is a broken-out and polished version of the original scratch at\n>> https://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\n>\n> I read through this and it all looks fine to me. I was a bit puzzled at\n> the layout of your series at first. In particular, the difference\n> between cases in patch 4 versus the later ones.\n>\n> I think it is that in patch 4, these are all unambiguously positive\n> because we are getting rid of magic numbers (or magically-sized arrays).\n> Whereas in patches 5-8, there's nothing inherently wrong with the\n> call-sites; but as we get rid of the API wrappers, we convert them. So\n> they are collateral damage, so to speak, from the simplification of the\n> API.\n>\n> That makes sense to me, though I could point out that most of the sites\n> cleaned up in patch 4 _could_ be converted to look like the ones that\n> are converted in 5-8. Obviously that doesn't make sense to do, knowing\n> that 5-8 are coming. But if the point in splitting it this way is to\n> show that we could stop at patch 4, cleaning up call sites but not\n> shrinking the run-command API, then I just want to point out that there\n> is another way to do those cleanups. :)\n\nYes, almost, except that I think 5-7 are doing necessary pruning and 8\nrequires a small leap of faith in the value of simplicity.  And I wanted\nto shrink down and simplify that last patch.\n\nRené\n"},{"id":"465956","messageId":"749f6adc-928a-0978-e3a1-2ede9f07def0@web.de","threadId":"58704","inReplyTo":"221028.86edusan0k.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-28T16:04:41Z","receivedAt":"2022-10-28T16:06:31Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 27.10.22 um 23:46 schrieb Ævar Arnfjörð Bjarmason:\n>\n> On Thu, Oct 27 2022, René Scharfe wrote:\n>\n>> Replace the convenience functions run_command_v_opt() et. al. and use\n>> struct child_process and run_command() directly instead, for an overall\n>> code reduction and a simpler and more flexible API that allows creating\n>> argument lists without magic numbers and reduced risk of memory leaks.\n>>\n>> This is a broken-out and polished version of the original scratch at\n>> https://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\n>>\n>> Ævar Arnfjörð Bjarmason (1):\n>>   merge: remove always-the-same \"verbose\" arguments\n>>\n>> René Scharfe (7):\n>>   bisect--helper: factor out do_bisect_run()\n>>   use child_process members \"args\" and \"env\" directly\n>>   use child_process member \"args\" instead of string array variable\n>>   replace and remove run_command_v_opt_cd_env()\n>>   replace and remove run_command_v_opt_tr2()\n>>   replace and remove run_command_v_opt_cd_env_tr2()\n>>   replace and remove run_command_v_opt()\n>\n> Even though I had a an earlier alternate series series[1] for this I'd\n> be happy to see this version go in. I left some comments here and there,\n> but with/without a re-roll am happy to give this:\n>\n> \tReviewed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n> I think I would have just gone for this in the first place, but thought\n> that people loved the convenience functions too much. It can be hard to\n> judge sentiments in advance :)\n>\n> One reason I hadn't re-submitted something is that there were\n> outstanding new conflicts with \"seen\", and I see from applying this\n> topic & merging it that it conflicts somewhat heavily. Junio seems to be\n> on-board with this though, so maybe he won't mind.\n>\n> I didn't see any glaring instances where this made things worse, so\n> maybe we didn't need these convenience wrappers in the first place.\n\nRight, and I think this is important.\n\n>\n> But from the earlier discussion it does seema bit like we tossed the\n> very notion out because some didn't like the duplicating of struct\n> members with the flags (which I also doen't like).\n>\n> So I came up with the below experiment on top, it's not an attempt to\n> convert all callers, just a demo.\n>\n> Maybe you think some ideas here are worth using, I probably won't pursue\n> it (but maybe as ideas for some other future API).\n>\n> It's a combination of stuff, some of which you might like, some not,\n> namely:\n>\n> - Have the API work the same way, but just have a run_commandl(&opt,\n>   ...) in addition to the run_command(). It's just a trivial wrapper to\n>   push stuff into &cmd.args for you.\n>\n> - I saw a few callers that could have perhaps used a similarly trivial\n>   run_commandv(), but that doesn't benefit from LAST_ARG_MUST_BE_NULL,\n>   so I didn't bother.\n\nI thought about that as well, but at least for me is probably a just a\ncase of loss aversion, which I have aplenty.  run_command_v_opt() alone\nisn't bad and patch 8 on its own probably wouldn't fly, so I mentally\ncling to it.  But without it the API is untangled and simpler.  Only a\nsingle way to specify flags and arguments, no shortcuts for special\ncombinations.  Overall easier to use once we forget the old ways.\n\n>\n> - I wish C had a nicer syntax for not just declaring but squashing\n>   together compile_time bracketed lists (think set operations). But the\n>   below \"CHILD_PROCESS_INIT_LIST\" is a pretty good poor man's version.\n>\n>   I see gcc/clang nicely give us safety rails for that with\n>   \"-Woverride-init\", for this sort of \"opts struct with internal stuff,\n>   but also user options\" I think it works out very nicely.\n>\n\nThat's a nice and simple macro.  I played with a gross variant à la\n\n  #define CHILD_PROCESS_INIT_EX(...) { .args = STRVEC_INIT, __VA_ARGS__ }\n\nwhich would allow e.g.\n\n  struct child_process cmd = CHILD_PROCESS_INIT_EX(.git_cmd = 1);\n\nYours is better, but they share the downside of not actually saving any\nlines of code..\n\n> - We have quite a few callers that want \"on error, die\", so maybe we\n>   should have something like that \"on_error\" sooner than later.\n\nWe could add a die_on_error bit for that, or start_command_or_die() and\nrun_command_or_die() variants (there I go again, multiplying APIs..).\nThey could report the failed command, which a caller can't do because\nthe internal strvec is already cleared once it learns of the failure.\n\n>\n> On clever (but perhaps overly clever) thing I didn't use, but played\n> with recently in another context, is that now with C99 you can also do:\n>\n> \tint run_commandl(struct child_process *cmd, ...);\n> \t#define run_command(...) run_command_1(__VA_ARGS__, NULL)\n>\n> I.e. make the API itself support all of:\n>\n> \trun_command(&cmd);\n> \trun_command(&cmd, \"reboot\");\n> \trun_command(&cmd, \"reboot\", NULL);\n>\n> I haven't made up my mind on whether that's just overly clever, or\n> outright insane :)\n\nNeither, I'd say.  By combining two operations it's less flexible than\na pure run method plus direct access to a strvec member.  It's a\nshortcut that may save a line but requires more effort to extend its\ncallers, e.g. to conditionally add a new argument.\n\n>\n> diff --git a/bisect.c b/bisect.c\n> index ec7487e6836..ef4f80650f7 100644\n> --- a/bisect.c\n> +++ b/bisect.c\n> @@ -740,9 +740,8 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n>  \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>\n>  \t\tcmd.git_cmd = 1;\n> -\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n> -\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n> -\t\tif (run_command(&cmd))\n> +\t\tif (run_commandl(&cmd, \"checkout\", \"-q\",\n> +\t\t\t\t oid_to_hex(bisect_rev), \"--\", NULL))\n>  \t\t\t/*\n>  \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n>  \t\t\t * and errors in the child process, signaled by res > 0\n> diff --git a/builtin/am.c b/builtin/am.c\n> index 20aea0d2487..3b7df32ce22 100644\n> --- a/builtin/am.c\n> +++ b/builtin/am.c\n> @@ -2189,10 +2189,9 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n>  \tif (!is_null_oid(&state->orig_commit)) {\n>  \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n>\n> -\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n> -\t\t\t     \"--\", NULL);\n>  \t\tcmd.git_cmd = 1;\n> -\t\treturn run_command(&cmd);\n> +\t\treturn run_commandl(&cmd, \"show\", oid_to_hex(&state->orig_commit),\n> +\t\t\t\t    \"--\", NULL);\n>  \t}\n>\n>  \tswitch (sub_mode) {\n> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\n> index 1d2ce8a0e12..087d21c614a 100644\n> --- a/builtin/bisect--helper.c\n> +++ b/builtin/bisect--helper.c\n> @@ -764,12 +764,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a\n>  \t\tstrbuf_read_file(&start_head, git_path_bisect_start(), 0);\n>  \t\tstrbuf_trim(&start_head);\n>  \t\tif (!no_checkout) {\n> -\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> -\n> -\t\t\tcmd.git_cmd = 1;\n> -\t\t\tstrvec_pushl(&cmd.args, \"checkout\", start_head.buf,\n> -\t\t\t\t     \"--\", NULL);\n> -\t\t\tif (run_command(&cmd)) {\n> +\t\t\tstruct child_process cmd = {\n> +\t\t\t\tCHILD_PROCESS_INIT_LIST,\n> +\t\t\t\t.git_cmd = 1,\n> +\t\t\t};\n> +\t\t\tif (run_commandl(&cmd, \"checkout\", start_head.buf,\n> +\t\t\t\t\t \"--\", NULL)) {\n>  \t\t\t\tres = error(_(\"checking out '%s' failed.\"\n>  \t\t\t\t\t\t \" Try 'git bisect start \"\n>  \t\t\t\t\t\t \"<valid-branch>'.\"),\n> @@ -1147,8 +1147,7 @@ static int do_bisect_run(const char *command)\n>\n>  \tprintf(_(\"running %s\\n\"), command);\n>  \tcmd.use_shell = 1;\n> -\tstrvec_push(&cmd.args, command);\n> -\treturn run_command(&cmd);\n> +\treturn run_commandl(&cmd, command, NULL);\n>  }\n>\n>  static int verify_good(const struct bisect_terms *terms, const char *command)\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index 0e4348686b6..80d09e0fbf1 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -655,7 +655,6 @@ static int git_sparse_checkout_init(const char *repo)\n>  {\n>  \tstruct child_process cmd = CHILD_PROCESS_INIT;\n>  \tint result = 0;\n> -\tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n>\n>  \t/*\n>  \t * We must apply the setting in the current process\n> @@ -664,7 +663,7 @@ static int git_sparse_checkout_init(const char *repo)\n>  \tcore_apply_sparse_checkout = 1;\n>\n>  \tcmd.git_cmd = 1;\n> -\tif (run_command(&cmd)) {\n> +\tif (run_commandl(&cmd, \"-C\", repo, \"sparse-checkout\", \"set\", NULL)) {\n>  \t\terror(_(\"failed to initialize sparse-checkout\"));\n>  \t\tresult = 1;\n>  \t}\n> @@ -868,13 +867,14 @@ static void dissociate_from_references(void)\n>  \tchar *alternates = git_pathdup(\"objects/info/alternates\");\n>\n>  \tif (!access(alternates, F_OK)) {\n> -\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> -\n> -\t\tcmd.git_cmd = 1;\n> -\t\tcmd.no_stdin = 1;\n> -\t\tstrvec_pushl(&cmd.args, \"repack\", \"-a\", \"-d\", NULL);\n> -\t\tif (run_command(&cmd))\n> -\t\t\tdie(_(\"cannot repack to clean up\"));\n> +\t\tstruct child_process cmd = {\n> +\t\t\tCHILD_PROCESS_INIT_LIST,\n> +\t\t\t.git_cmd = 1,\n> +\t\t\t.no_stdin = 1,\n> +\t\t\t.on_error = CHILD_PROCESS_ON_ERROR_DIE,\n> +\t\t};\n> +\n> +\t\trun_commandl(&cmd, \"repack\", \"-a\", \"-d\", NULL);\n>  \t\tif (unlink(alternates) && errno != ENOENT)\n>  \t\t\tdie_errno(_(\"cannot unlink temporary alternates file\"));\n>  \t}\n> diff --git a/builtin/difftool.c b/builtin/difftool.c\n> index d7f08c8a7fa..b4165b5a8ae 100644\n> --- a/builtin/difftool.c\n> +++ b/builtin/difftool.c\n> @@ -44,11 +44,12 @@ static int difftool_config(const char *var, const char *value, void *cb)\n>\n>  static int print_tool_help(void)\n>  {\n> -\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> -\n> -\tcmd.git_cmd = 1;\n> -\tstrvec_pushl(&cmd.args, \"mergetool\", \"--tool-help=diff\", NULL);\n> -\treturn run_command(&cmd);\n> +\tstruct child_process cmd = {\n> +\t\tCHILD_PROCESS_INIT_LIST,\n> +\t\t.git_cmd = 1,\n> +\t};\n> +\n> +\treturn run_commandl(&cmd, \"mergetool\", \"--tool-help=diff\", NULL);\n>  }\n>\n>  static int parse_index_info(char *p, int *mode1, int *mode2,\n> diff --git a/git.c b/git.c\n> index 6662548986f..93179f44f78 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -724,7 +724,13 @@ static void handle_builtin(int argc, const char **argv)\n>\n>  static void execv_dashed_external(const char **argv)\n>  {\n> -\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> +\tstruct child_process cmd = {\n> +\t\tCHILD_PROCESS_INIT_LIST,\n> +\t\t.clean_on_exit = 1,\n> +\t\t.wait_after_clean = 1,\n> +\t\t.silent_exec_failure = 1,\n> +\t\t.trace2_child_class = \"dashed\",\n> +\t};\n>  \tint status;\n>\n>  \tif (get_super_prefix())\n> @@ -736,10 +742,6 @@ static void execv_dashed_external(const char **argv)\n>\n>  \tstrvec_pushf(&cmd.args, \"git-%s\", argv[0]);\n>  \tstrvec_pushv(&cmd.args, argv + 1);\n> -\tcmd.clean_on_exit = 1;\n> -\tcmd.wait_after_clean = 1;\n> -\tcmd.silent_exec_failure = 1;\n> -\tcmd.trace2_child_class = \"dashed\";\n>\n>  \ttrace2_cmd_name(\"_run_dashed_\");\n>\n> diff --git a/run-command.c b/run-command.c\n> index 23e100dffc4..4b20aa1b577 100644\n> --- a/run-command.c\n> +++ b/run-command.c\n> @@ -993,15 +993,45 @@ int finish_command_in_signal(struct child_process *cmd)\n>\n>  int run_command(struct child_process *cmd)\n>  {\n> -\tint code;\n> +\tint starting = 1;\n> +\tint code = 0;\n>\n>  \tif (cmd->out < 0 || cmd->err < 0)\n>  \t\tBUG(\"run_command with a pipe can cause deadlock\");\n>\n>  \tcode = start_command(cmd);\n>  \tif (code)\n> +\t\tgoto error;\n> +\tstarting = 0;\n> +\tcode = finish_command(cmd);\n> +\tif (!code)\n> +\t\treturn 0;\n> +error:\n> +\tswitch (cmd->on_error) {\n> +\tcase CHILD_PROCESS_ON_ERROR_DIE:\n> +\t\tdie(starting ?\n> +\t\t    _(\"start_command() for '%s' failed (error code %d)\") :\n> +\t\t    _(\"'%s': got non-zero exit code '%d'\"),\n> +\t\t    cmd->args.v[0], code);\n> +\t\tbreak;\n> +\tcase CHILD_PROCESS_ON_ERROR_RETURN:\n>  \t\treturn code;\n> -\treturn finish_command(cmd);\n> +\tdefault:\n> +\t\tBUG(\"unreachable\");\n> +\t}\n> +}\n> +\n> +int run_commandl(struct child_process *cmd, ...)\n> +{\n> +\tva_list ap;\n> +\tconst char *arg;\n> +\n> +\tva_start(ap, cmd);\n> +\twhile ((arg = va_arg(ap, const char *)))\n> +\t\tstrvec_push(&cmd->args, arg);\n> +\tva_end(ap);\n> +\n> +\treturn run_command(cmd);\n>  }\n>\n>  #ifndef NO_PTHREADS\n> diff --git a/run-command.h b/run-command.h\n> index fe2717ad11e..71e390350ed 100644\n> --- a/run-command.h\n> +++ b/run-command.h\n> @@ -15,7 +15,22 @@\n>   * produces in the caller in order to process it.\n>   */\n>\n> +enum child_process_on_error {\n> +\t/**\n> +\t * Return a status code from run_command(). Set to 0 so that\n> +\t * it'll be { 0 } init'd. If it's specified in a\n> +\t * CHILD_PROCESS_INIT_LIST initialization we don't want a\n> +\t * \"-Woverride-init\" warning.\n> +\t */\n> +\tCHILD_PROCESS_ON_ERROR_RETURN = 0,\n>\n> +\t/**\n> +\t * die() with some sensible message if run_command() would\n> +\t * have returned a non-zero exit code.\n> +\t */\n> +\tCHILD_PROCESS_ON_ERROR_DIE,\n> +};\n> +\n>  /**\n>   * This describes the arguments, redirections, and environment of a\n>   * command to run in a sub-process.\n> @@ -42,6 +57,10 @@\n>   *\t\tredirected.\n>   */\n>  struct child_process {\n> +\t/**\n> +\t * Error behavior, see \"enum child_process_on_error\" above.\n> +\t */\n> +\tenum child_process_on_error on_error;\n>\n>  \t/**\n>  \t * The .args is a `struct strvec', use that API to manipulate\n> @@ -144,10 +163,11 @@ struct child_process {\n>  \tvoid (*clean_on_exit_handler)(struct child_process *process);\n>  };\n>\n> -#define CHILD_PROCESS_INIT { \\\n> +#define CHILD_PROCESS_INIT_LIST \\\n> +\t/* .on_error = CHILD_PROCESS_ON_ERROR_RETURN */ \\\n>  \t.args = STRVEC_INIT, \\\n> -\t.env = STRVEC_INIT, \\\n> -}\n> +\t.env = STRVEC_INIT\n> +#define CHILD_PROCESS_INIT { CHILD_PROCESS_INIT_LIST }\n>\n>  /**\n>   * The functions: child_process_init, start_command, finish_command,\n> @@ -218,6 +238,14 @@ int finish_command_in_signal(struct child_process *);\n>   */\n>  int run_command(struct child_process *);\n>\n> +/**\n> + * Like run_command() but takes a variable number of arguments, which\n> + * will be appended with the equivalent of strvec_pushl(&cmd.args,\n> + * ...) before invoking run_command().\n> + */\n> +LAST_ARG_MUST_BE_NULL\n> +int run_commandl(struct child_process *cmd, ...);\n> +\n>  /*\n>   * Trigger an auto-gc\n>   */\n"},{"id":"465957","messageId":"221028.8635b87wo6.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"749f6adc-928a-0978-e3a1-2ede9f07def0@web.de","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-28T16:11:35Z","receivedAt":"2022-10-28T16:16:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Oct 28 2022, René Scharfe wrote:\n\n> Am 27.10.22 um 23:46 schrieb Ævar Arnfjörð Bjarmason:\n>>\n>> - I wish C had a nicer syntax for not just declaring but squashing\n>>   together compile_time bracketed lists (think set operations). But the\n>>   below \"CHILD_PROCESS_INIT_LIST\" is a pretty good poor man's version.\n>>\n>>   I see gcc/clang nicely give us safety rails for that with\n>>   \"-Woverride-init\", for this sort of \"opts struct with internal stuff,\n>>   but also user options\" I think it works out very nicely.\n>>\n>\n> That's a nice and simple macro.  I played with a gross variant à la\n>\n>   #define CHILD_PROCESS_INIT_EX(...) { .args = STRVEC_INIT, __VA_ARGS__ }\n>\n> which would allow e.g.\n>\n>   struct child_process cmd = CHILD_PROCESS_INIT_EX(.git_cmd = 1);\n>\n> Yours is better, \n\nI actually think yours is better, anyway...\n\n> but they share the downside of not actually saving any lines of code..\n\nTo me it's not about saving code, but that it's immediately obvious when\nreading the code that this set of options can be determined and set at\nfunction or scope entry.\n\nWe tend to otherwise have creep where the decl and option init drifts\napart over time, and with complex init's you might stare at it for 30s,\nbefore realizing that between the decl and fully init ing it often 50\nlines later nothing actually changed vis-a-vis the state, we could have\njust done it earlier.\n\nI think that's worth it in general, whether it's worth the churn in this\ncase...\n\n>> - We have quite a few callers that want \"on error, die\", so maybe we\n>>   should have something like that \"on_error\" sooner than later.\n>\n> We could add a die_on_error bit for that, or start_command_or_die() and\n> run_command_or_die() variants (there I go again, multiplying APIs..).\n> They could report the failed command, which a caller can't do because\n> the internal strvec is already cleared once it learns of the failure.\n\n*nod*\n"},{"id":"465960","messageId":"b200477c-f856-f107-b897-63721c805093@web.de","threadId":"58704","inReplyTo":"221028.8635b87wo6.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-28T17:16:33Z","receivedAt":"2022-10-28T17:16:49Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 28.10.22 um 18:11 schrieb Ævar Arnfjörð Bjarmason:\n>\n> On Fri, Oct 28 2022, René Scharfe wrote:\n>\n>> Am 27.10.22 um 23:46 schrieb Ævar Arnfjörð Bjarmason:\n>>>\n>>> - I wish C had a nicer syntax for not just declaring but squashing\n>>>   together compile_time bracketed lists (think set operations). But the\n>>>   below \"CHILD_PROCESS_INIT_LIST\" is a pretty good poor man's version.\n>>>\n>>>   I see gcc/clang nicely give us safety rails for that with\n>>>   \"-Woverride-init\", for this sort of \"opts struct with internal stuff,\n>>>   but also user options\" I think it works out very nicely.\n>>>\n>>\n>> That's a nice and simple macro.  I played with a gross variant à la\n>>\n>>   #define CHILD_PROCESS_INIT_EX(...) { .args = STRVEC_INIT, __VA_ARGS__ }\n>>\n>> which would allow e.g.\n>>\n>>   struct child_process cmd = CHILD_PROCESS_INIT_EX(.git_cmd = 1);\n>>\n>> Yours is better,\n>\n> I actually think yours is better, anyway...\n>\n>> but they share the downside of not actually saving any lines of code..\n>\n> To me it's not about saving code, but that it's immediately obvious when\n> reading the code that this set of options can be determined and set at\n> function or scope entry.\n>\n> We tend to otherwise have creep where the decl and option init drifts\n> apart over time, and with complex init's you might stare at it for 30s,\n> before realizing that between the decl and fully init ing it often 50\n> lines later nothing actually changed vis-a-vis the state, we could have\n> just done it earlier.\n\nHmm, we could do that by collecting the flag setting parts at the top,\nwithout the need for a new macro.  Or at the bottom, before the\nrun_command() call.\n\n> I think that's worth it in general, whether it's worth the churn in this\n> case...\n>\n>>> - We have quite a few callers that want \"on error, die\", so maybe we\n>>>   should have something like that \"on_error\" sooner than later.\n>>\n>> We could add a die_on_error bit for that, or start_command_or_die() and\n>> run_command_or_die() variants (there I go again, multiplying APIs..).\n>> They could report the failed command, which a caller can't do because\n>> the internal strvec is already cleared once it learns of the failure.\n>\n> *nod*\n\nWait a second: Does it even make sense to mention the command in a die()\nmessage after start_command() failed?  Unless .silent_exec_failure is\nset, start_command() already reports it.  E.g. archive-tar.c has:\n\n\tif (start_command(&filter) < 0)\n\t\tdie_errno(_(\"unable to start '%s' filter\"), cmd.buf);\n\n... and the result looks like this upon failure:\n\n   $ git -c tar.tgz.command=nonsense archive --format=tgz HEAD\n   error: cannot run nonsense: No such file or directory\n   fatal: unable to start 'nonsense' filter: No such file or directory\n\nThe second message is mostly redundant, but it mentions that the failed\ncommand was a filter.  Probably .silent_exec_failure should be set here,\nthen the die() message is no longer redundant.  This requires args[0] to\nbe stored outside of struct child_process, though, which is already done\nhere, but may be a bit tedious in other cases.\n\nSo for start_command(), would it be a generally useful to support a\nscenario where upon failure\n\n- the program terminates,\n- but before that prints a single message,\n- which includes the command that could not be started\n- and some kind of hint why we tried to start it?\n\nFor run_command() we'd need to distinguish between not being able to\nrun the command and getting an error code from it after a successful\nstart.\n\nRené\n"},{"id":"465999","messageId":"221029.86y1sz74ez.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"b200477c-f856-f107-b897-63721c805093@web.de","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-29T02:17:23Z","receivedAt":"2022-10-29T02:24:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Oct 28 2022, René Scharfe wrote:\n\n> Am 28.10.22 um 18:11 schrieb Ævar Arnfjörð Bjarmason:\n>>\n>> On Fri, Oct 28 2022, René Scharfe wrote:\n>>\n>>> Am 27.10.22 um 23:46 schrieb Ævar Arnfjörð Bjarmason:\n>>>>\n>>>> - I wish C had a nicer syntax for not just declaring but squashing\n>>>>   together compile_time bracketed lists (think set operations). But the\n>>>>   below \"CHILD_PROCESS_INIT_LIST\" is a pretty good poor man's version.\n>>>>\n>>>>   I see gcc/clang nicely give us safety rails for that with\n>>>>   \"-Woverride-init\", for this sort of \"opts struct with internal stuff,\n>>>>   but also user options\" I think it works out very nicely.\n>>>>\n>>>\n>>> That's a nice and simple macro.  I played with a gross variant à la\n>>>\n>>>   #define CHILD_PROCESS_INIT_EX(...) { .args = STRVEC_INIT, __VA_ARGS__ }\n>>>\n>>> which would allow e.g.\n>>>\n>>>   struct child_process cmd = CHILD_PROCESS_INIT_EX(.git_cmd = 1);\n>>>\n>>> Yours is better,\n>>\n>> I actually think yours is better, anyway...\n>>\n>>> but they share the downside of not actually saving any lines of code..\n>>\n>> To me it's not about saving code, but that it's immediately obvious when\n>> reading the code that this set of options can be determined and set at\n>> function or scope entry.\n>>\n>> We tend to otherwise have creep where the decl and option init drifts\n>> apart over time, and with complex init's you might stare at it for 30s,\n>> before realizing that between the decl and fully init ing it often 50\n>> lines later nothing actually changed vis-a-vis the state, we could have\n>> just done it earlier.\n>\n> Hmm, we could do that by collecting the flag setting parts at the top,\n> without the need for a new macro.\n\nYou mean just to pinky promise to always try to set the flags right\nafter we declare variables. Yes, we can try to aim for that, but\nsometimes you need to declare quite a few of them (e.g. that difftools\ncaller), so not having distance between the decl & setting can help.\n>> I think that's worth it in general, whether it's worth the churn in this\n>> case...\n>>\n>>>> - We have quite a few callers that want \"on error, die\", so maybe we\n>>>>   should have something like that \"on_error\" sooner than later.\n>>>\n>>> We could add a die_on_error bit for that, or start_command_or_die() and\n>>> run_command_or_die() variants (there I go again, multiplying APIs..).\n>>> They could report the failed command, which a caller can't do because\n>>> the internal strvec is already cleared once it learns of the failure.\n>>\n>> *nod*\n>\n> Wait a second: Does it even make sense to mention the command in a die()\n> message after start_command() failed?  Unless .silent_exec_failure is\n> set, start_command() already reports it.  E.g. archive-tar.c has:\n>\n> \tif (start_command(&filter) < 0)\n> \t\tdie_errno(_(\"unable to start '%s' filter\"), cmd.buf);\n>\n> ... and the result looks like this upon failure:\n>\n>    $ git -c tar.tgz.command=nonsense archive --format=tgz HEAD\n>    error: cannot run nonsense: No such file or directory\n>    fatal: unable to start 'nonsense' filter: No such file or directory\n>\n> The second message is mostly redundant, but it mentions that the failed\n> command was a filter.  Probably .silent_exec_failure should be set here,\n> then the die() message is no longer redundant.  This requires args[0] to\n> be stored outside of struct child_process, though, which is already done\n> here, but may be a bit tedious in other cases.\n\nYes, maybe these messages aren't all that useful. But note that that\n\"error\" is emittted by thy \"#ifndef ...WINDOWS...\" part of the code. See\nalso the recent 255a6f91ae4 (t1800: correct test to handle Cygwin,\n2022-09-15).\n\nBut I suspect some of the \"die\" messaging is just boilerplate/legacy.\n\n> So for start_command(), would it be a generally useful to support a\n> scenario where upon failure\n>\n> - the program terminates,\n> - but before that prints a single message,\n> - which includes the command that could not be started\n\nIsn't this just the \"cannot spawn\"/\"cannot fork\" etc. messaging from\nstart_command() now?\n\n> - and some kind of hint why we tried to start it?\n>\n> For run_command() we'd need to distinguish between not being able to\n> run the command and getting an error code from it after a successful\n> start.\n\n*nod*, it would be nice if that messaging was portable. Part of that\njust seems to be that we need to do the same (or share) the whole \"set\ndie message\" part we do on *nix.\n\nSome of the reasons a command fails aren't portable, but we should be\nable to portably emit e.g. \"can't execute this command because it\ndoesn't exist\" etc.\n"},{"id":"466009","messageId":"9f300d06-a595-5f21-c97a-7b8c6b02b866@web.de","threadId":"58704","inReplyTo":"221029.86y1sz74ez.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-29T10:05:53Z","receivedAt":"2022-10-29T10:06:14Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 29.10.2022 um 04:17 schrieb Ævar Arnfjörð Bjarmason:\n>\n> On Fri, Oct 28 2022, René Scharfe wrote:\n>\n>> Wait a second: Does it even make sense to mention the command in a die()\n>> message after start_command() failed?  Unless .silent_exec_failure is\n>> set, start_command() already reports it.  E.g. archive-tar.c has:\n>>\n>> \tif (start_command(&filter) < 0)\n>> \t\tdie_errno(_(\"unable to start '%s' filter\"), cmd.buf);\n>>\n>> ... and the result looks like this upon failure:\n>>\n>>    $ git -c tar.tgz.command=nonsense archive --format=tgz HEAD\n>>    error: cannot run nonsense: No such file or directory\n>>    fatal: unable to start 'nonsense' filter: No such file or directory\n>>\n>> The second message is mostly redundant, but it mentions that the failed\n>> command was a filter.  Probably .silent_exec_failure should be set here,\n>> then the die() message is no longer redundant.  This requires args[0] to\n>> be stored outside of struct child_process, though, which is already done\n>> here, but may be a bit tedious in other cases.\n>\n> Yes, maybe these messages aren't all that useful. But note that that\n> \"error\" is emittted by thy \"#ifndef ...WINDOWS...\" part of the code. See\n> also the recent 255a6f91ae4 (t1800: correct test to handle Cygwin,\n> 2022-09-15).\n\nOn Windows I get:\n\n  $ git -c tar.tgz.command=nonsense archive --format=tgz HEAD\n  error: cannot spawn nonsense: No such file or directory\n  fatal: unable to start 'nonsense' filter: No such file or directory\n\n>> So for start_command(), would it be a generally useful to support a\n>> scenario where upon failure\n>>\n>> - the program terminates,\n>> - but before that prints a single message,\n>> - which includes the command that could not be started\n>\n> Isn't this just the \"cannot spawn\"/\"cannot fork\" etc. messaging from\n> start_command() now?\n\nSure, a subset is easy, but do we need all four points?\n\n>> - and some kind of hint why we tried to start it?\n\nI suspect .silent_exec_failure suffices for most cases.  Testing calls\nof commands that usually exist is a bit hard without source code\nchanges, though.  Then again: Beatifying messages that will almost\nnever be seen isn't probably worth it anyway.\n\nBut I'll send a patch for the archive case above.\n\nRené\n"},{"id":"466029","messageId":"Y11s+XokKbbJ1WTL@nand.local","threadId":"58704","inReplyTo":"0cae80c0-2576-8be7-48f7-06a0a135961f@web.de","subject":"Re: [PATCH 1/8] merge: remove always-the-same \"verbose\" arguments","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-10-29T18:12:09Z","receivedAt":"2022-10-29T18:12:15Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Oct 27, 2022 at 06:35:02PM +0200, René Scharfe wrote:\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n> Simplify the code that builds the arguments for the \"read-tree\"\n> invocation in reset_hard() and read_empty() to remove the \"verbose\"\n> parameter.\n>\n> Before 172b6428d06 (do not overwrite untracked during merge from\n> unborn branch, 2010-11-14) there was a \"reset_hard()\" function that\n> would be called in two places, one of those passed a \"verbose=1\", the\n> other a \"verbose=0\".\n>\n> After 172b6428d06 when read_empty() was split off from reset_hard()\n> both of these functions only had one caller. The \"verbose\" in\n> read_empty() would always be false, and the one in reset_hard() would\n> always be true.\n>\n> There was never a good reason for the code to act this way, it\n> happened because the read_empty() function was a copy/pasted and\n> adjusted version of reset_hard().\n>\n> Since we're no longer conditionally adding the \"-v\" parameter\n> here (and we'd only add it for \"reset_hard()\" we'll be able to move to\n> a simpler and safer run-command API in the subsequent commit.\n\nAll looks reasonable here. Thanks for the detailed explanation of when\nand why these changed and at which point they became redundant.\n\nThanks,\nTaylor\n"},{"id":"466030","messageId":"Y11uAFr/X+45kCgc@nand.local","threadId":"58704","inReplyTo":"221028.86v8o4aoiq.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 2/8] bisect--helper: factor out do_bisect_run()","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-10-29T18:16:32Z","receivedAt":"2022-10-29T18:16:41Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Fri, Oct 28, 2022 at 12:26:51AM +0200, Ævar Arnfjörð Bjarmason wrote:\n> But given the line count of some of the other changes, and\n> e.g. including the free(), oid_to_hex_r() to oid_to_hex() etc. in later\n> commits I don't see why we can't just make it use run_command()\n> directly.\n>\n> I.e. I think it makes sense as one commit, but the conversion is easy\n> enough, easier than looking at the same code again later in the\n> series...\n\nHmmph. I don't think that it matters much, either way. I was initially\nconfused not having read the whole series, but I see what you're saying\nnow.\n\nWe see the same part of the diff twice here: the post-image of this hunk\nto use run_command_v_opt() becomes the pre-image of a hunk in one of the\nlater patches to change it to use run_command() directly.\n\nArguably the two could be combined into a single patch, yes. Though I\nfind the version as written here to be clearer, since it's a\nstraightforward code movement within bisect--helper.\n\nThanks,\nTaylor\n"},{"id":"466031","messageId":"Y11wVpgoD0KrHm55@nand.local","threadId":"58704","inReplyTo":"0e889c96-6004-96e4-9505-19ce1e7f9900@web.de","subject":"Re: [PATCH 4/8] use child_process member \"args\" instead of string array variable","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-10-29T18:26:30Z","receivedAt":"2022-10-29T18:26:36Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Oct 27, 2022 at 06:37:52PM +0200, René Scharfe wrote:\n> @@ -729,20 +727,22 @@ static int is_expected_rev(const struct object_id *oid)\n>  enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n>  \t\t\t\t  int no_checkout)\n>  {\n> -\tchar bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n>  \tstruct commit *commit;\n>  \tstruct pretty_print_context pp = {0};\n>  \tstruct strbuf commit_msg = STRBUF_INIT;\n>\n> -\toid_to_hex_r(bisect_rev_hex, bisect_rev);\n>  \tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n>\n> -\targv_checkout[2] = bisect_rev_hex;\n>  \tif (no_checkout) {\n>  \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n>  \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n>  \t} else {\n> -\t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n> +\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> +\n> +\t\tcmd.git_cmd = 1;\n> +\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n> +\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n\nI was wondering if this part of the conversion was right, since\noid_to_hex() uses a ring of output buffers (see hash_to_hex_algop()).\nBut we do call xstrdup() on the argument from strvec_push(), so we are\nOK here.\n\nThanks,\nTaylor\n"},{"id":"466032","messageId":"Y11xS9Epq2rHiVkH@nand.local","threadId":"58704","inReplyTo":"6c30a1f6-8d89-31b9-faf8-c695c46173cd@web.de","subject":"Re: [PATCH 4/8] use child_process member \"args\" instead of string array variable","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-10-29T18:30:35Z","receivedAt":"2022-10-29T18:30:41Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Fri, Oct 28, 2022 at 04:23:31PM +0200, René Scharfe wrote:\n> > A lot of your 3-lines would be 1 lines if we just had e.g. (untested,\n> > and could be a function not a macro, but you get the idea):\n> >\n> > \t#define run_command_git_simple(__VA_ARGS__) \\\n> > \t\tstruct child_process cmd = CHILD_PROCESS_INIT; \\\n> > \t\tcmd.git_cmd = 1; \\\n> > \t\tstrvec_pushl(&cmd.args, __VA_ARGS__); \\\n> > \t\trun_command(&cmd);\n> >\n> > But maybe nobody except me thinks that's worthwhile...\n>\n> I have similar temptations; you could see that in my scratch patch\n> https://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\n> which added run_git_or_die() in builtin/gc.c.  Why, oh why?  Perhaps\n> because taking a blank form (CHILD_PROCESS_INIT), ticking boxes\n> (.git_cmd = 1), filling out text fields (strvec_push(...)) and\n> submitting it (run_command()) feels tedious and bureaucratic, Java-esque\n> even.  And some patterns appear again and again.\n>\n> How bad is that?  Is it bad at all?  I think overall we should try to\n> reduce the number of external calls and make those we have to do\n> self-documenting and leak-free.  A bit of tedium is OK; this API should\n> be used rarely and sparingly.  Still I get the urge to search for\n> patterns and define shortcuts when I see all those similar calls..\n>\n> run_command_git_simple as defined above wouldn't compile, but I get it.\n> Reducing the number of lines feels good, but it also makes the code less\n> flexible -- adding a conditional parameter requires converting back to\n> run_command().\n\nFor what it's worth, I agree. I don't think there are or should be any\nhard and fast rules about when extracting a pattern like this into a\nfunction or macro is right. But here it feels wrong and\ncounterproductive to the goal of this series.\n\nMy main gripe with it is that it seems to be overfit to these small\nhandful of calls, and that changing it in the future would require us to\ngo and update existing callers to accommodate new functionality in other\ncallers.\n\nSo I'd prefer to see it left alone.\n\nThanks,\nTaylor\n"},{"id":"466034","messageId":"Y11+Tt2xNtPo0/pA@nand.local","threadId":"58704","inReplyTo":"e72a1abe-3098-e4fb-f064-a8b5c8f14239@web.de","subject":"Re: [PATCH 5/8] replace and remove run_command_v_opt_cd_env()","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-10-29T19:26:06Z","receivedAt":"2022-10-29T19:26:13Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Oct 27, 2022 at 06:38:41PM +0200, René Scharfe wrote:\n> run_command_v_opt_cd_env() is only used in an example in a comment.  Use\n> the struct child_process member \"env\" and run_command() directly instead\n> and then remove the unused convenience function.\n\nNice, this is a very satisfying clean-up.\n\nThanks,\nTaylor\n"},{"id":"466035","messageId":"Y11/2hyApN5NLruq@nand.local","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"Re: [PATCH 0/8] run-command: remove run_command_v_*()","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-10-29T19:32:42Z","receivedAt":"2022-10-29T19:32:49Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Oct 27, 2022 at 06:30:36PM +0200, René Scharfe wrote:\n> Replace the convenience functions run_command_v_opt() et. al. and use\n> struct child_process and run_command() directly instead, for an overall\n> code reduction and a simpler and more flexible API that allows creating\n> argument lists without magic numbers and reduced risk of memory leaks.\n>\n> This is a broken-out and polished version of the original scratch at\n> https://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/\n\nThanks, this round is looking pretty good to me. It sounds like there\nwere a couple of small comments for which a reroll would be appreciated.\n\nAssuming that that all looks good, I can queue the rerolled version.\nThanks.\n\nThanks,\nTaylor\n"},{"id":"466044","messageId":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","threadId":"58704","inReplyTo":"7407e074-4bd8-b351-7fa4-baf59b41880c@web.de","subject":"[PATCH v2 0/12] run-command: remove run_command_v_*()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:40:23Z","receivedAt":"2022-10-30T11:40:43Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Replace the convenience functions run_command_v_opt() et. al. and use\nstruct child_process and run_command() directly instead, for an overall\ncode reduction and a simpler and more flexible API that allows creating\nargument lists without magic numbers and reduced risk of memory leaks.\n\nChanges since v1:\n- Do the return value fix earlier; it was only an afterthought before.\n  Keep the colon (no \"while at it, ...\").\n- Break out the xstrdup(), oid_to_hex_r() and C99 cleanups.\n- Convert tricky string arrays before strvecs because Ævar didn't like\n  the opposite order.\n- Extend the example code in tmp-objdir.h so it still only requires\n  \"cmd\".\n\nÆvar Arnfjörð Bjarmason (1):\n  merge: remove always-the-same \"verbose\" arguments\n\nRené Scharfe (11):\n  run-command: fix return value comment\n  am: simplify building \"show\" argument list\n  bisect: simplify building \"checkout\" argument list\n  bisect--helper: factor out do_bisect_run()\n  sequencer: simplify building argument list in do_exec()\n  use child_process member \"args\" instead of string array variable\n  use child_process members \"args\" and \"env\" directly\n  replace and remove run_command_v_opt_cd_env()\n  replace and remove run_command_v_opt_tr2()\n  replace and remove run_command_v_opt_cd_env_tr2()\n  replace and remove run_command_v_opt()\n\n add-interactive.c        |   9 ++-\n bisect.c                 |  12 ++--\n builtin/add.c            |  19 +++--\n builtin/am.c             |  12 ++--\n builtin/bisect--helper.c |  68 +++++++++---------\n builtin/clone.c          |  41 ++++++-----\n builtin/difftool.c       |  24 ++++---\n builtin/fetch.c          |   9 ++-\n builtin/gc.c             |  55 ++++++++++-----\n builtin/merge-index.c    |   4 +-\n builtin/merge.c          |  53 ++++++--------\n builtin/pull.c           | 147 +++++++++++++++++++--------------------\n builtin/remote.c         |  40 +++++------\n compat/mingw.c           |  11 +--\n diff.c                   |  27 ++++---\n fsmonitor-ipc.c          |  10 ++-\n git.c                    |  15 ++--\n ll-merge.c               |   7 +-\n merge.c                  |  18 ++---\n run-command.c            |  35 ----------\n run-command.h            |  34 +--------\n scalar.c                 |  13 ++--\n sequencer.c              |  32 ++++-----\n shell.c                  |  17 +++--\n t/helper/test-fake-ssh.c |   7 +-\n t/helper/test-trace2.c   |   4 +-\n tmp-objdir.h             |   6 +-\n 27 files changed, 346 insertions(+), 383 deletions(-)\n\nRange-Diff gegen v1:\n 1:  c0b2b88500 =  1:  113a9e0a81 merge: remove always-the-same \"verbose\" arguments\n -:  ---------- >  2:  d10e70460b run-command: fix return value comment\n -:  ---------- >  3:  c8cd913e39 am: simplify building \"show\" argument list\n -:  ---------- >  4:  4bb142e4a9 bisect: simplify building \"checkout\" argument list\n 2:  387db545d1 =  5:  5fcbe94eb4 bisect--helper: factor out do_bisect_run()\n -:  ---------- >  6:  962403cf22 sequencer: simplify building argument list in do_exec()\n 4:  348bc6d32c !  7:  f1689abe85 use child_process member \"args\" instead of string array variable\n    @@ Commit message\n\n         Use run_command() with a struct child_process variable and populate its\n         \"args\" member directly instead of building a string array and passing it\n    -    to run_command_v_opt().  This avoids the use of magic index numbers.\n    -\n    - ## bisect.c ##\n    -@@ bisect.c: static struct oid_array skipped_revs;\n    -\n    - static struct object_id *current_bad_oid;\n    -\n    --static const char *argv_checkout[] = {\"checkout\", \"-q\", NULL, \"--\", NULL};\n    --\n    - static const char *term_bad;\n    - static const char *term_good;\n    -\n    -@@ bisect.c: static int is_expected_rev(const struct object_id *oid)\n    - enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n    - \t\t\t\t  int no_checkout)\n    - {\n    --\tchar bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n    - \tstruct commit *commit;\n    - \tstruct pretty_print_context pp = {0};\n    - \tstruct strbuf commit_msg = STRBUF_INIT;\n    -\n    --\toid_to_hex_r(bisect_rev_hex, bisect_rev);\n    - \tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n    -\n    --\targv_checkout[2] = bisect_rev_hex;\n    - \tif (no_checkout) {\n    - \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n    - \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n    - \t} else {\n    --\t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n    -+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n    -+\n    -+\t\tcmd.git_cmd = 1;\n    -+\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n    -+\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n    -+\t\tif (run_command(&cmd))\n    - \t\t\t/*\n    - \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n    - \t\t\t * and errors in the child process, signaled by res > 0\n    -\n    - ## builtin/am.c ##\n    -@@ builtin/am.c: static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n    - \tint len;\n    -\n    - \tif (!is_null_oid(&state->orig_commit)) {\n    --\t\tconst char *av[4] = { \"show\", NULL, \"--\", NULL };\n    --\t\tchar *new_oid_str;\n    --\t\tint ret;\n    -+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n    -\n    --\t\tav[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));\n    --\t\tret = run_command_v_opt(av, RUN_GIT_CMD);\n    --\t\tfree(new_oid_str);\n    --\t\treturn ret;\n    -+\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n    -+\t\t\t     \"--\", NULL);\n    -+\t\tcmd.git_cmd = 1;\n    -+\t\treturn run_command(&cmd);\n    - \t}\n    -\n    - \tswitch (sub_mode) {\n    +    to run_command_v_opt().  This avoids the use of magic index numbers and\n    +    makes simplifies the possible addition of more arguments in the future.\n\n      ## builtin/difftool.c ##\n     @@ builtin/difftool.c: static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n    @@ ll-merge.c: static enum ll_merge_result ll_ext_merge(const struct ll_merge_drive\n      \tif (fd < 0)\n      \t\tgoto bad;\n\n    - ## sequencer.c ##\n    -@@ sequencer.c: static int error_failed_squash(struct repository *r,\n    -\n    - static int do_exec(struct repository *r, const char *command_line)\n    - {\n    --\tconst char *child_argv[] = { NULL, NULL };\n    -+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n    - \tint dirty, status;\n    -\n    - \tfprintf(stderr, _(\"Executing: %s\\n\"), command_line);\n    --\tchild_argv[0] = command_line;\n    --\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n    -+\tcmd.use_shell = 1;\n    -+\tstrvec_push(&cmd.args, command_line);\n    -+\tstatus = run_command(&cmd);\n    -\n    - \t/* force re-reading of the cache */\n    - \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0)\n    -\n      ## t/helper/test-fake-ssh.c ##\n     @@ t/helper/test-fake-ssh.c: int cmd_main(int argc, const char **argv)\n      \tstruct strbuf buf = STRBUF_INIT;\n 3:  7745e16df4 =  8:  a467a4ecb5 use child_process members \"args\" and \"env\" directly\n 5:  eeaa6aa86d !  9:  dc27358775 replace and remove run_command_v_opt_cd_env()\n    @@ run-command.h\n     @@ run-command.h: struct child_process {\n\n      /**\n    -  * The functions: child_process_init, start_command, finish_command,\n    -- * run_command, run_command_v_opt, run_command_v_opt_cd_env, child_process_clear\n    -- * do the following:\n    -+ * run_command, run_command_v_opt, child_process_clear do the following:\n    +  * The functions: start_command, finish_command, run_command,\n    +- * run_command_v_opt, run_command_v_opt_cd_env do the following:\n    ++ * run_command_v_opt do the following:\n       *\n       * - If a system call failed, errno is set and -1 is returned. A diagnostic\n       *   is printed.\n    @@ run-command.h: int run_command_v_opt_tr2(const char **argv, int opt, const char\n\n      ## tmp-objdir.h ##\n     @@\n    +  *\n       * Example:\n       *\n    ++ *\tstruct child_process child = CHILD_PROCESS_INIT;\n       *\tstruct tmp_objdir *t = tmp_objdir_create(\"incoming\");\n     - *\tif (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&\n     - *\t    !tmp_objdir_migrate(t))\n    -+ *\tstrvec_pushv(&cmd.env, tmp_objdir_env(t));\n    -+ *\tif (!run_command(&cmd)) && !tmp_objdir_migrate(t))\n    ++ *\tstrvec_push(&child.args, cmd);\n    ++ *\tstrvec_pushv(&child.env, tmp_objdir_env(t));\n    ++ *\tif (!run_command(&child)) && !tmp_objdir_migrate(t))\n       *\t\tprintf(\"success!\\n\");\n       *\telse\n       *\t\tdie(\"failed...tmp_objdir will clean up for us\");\n 6:  95b5dcbb66 = 10:  dcd65580c6 replace and remove run_command_v_opt_tr2()\n 7:  5e111ab053 ! 11:  389ec8ef54 replace and remove run_command_v_opt_cd_env_tr2()\n    @@ run-command.h: int run_auto_maintenance(int quiet);\n\n      /**\n     - * Convenience functions that encapsulate a sequence of\n    -+ * Convenience function that encapsulate a sequence of\n    ++ * Convenience function that encapsulates a sequence of\n       * start_command() followed by finish_command(). The argument argv\n       * specifies the program and its arguments. The argument opt is zero\n       * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,\n 8:  f1f438d724 ! 12:  a6e7135e31 replace and remove run_command_v_opt()\n    @@ Commit message\n\n         Suggested-by: Jeff King <peff@peff.net>\n\n    + ## bisect.c ##\n    +@@ bisect.c: enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n    + \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n    + \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n    + \t} else {\n    +-\t\tconst char *argv_checkout[] = {\n    +-\t\t\t\"checkout\", \"-q\", oid_to_hex(bisect_rev), \"--\", NULL\n    +-\t\t};\n    ++\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n    +\n    +-\t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n    ++\t\tcmd.git_cmd = 1;\n    ++\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n    ++\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n    ++\t\tif (run_command(&cmd))\n    + \t\t\t/*\n    + \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n    + \t\t\t * and errors in the child process, signaled by res > 0\n    +\n    + ## builtin/am.c ##\n    +@@ builtin/am.c: static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n    + \tint len;\n    +\n    + \tif (!is_null_oid(&state->orig_commit)) {\n    +-\t\tconst char *av[] = {\n    +-\t\t\t\"show\", oid_to_hex(&state->orig_commit), \"--\", NULL\n    +-\t\t};\n    ++\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n    +\n    +-\t\treturn run_command_v_opt(av, RUN_GIT_CMD);\n    ++\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n    ++\t\t\t     \"--\", NULL);\n    ++\t\tcmd.git_cmd = 1;\n    ++\t\treturn run_command(&cmd);\n    + \t}\n    +\n    + \tswitch (sub_mode) {\n    +\n      ## builtin/bisect--helper.c ##\n     @@ builtin/bisect--helper.c: static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a\n      \t\tstrbuf_read_file(&start_head, git_path_bisect_start(), 0);\n    @@ run-command.c: int run_command(struct child_process *cmd)\n\n      ## run-command.h ##\n     @@ run-command.h: struct child_process {\n    + }\n\n      /**\n    -  * The functions: child_process_init, start_command, finish_command,\n    -- * run_command, run_command_v_opt, child_process_clear do the following:\n    -+ * run_command, child_process_clear do the following:\n    +- * The functions: start_command, finish_command, run_command,\n    +- * run_command_v_opt do the following:\n    ++ * The functions: start_command, finish_command, run_command do the following:\n       *\n       * - If a system call failed, errno is set and -1 is returned. A diagnostic\n       *   is printed.\n    @@ run-command.h: int run_command(struct child_process *);\n     -#define RUN_CLOSE_OBJECT_STORE\t\t(1<<7)\n     -\n     -/**\n    -- * Convenience function that encapsulate a sequence of\n    +- * Convenience function that encapsulates a sequence of\n     - * start_command() followed by finish_command(). The argument argv\n     - * specifies the program and its arguments. The argument opt is zero\n     - * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,\n    @@ run-command.h: int run_command(struct child_process *);\n       * Execute the given command, sending \"in\" to its stdin, and capturing its\n       * stdout and stderr in the \"out\" and \"err\" strbufs. Any of the three may\n\n    + ## sequencer.c ##\n    +@@ sequencer.c: static int error_failed_squash(struct repository *r,\n    +\n    + static int do_exec(struct repository *r, const char *command_line)\n    + {\n    +-\tconst char *child_argv[] = { command_line, NULL };\n    ++\tstruct child_process cmd = CHILD_PROCESS_INIT;\n    + \tint dirty, status;\n    +\n    + \tfprintf(stderr, _(\"Executing: %s\\n\"), command_line);\n    +-\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n    ++\tcmd.use_shell = 1;\n    ++\tstrvec_push(&cmd.args, command_line);\n    ++\tstatus = run_command(&cmd);\n    +\n    + \t/* force re-reading of the cache */\n    + \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0)\n    +\n      ## shell.c ##\n     @@ shell.c: static void cd_to_homedir(void)\n      static void run_shell(void)\n 9:  59677c9598 <  -:  ---------- run-command: fix return value comment\n--\n2.38.1\n"},{"id":"466045","messageId":"1281ea52-cb10-638b-7612-8fa9dbecc31e@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 01/12] merge: remove always-the-same \"verbose\" arguments","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:42:59Z","receivedAt":"2022-10-30T11:43:11Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nSimplify the code that builds the arguments for the \"read-tree\"\ninvocation in reset_hard() and read_empty() to remove the \"verbose\"\nparameter.\n\nBefore 172b6428d06 (do not overwrite untracked during merge from\nunborn branch, 2010-11-14) there was a \"reset_hard()\" function that\nwould be called in two places, one of those passed a \"verbose=1\", the\nother a \"verbose=0\".\n\nAfter 172b6428d06 when read_empty() was split off from reset_hard()\nboth of these functions only had one caller. The \"verbose\" in\nread_empty() would always be false, and the one in reset_hard() would\nalways be true.\n\nThere was never a good reason for the code to act this way, it\nhappened because the read_empty() function was a copy/pasted and\nadjusted version of reset_hard().\n\nSince we're no longer conditionally adding the \"-v\" parameter\nhere (and we'd only add it for \"reset_hard()\" we'll be able to move to\na simpler and safer run-command API in the subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n builtin/merge.c | 13 +++++--------\n 1 file changed, 5 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 5900b81729..3bb49d805b 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -345,14 +345,12 @@ static int save_state(struct object_id *stash)\n \treturn rc;\n }\n\n-static void read_empty(const struct object_id *oid, int verbose)\n+static void read_empty(const struct object_id *oid)\n {\n \tint i = 0;\n \tconst char *args[7];\n\n \targs[i++] = \"read-tree\";\n-\tif (verbose)\n-\t\targs[i++] = \"-v\";\n \targs[i++] = \"-m\";\n \targs[i++] = \"-u\";\n \targs[i++] = empty_tree_oid_hex();\n@@ -363,14 +361,13 @@ static void read_empty(const struct object_id *oid, int verbose)\n \t\tdie(_(\"read-tree failed\"));\n }\n\n-static void reset_hard(const struct object_id *oid, int verbose)\n+static void reset_hard(const struct object_id *oid)\n {\n \tint i = 0;\n \tconst char *args[6];\n\n \targs[i++] = \"read-tree\";\n-\tif (verbose)\n-\t\targs[i++] = \"-v\";\n+\targs[i++] = \"-v\";\n \targs[i++] = \"--reset\";\n \targs[i++] = \"-u\";\n \targs[i++] = oid_to_hex(oid);\n@@ -385,7 +382,7 @@ static void restore_state(const struct object_id *head,\n {\n \tstruct strvec args = STRVEC_INIT;\n\n-\treset_hard(head, 1);\n+\treset_hard(head);\n\n \tif (is_null_oid(stash))\n \t\tgoto refresh_cache;\n@@ -1470,7 +1467,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t       check_trust_level);\n\n \t\tremote_head_oid = &remoteheads->item->object.oid;\n-\t\tread_empty(remote_head_oid, 0);\n+\t\tread_empty(remote_head_oid);\n \t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head_oid, NULL, 0,\n \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n \t\tgoto done;\n--\n2.38.1\n"},{"id":"466046","messageId":"a9b56eb8-1ef9-0e28-1ea2-85ccad7903d2@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 02/12] run-command: fix return value comment","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:45:13Z","receivedAt":"2022-10-30T11:45:26Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"483bbd4e4c (run-command: introduce child_process_init(), 2014-08-19) and\n2d71608ec0 (run-command: factor out child_process_clear(), 2015-10-24)\nadded help texts about child_process_init() and child_process_clear()\nwithout updating the immediately following documentation of return codes\nthat only applied to the preexisting functions.\n\n4c4066d95d (run-command: move doc to run-command.h, 2019-11-17) started\nto list the functions explicitly that this paragraph applies to, but\nstill wrongly included child_process_init() and child_process_clear().\nRemove their names from that list.\n\nSuggested-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n run-command.h | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/run-command.h b/run-command.h\nindex e3e1ea01ad..820fc25b02 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -150,9 +150,8 @@ struct child_process {\n }\n\n /**\n- * The functions: child_process_init, start_command, finish_command,\n- * run_command, run_command_v_opt, run_command_v_opt_cd_env, child_process_clear\n- * do the following:\n+ * The functions: start_command, finish_command, run_command,\n+ * run_command_v_opt, run_command_v_opt_cd_env do the following:\n  *\n  * - If a system call failed, errno is set and -1 is returned. A diagnostic\n  *   is printed.\n--\n2.38.1\n"},{"id":"466047","messageId":"e3a5cf13-d1c7-20b3-3f40-3eaff5a18b67@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 03/12] am: simplify building \"show\" argument list","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:46:11Z","receivedAt":"2022-10-30T11:46:32Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Build the string array av during initialization, without any magic\nnumbers or heap allocations.  Not duplicating the result of oid_to_hex()\nis safe because run_command_v_opt() duplicates all arguments already.\n(It would even be safe if it didn't, but that's a different story.)\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n builtin/am.c | 11 ++++-------\n 1 file changed, 4 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 39fea24833..5781e7a95e 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -2187,14 +2187,11 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n \tint len;\n\n \tif (!is_null_oid(&state->orig_commit)) {\n-\t\tconst char *av[4] = { \"show\", NULL, \"--\", NULL };\n-\t\tchar *new_oid_str;\n-\t\tint ret;\n+\t\tconst char *av[] = {\n+\t\t\t\"show\", oid_to_hex(&state->orig_commit), \"--\", NULL\n+\t\t};\n\n-\t\tav[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));\n-\t\tret = run_command_v_opt(av, RUN_GIT_CMD);\n-\t\tfree(new_oid_str);\n-\t\treturn ret;\n+\t\treturn run_command_v_opt(av, RUN_GIT_CMD);\n \t}\n\n \tswitch (sub_mode) {\n--\n2.38.1\n"},{"id":"466048","messageId":"78a5fb6d-4728-e6dc-a3e2-2d38c95ad381@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 04/12] bisect: simplify building \"checkout\" argument list","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:47:02Z","receivedAt":"2022-10-30T11:47:12Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Reduce the scope of argv_checkout, which allows to fully build it during\ninitialization.  Use oid_to_hex() instead of oid_to_hex_r(), because\nthat's simpler and using the static buffer of the former is just as safe\nas the old static argv_checkout.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n bisect.c | 9 ++++-----\n 1 file changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex fd581b85a7..090aa5c4b4 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -22,8 +22,6 @@ static struct oid_array skipped_revs;\n\n static struct object_id *current_bad_oid;\n\n-static const char *argv_checkout[] = {\"checkout\", \"-q\", NULL, \"--\", NULL};\n-\n static const char *term_bad;\n static const char *term_good;\n\n@@ -729,19 +727,20 @@ static int is_expected_rev(const struct object_id *oid)\n enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n \t\t\t\t  int no_checkout)\n {\n-\tchar bisect_rev_hex[GIT_MAX_HEXSZ + 1];\n \tstruct commit *commit;\n \tstruct pretty_print_context pp = {0};\n \tstruct strbuf commit_msg = STRBUF_INIT;\n\n-\toid_to_hex_r(bisect_rev_hex, bisect_rev);\n \tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n\n-\targv_checkout[2] = bisect_rev_hex;\n \tif (no_checkout) {\n \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n \t} else {\n+\t\tconst char *argv_checkout[] = {\n+\t\t\t\"checkout\", \"-q\", oid_to_hex(bisect_rev), \"--\", NULL\n+\t\t};\n+\n \t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n \t\t\t/*\n \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n--\n2.38.1\n"},{"id":"466049","messageId":"434b9dde-8e87-6890-d2b1-581e88bd3c03@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 05/12] bisect--helper: factor out do_bisect_run()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:48:16Z","receivedAt":"2022-10-30T11:48:26Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Deduplicate the code for reporting and starting the bisect run command\nby moving it to a short helper function.  Use a string array instead of\na strvec to prepare the arguments, for simplicity.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n builtin/bisect--helper.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 28ef7ec2a4..70d1e9d1ad 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -1142,8 +1142,14 @@ static int get_first_good(const char *refname UNUSED,\n \treturn 1;\n }\n\n-static int verify_good(const struct bisect_terms *terms,\n-\t\t       const char **quoted_argv)\n+static int do_bisect_run(const char *command)\n+{\n+\tconst char *argv[] = { command, NULL };\n+\tprintf(_(\"running %s\\n\"), command);\n+\treturn run_command_v_opt(argv, RUN_USING_SHELL);\n+}\n+\n+static int verify_good(const struct bisect_terms *terms, const char *command)\n {\n \tint rc;\n \tenum bisect_error res;\n@@ -1163,8 +1169,7 @@ static int verify_good(const struct bisect_terms *terms,\n \tif (res != BISECT_OK)\n \t\treturn -1;\n\n-\tprintf(_(\"running %s\\n\"), quoted_argv[0]);\n-\trc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);\n+\trc = do_bisect_run(command);\n\n \tres = bisect_checkout(&current_rev, no_checkout);\n \tif (res != BISECT_OK)\n@@ -1177,7 +1182,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n {\n \tint res = BISECT_OK;\n \tstruct strbuf command = STRBUF_INIT;\n-\tstruct strvec run_args = STRVEC_INIT;\n \tconst char *new_state;\n \tint temporary_stdout_fd, saved_stdout;\n \tint is_first_run = 1;\n@@ -1192,11 +1196,8 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \t\treturn BISECT_FAILED;\n \t}\n\n-\tstrvec_push(&run_args, command.buf);\n-\n \twhile (1) {\n-\t\tprintf(_(\"running %s\\n\"), command.buf);\n-\t\tres = run_command_v_opt(run_args.v, RUN_USING_SHELL);\n+\t\tres = do_bisect_run(command.buf);\n\n \t\t/*\n \t\t * Exit code 126 and 127 can either come from the shell\n@@ -1206,7 +1207,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \t\t * missing or non-executable script.\n \t\t */\n \t\tif (is_first_run && (res == 126 || res == 127)) {\n-\t\t\tint rc = verify_good(terms, run_args.v);\n+\t\t\tint rc = verify_good(terms, command.buf);\n \t\t\tis_first_run = 0;\n \t\t\tif (rc < 0) {\n \t\t\t\terror(_(\"unable to verify '%s' on good\"\n@@ -1273,7 +1274,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)\n \t}\n\n \tstrbuf_release(&command);\n-\tstrvec_clear(&run_args);\n \treturn res;\n }\n\n--\n2.38.1\n"},{"id":"466050","messageId":"3ba55be2-c0f7-77e6-1b40-323786ccc2bb@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 06/12] sequencer: simplify building argument list in do_exec()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:49:37Z","receivedAt":"2022-10-30T11:49:48Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Build child_argv during initialization, taking advantage of the C99\nsupport for initialization expressions that are not compile time\nconstants.  This avoids the use of a magic index constant and is shorter\nand simpler.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n sequencer.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex debb2ecbaf..66eedd2c76 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3558,11 +3558,10 @@ static int error_failed_squash(struct repository *r,\n\n static int do_exec(struct repository *r, const char *command_line)\n {\n-\tconst char *child_argv[] = { NULL, NULL };\n+\tconst char *child_argv[] = { command_line, NULL };\n \tint dirty, status;\n\n \tfprintf(stderr, _(\"Executing: %s\\n\"), command_line);\n-\tchild_argv[0] = command_line;\n \tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n\n \t/* force re-reading of the cache */\n--\n2.38.1\n"},{"id":"466051","messageId":"76272512-1e37-0206-5518-5c9c5e646fe8@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 07/12] use child_process member \"args\" instead of string array variable","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:50:27Z","receivedAt":"2022-10-30T11:50:40Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Use run_command() with a struct child_process variable and populate its\n\"args\" member directly instead of building a string array and passing it\nto run_command_v_opt().  This avoids the use of magic index numbers and\nmakes simplifies the possible addition of more arguments in the future.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n builtin/difftool.c       | 17 +++++++++--------\n builtin/merge.c          | 32 ++++++++++++--------------------\n builtin/remote.c         | 14 ++++++++------\n compat/mingw.c           | 11 +++++++----\n ll-merge.c               |  7 ++++---\n t/helper/test-fake-ssh.c |  7 ++++---\n 6 files changed, 44 insertions(+), 44 deletions(-)\n\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 4b10ad1a36..22bcc3444b 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -360,8 +360,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \tstruct pair_entry *entry;\n \tstruct index_state wtindex;\n \tstruct checkout lstate, rstate;\n-\tint flags = RUN_GIT_CMD, err = 0;\n-\tconst char *helper_argv[] = { \"difftool--helper\", NULL, NULL, NULL };\n+\tint err = 0;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct hashmap wt_modified, tmp_modified;\n \tint indices_loaded = 0;\n\n@@ -563,16 +563,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,\n \t}\n\n \tstrbuf_setlen(&ldir, ldir_len);\n-\thelper_argv[1] = ldir.buf;\n \tstrbuf_setlen(&rdir, rdir_len);\n-\thelper_argv[2] = rdir.buf;\n\n \tif (extcmd) {\n-\t\thelper_argv[0] = extcmd;\n-\t\tflags = 0;\n-\t} else\n+\t\tstrvec_push(&cmd.args, extcmd);\n+\t} else {\n+\t\tstrvec_push(&cmd.args, \"difftool--helper\");\n+\t\tcmd.git_cmd = 1;\n \t\tsetenv(\"GIT_DIFFTOOL_DIRDIFF\", \"true\", 1);\n-\tret = run_command_v_opt(helper_argv, flags);\n+\t}\n+\tstrvec_pushl(&cmd.args, ldir.buf, rdir.buf, NULL);\n+\tret = run_command(&cmd);\n\n \t/* TODO: audit for interaction with sparse-index. */\n \tensure_full_index(&wtindex);\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 3bb49d805b..3481577145 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -347,33 +347,25 @@ static int save_state(struct object_id *stash)\n\n static void read_empty(const struct object_id *oid)\n {\n-\tint i = 0;\n-\tconst char *args[7];\n-\n-\targs[i++] = \"read-tree\";\n-\targs[i++] = \"-m\";\n-\targs[i++] = \"-u\";\n-\targs[i++] = empty_tree_oid_hex();\n-\targs[i++] = oid_to_hex(oid);\n-\targs[i] = NULL;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tstrvec_pushl(&cmd.args, \"read-tree\", \"-m\", \"-u\", empty_tree_oid_hex(),\n+\t\t     oid_to_hex(oid), NULL);\n+\tcmd.git_cmd = 1;\n\n-\tif (run_command_v_opt(args, RUN_GIT_CMD))\n+\tif (run_command(&cmd))\n \t\tdie(_(\"read-tree failed\"));\n }\n\n static void reset_hard(const struct object_id *oid)\n {\n-\tint i = 0;\n-\tconst char *args[6];\n-\n-\targs[i++] = \"read-tree\";\n-\targs[i++] = \"-v\";\n-\targs[i++] = \"--reset\";\n-\targs[i++] = \"-u\";\n-\targs[i++] = oid_to_hex(oid);\n-\targs[i] = NULL;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tstrvec_pushl(&cmd.args, \"read-tree\", \"-v\", \"--reset\", \"-u\",\n+\t\t     oid_to_hex(oid), NULL);\n+\tcmd.git_cmd = 1;\n\n-\tif (run_command_v_opt(args, RUN_GIT_CMD))\n+\tif (run_command(&cmd))\n \t\tdie(_(\"read-tree failed\"));\n }\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 93285fc06e..f0c11fa301 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -92,13 +92,15 @@ static int verbose;\n\n static int fetch_remote(const char *name)\n {\n-\tconst char *argv[] = { \"fetch\", name, NULL, NULL };\n-\tif (verbose) {\n-\t\targv[1] = \"-v\";\n-\t\targv[2] = name;\n-\t}\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tstrvec_push(&cmd.args, \"fetch\");\n+\tif (verbose)\n+\t\tstrvec_push(&cmd.args, \"-v\");\n+\tstrvec_push(&cmd.args, name);\n+\tcmd.git_cmd = 1;\n \tprintf_ln(_(\"Updating %s\"), name);\n-\tif (run_command_v_opt(argv, RUN_GIT_CMD))\n+\tif (run_command(&cmd))\n \t\treturn error(_(\"Could not fetch %s\"), name);\n \treturn 0;\n }\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex 901375d584..d614f156df 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -196,16 +196,19 @@ static int read_yes_no_answer(void)\n static int ask_yes_no_if_possible(const char *format, ...)\n {\n \tchar question[4096];\n-\tconst char *retry_hook[] = { NULL, NULL, NULL };\n+\tconst char *retry_hook;\n \tva_list args;\n\n \tva_start(args, format);\n \tvsnprintf(question, sizeof(question), format, args);\n \tva_end(args);\n\n-\tif ((retry_hook[0] = mingw_getenv(\"GIT_ASK_YESNO\"))) {\n-\t\tretry_hook[1] = question;\n-\t\treturn !run_command_v_opt(retry_hook, 0);\n+\tretry_hook = mingw_getenv(\"GIT_ASK_YESNO\");\n+\tif (retry_hook) {\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\tstrvec_pushl(&cmd.args, retry_hook, question, NULL);\n+\t\treturn !run_command(&cmd);\n \t}\n\n \tif (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))\ndiff --git a/ll-merge.c b/ll-merge.c\nindex a8e2db9336..22a603e8af 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -193,7 +193,7 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,\n \tstruct strbuf cmd = STRBUF_INIT;\n \tstruct strbuf_expand_dict_entry dict[6];\n \tstruct strbuf path_sq = STRBUF_INIT;\n-\tconst char *args[] = { NULL, NULL };\n+\tstruct child_process child = CHILD_PROCESS_INIT;\n \tint status, fd, i;\n \tstruct stat st;\n \tenum ll_merge_result ret;\n@@ -219,8 +219,9 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,\n\n \tstrbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);\n\n-\targs[0] = cmd.buf;\n-\tstatus = run_command_v_opt(args, RUN_USING_SHELL);\n+\tchild.use_shell = 1;\n+\tstrvec_push(&child.args, cmd.buf);\n+\tstatus = run_command(&child);\n \tfd = open(temp[1], O_RDONLY);\n \tif (fd < 0)\n \t\tgoto bad;\ndiff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c\nindex 12beee99ad..2e576bcc11 100644\n--- a/t/helper/test-fake-ssh.c\n+++ b/t/helper/test-fake-ssh.c\n@@ -8,7 +8,7 @@ int cmd_main(int argc, const char **argv)\n \tstruct strbuf buf = STRBUF_INIT;\n \tFILE *f;\n \tint i;\n-\tconst char *child_argv[] = { NULL, NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \t/* First, print all parameters into $TRASH_DIRECTORY/ssh-output */\n \tif (!trash_directory)\n@@ -25,6 +25,7 @@ int cmd_main(int argc, const char **argv)\n \t/* Now, evaluate the *last* parameter */\n \tif (argc < 2)\n \t\treturn 0;\n-\tchild_argv[0] = argv[argc - 1];\n-\treturn run_command_v_opt(child_argv, RUN_USING_SHELL);\n+\tcmd.use_shell = 1;\n+\tstrvec_push(&cmd.args, argv[argc - 1]);\n+\treturn run_command(&cmd);\n }\n--\n2.38.1\n"},{"id":"466052","messageId":"97566ec6-ad18-a53a-ed1f-2148706fbd25@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 08/12] use child_process members \"args\" and \"env\" directly","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:51:14Z","receivedAt":"2022-10-30T11:51:31Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Build argument list and environment of child processes by using\nstruct child_process and populating its members \"args\" and \"env\"\ndirectly instead of maintaining separate strvecs and letting\nrun_command_v_opt() and friends populate these members.  This is\nsimpler, shorter and slightly more efficient.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n add-interactive.c        |   9 ++-\n builtin/add.c            |  19 +++--\n builtin/bisect--helper.c |  35 +++++-----\n builtin/clone.c          |  33 ++++-----\n builtin/gc.c             |  14 ++--\n builtin/merge.c          |  10 +--\n builtin/pull.c           | 147 +++++++++++++++++++--------------------\n builtin/remote.c         |  26 ++++---\n diff.c                   |  27 ++++---\n git.c                    |  15 ++--\n merge.c                  |  18 ++---\n scalar.c                 |  13 ++--\n sequencer.c              |  25 +++----\n 13 files changed, 185 insertions(+), 206 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex f071b2a1b4..ecc5ae1b24 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -997,18 +997,17 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,\n \tcount = list_and_choose(s, files, opts);\n \topts->flags = 0;\n \tif (count > 0) {\n-\t\tstruct strvec args = STRVEC_INIT;\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\tstrvec_pushl(&args, \"git\", \"diff\", \"-p\", \"--cached\",\n+\t\tstrvec_pushl(&cmd.args, \"git\", \"diff\", \"-p\", \"--cached\",\n \t\t\t     oid_to_hex(!is_initial ? &oid :\n \t\t\t\t\ts->r->hash_algo->empty_tree),\n \t\t\t     \"--\", NULL);\n \t\tfor (i = 0; i < files->items.nr; i++)\n \t\t\tif (files->selected[i])\n-\t\t\t\tstrvec_push(&args,\n+\t\t\t\tstrvec_push(&cmd.args,\n \t\t\t\t\t    files->items.items[i].string);\n-\t\tres = run_command_v_opt(args.v, 0);\n-\t\tstrvec_clear(&args);\n+\t\tres = run_command(&cmd);\n \t}\n\n \tputchar('\\n');\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f84372964c..626c71ec6a 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -240,8 +240,8 @@ static int refresh(int verbose, const struct pathspec *pathspec)\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst struct pathspec *pathspec)\n {\n-\tint status, i;\n-\tstruct strvec argv = STRVEC_INIT;\n+\tint i;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint use_builtin_add_i =\n \t\tgit_env_bool(\"GIT_TEST_ADD_I_USE_BUILTIN\", -1);\n\n@@ -272,19 +272,18 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n \t}\n\n-\tstrvec_push(&argv, \"add--interactive\");\n+\tstrvec_push(&cmd.args, \"add--interactive\");\n \tif (patch_mode)\n-\t\tstrvec_push(&argv, patch_mode);\n+\t\tstrvec_push(&cmd.args, patch_mode);\n \tif (revision)\n-\t\tstrvec_push(&argv, revision);\n-\tstrvec_push(&argv, \"--\");\n+\t\tstrvec_push(&cmd.args, revision);\n+\tstrvec_push(&cmd.args, \"--\");\n \tfor (i = 0; i < pathspec->nr; i++)\n \t\t/* pass original pathspec, to be re-parsed */\n-\t\tstrvec_push(&argv, pathspec->items[i].original);\n+\t\tstrvec_push(&cmd.args, pathspec->items[i].original);\n\n-\tstatus = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&argv);\n-\treturn status;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n int interactive_add(const char **argv, const char *prefix, int patch)\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 70d1e9d1ad..5c63ba6994 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -220,18 +220,17 @@ static int bisect_reset(const char *commit)\n \t}\n\n \tif (!ref_exists(\"BISECT_HEAD\")) {\n-\t\tstruct strvec argv = STRVEC_INIT;\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\tstrvec_pushl(&argv, \"checkout\", branch.buf, \"--\", NULL);\n-\t\tif (run_command_v_opt(argv.v, RUN_GIT_CMD)) {\n+\t\tcmd.git_cmd = 1;\n+\t\tstrvec_pushl(&cmd.args, \"checkout\", branch.buf, \"--\", NULL);\n+\t\tif (run_command(&cmd)) {\n \t\t\terror(_(\"could not check out original\"\n \t\t\t\t\" HEAD '%s'. Try 'git bisect\"\n \t\t\t\t\" reset <commit>'.\"), branch.buf);\n \t\t\tstrbuf_release(&branch);\n-\t\t\tstrvec_clear(&argv);\n \t\t\treturn -1;\n \t\t}\n-\t\tstrvec_clear(&argv);\n \t}\n\n \tstrbuf_release(&branch);\n@@ -1098,40 +1097,38 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n\n static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)\n {\n-\tstruct strvec args = STRVEC_INIT;\n-\tint flags = RUN_COMMAND_NO_STDIN, res = 0;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n\n \tif (bisect_next_check(terms, NULL) != 0)\n \t\treturn BISECT_FAILED;\n\n+\tcmd.no_stdin = 1;\n \tif (!argc) {\n \t\tif ((getenv(\"DISPLAY\") || getenv(\"SESSIONNAME\") || getenv(\"MSYSTEM\") ||\n \t\t     getenv(\"SECURITYSESSIONID\")) && exists_in_PATH(\"gitk\")) {\n-\t\t\tstrvec_push(&args, \"gitk\");\n+\t\t\tstrvec_push(&cmd.args, \"gitk\");\n \t\t} else {\n-\t\t\tstrvec_push(&args, \"log\");\n-\t\t\tflags |= RUN_GIT_CMD;\n+\t\t\tstrvec_push(&cmd.args, \"log\");\n+\t\t\tcmd.git_cmd = 1;\n \t\t}\n \t} else {\n \t\tif (argv[0][0] == '-') {\n-\t\t\tstrvec_push(&args, \"log\");\n-\t\t\tflags |= RUN_GIT_CMD;\n+\t\t\tstrvec_push(&cmd.args, \"log\");\n+\t\t\tcmd.git_cmd = 1;\n \t\t} else if (strcmp(argv[0], \"tig\") && !starts_with(argv[0], \"git\"))\n-\t\t\tflags |= RUN_GIT_CMD;\n+\t\t\tcmd.git_cmd = 1;\n\n-\t\tstrvec_pushv(&args, argv);\n+\t\tstrvec_pushv(&cmd.args, argv);\n \t}\n\n-\tstrvec_pushl(&args, \"--bisect\", \"--\", NULL);\n+\tstrvec_pushl(&cmd.args, \"--bisect\", \"--\", NULL);\n\n \tstrbuf_read_file(&sb, git_path_bisect_names(), 0);\n-\tsq_dequote_to_strvec(sb.buf, &args);\n+\tsq_dequote_to_strvec(sb.buf, &cmd.args);\n \tstrbuf_release(&sb);\n\n-\tres = run_command_v_opt(args.v, flags);\n-\tstrvec_clear(&args);\n-\treturn res;\n+\treturn run_command(&cmd);\n }\n\n static int get_first_good(const char *refname UNUSED,\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 547d6464b3..56e7775dae 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -653,9 +653,9 @@ static void update_head(const struct ref *our, const struct ref *remote,\n\n static int git_sparse_checkout_init(const char *repo)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint result = 0;\n-\tstrvec_pushl(&argv, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n+\tstrvec_pushl(&cmd.args, \"-C\", repo, \"sparse-checkout\", \"set\", NULL);\n\n \t/*\n \t * We must apply the setting in the current process\n@@ -663,12 +663,12 @@ static int git_sparse_checkout_init(const char *repo)\n \t */\n \tcore_apply_sparse_checkout = 1;\n\n-\tif (run_command_v_opt(argv.v, RUN_GIT_CMD)) {\n+\tcmd.git_cmd = 1;\n+\tif (run_command(&cmd)) {\n \t\terror(_(\"failed to initialize sparse-checkout\"));\n \t\tresult = 1;\n \t}\n\n-\tstrvec_clear(&argv);\n \treturn result;\n }\n\n@@ -733,37 +733,38 @@ static int checkout(int submodule_progress, int filter_submodules)\n \t\t\t   oid_to_hex(&oid), \"1\", NULL);\n\n \tif (!err && (option_recurse_submodules.nr > 0)) {\n-\t\tstruct strvec args = STRVEC_INIT;\n-\t\tstrvec_pushl(&args, \"submodule\", \"update\", \"--require-init\", \"--recursive\", NULL);\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\t\tstrvec_pushl(&cmd.args, \"submodule\", \"update\", \"--require-init\",\n+\t\t\t     \"--recursive\", NULL);\n\n \t\tif (option_shallow_submodules == 1)\n-\t\t\tstrvec_push(&args, \"--depth=1\");\n+\t\t\tstrvec_push(&cmd.args, \"--depth=1\");\n\n \t\tif (max_jobs != -1)\n-\t\t\tstrvec_pushf(&args, \"--jobs=%d\", max_jobs);\n+\t\t\tstrvec_pushf(&cmd.args, \"--jobs=%d\", max_jobs);\n\n \t\tif (submodule_progress)\n-\t\t\tstrvec_push(&args, \"--progress\");\n+\t\t\tstrvec_push(&cmd.args, \"--progress\");\n\n \t\tif (option_verbosity < 0)\n-\t\t\tstrvec_push(&args, \"--quiet\");\n+\t\t\tstrvec_push(&cmd.args, \"--quiet\");\n\n \t\tif (option_remote_submodules) {\n-\t\t\tstrvec_push(&args, \"--remote\");\n-\t\t\tstrvec_push(&args, \"--no-fetch\");\n+\t\t\tstrvec_push(&cmd.args, \"--remote\");\n+\t\t\tstrvec_push(&cmd.args, \"--no-fetch\");\n \t\t}\n\n \t\tif (filter_submodules && filter_options.choice)\n-\t\t\tstrvec_pushf(&args, \"--filter=%s\",\n+\t\t\tstrvec_pushf(&cmd.args, \"--filter=%s\",\n \t\t\t\t     expand_list_objects_filter_spec(&filter_options));\n\n \t\tif (option_single_branch >= 0)\n-\t\t\tstrvec_push(&args, option_single_branch ?\n+\t\t\tstrvec_push(&cmd.args, option_single_branch ?\n \t\t\t\t\t       \"--single-branch\" :\n \t\t\t\t\t       \"--no-single-branch\");\n\n-\t\terr = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\t\tstrvec_clear(&args);\n+\t\tcmd.git_cmd = 1;\n+\t\terr = run_command(&cmd);\n \t}\n\n \treturn err;\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 24ea85c7af..a185bec813 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -1910,20 +1910,16 @@ static char *schtasks_task_name(const char *frequency)\n static int schtasks_remove_task(enum schedule_priority schedule)\n {\n \tconst char *cmd = \"schtasks\";\n-\tint result;\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process child = CHILD_PROCESS_INIT;\n \tconst char *frequency = get_frequency(schedule);\n \tchar *name = schtasks_task_name(frequency);\n\n \tget_schedule_cmd(&cmd, NULL);\n-\tstrvec_split(&args, cmd);\n-\tstrvec_pushl(&args, \"/delete\", \"/tn\", name, \"/f\", NULL);\n-\n-\tresult = run_command_v_opt(args.v, 0);\n-\n-\tstrvec_clear(&args);\n+\tstrvec_split(&child.args, cmd);\n+\tstrvec_pushl(&child.args, \"/delete\", \"/tn\", name, \"/f\", NULL);\n \tfree(name);\n-\treturn result;\n+\n+\treturn run_command(&child);\n }\n\n static int schtasks_remove_tasks(void)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 3481577145..b3f75f55c8 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -372,22 +372,22 @@ static void reset_hard(const struct object_id *oid)\n static void restore_state(const struct object_id *head,\n \t\t\t  const struct object_id *stash)\n {\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \treset_hard(head);\n\n \tif (is_null_oid(stash))\n \t\tgoto refresh_cache;\n\n-\tstrvec_pushl(&args, \"stash\", \"apply\", \"--index\", \"--quiet\", NULL);\n-\tstrvec_push(&args, oid_to_hex(stash));\n+\tstrvec_pushl(&cmd.args, \"stash\", \"apply\", \"--index\", \"--quiet\", NULL);\n+\tstrvec_push(&cmd.args, oid_to_hex(stash));\n\n \t/*\n \t * It is OK to ignore error here, for example when there was\n \t * nothing to restore.\n \t */\n-\trun_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n+\tcmd.git_cmd = 1;\n+\trun_command(&cmd);\n\n refresh_cache:\n \tif (discard_cache() < 0 || read_cache() < 0)\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 403a24d7ca..b21edd767a 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -515,76 +515,75 @@ static void parse_repo_refspecs(int argc, const char **argv, const char **repo,\n  */\n static int run_fetch(const char *repo, const char **refspecs)\n {\n-\tstruct strvec args = STRVEC_INIT;\n-\tint ret;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_pushl(&args, \"fetch\", \"--update-head-ok\", NULL);\n+\tstrvec_pushl(&cmd.args, \"fetch\", \"--update-head-ok\", NULL);\n\n \t/* Shared options */\n-\targv_push_verbosity(&args);\n+\targv_push_verbosity(&cmd.args);\n \tif (opt_progress)\n-\t\tstrvec_push(&args, opt_progress);\n+\t\tstrvec_push(&cmd.args, opt_progress);\n\n \t/* Options passed to git-fetch */\n \tif (opt_all)\n-\t\tstrvec_push(&args, opt_all);\n+\t\tstrvec_push(&cmd.args, opt_all);\n \tif (opt_append)\n-\t\tstrvec_push(&args, opt_append);\n+\t\tstrvec_push(&cmd.args, opt_append);\n \tif (opt_upload_pack)\n-\t\tstrvec_push(&args, opt_upload_pack);\n-\targv_push_force(&args);\n+\t\tstrvec_push(&cmd.args, opt_upload_pack);\n+\targv_push_force(&cmd.args);\n \tif (opt_tags)\n-\t\tstrvec_push(&args, opt_tags);\n+\t\tstrvec_push(&cmd.args, opt_tags);\n \tif (opt_prune)\n-\t\tstrvec_push(&args, opt_prune);\n+\t\tstrvec_push(&cmd.args, opt_prune);\n \tif (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)\n \t\tswitch (recurse_submodules_cli) {\n \t\tcase RECURSE_SUBMODULES_ON:\n-\t\t\tstrvec_push(&args, \"--recurse-submodules=on\");\n+\t\t\tstrvec_push(&cmd.args, \"--recurse-submodules=on\");\n \t\t\tbreak;\n \t\tcase RECURSE_SUBMODULES_OFF:\n-\t\t\tstrvec_push(&args, \"--recurse-submodules=no\");\n+\t\t\tstrvec_push(&cmd.args, \"--recurse-submodules=no\");\n \t\t\tbreak;\n \t\tcase RECURSE_SUBMODULES_ON_DEMAND:\n-\t\t\tstrvec_push(&args, \"--recurse-submodules=on-demand\");\n+\t\t\tstrvec_push(&cmd.args, \"--recurse-submodules=on-demand\");\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tBUG(\"submodule recursion option not understood\");\n \t\t}\n \tif (max_children)\n-\t\tstrvec_push(&args, max_children);\n+\t\tstrvec_push(&cmd.args, max_children);\n \tif (opt_dry_run)\n-\t\tstrvec_push(&args, \"--dry-run\");\n+\t\tstrvec_push(&cmd.args, \"--dry-run\");\n \tif (opt_keep)\n-\t\tstrvec_push(&args, opt_keep);\n+\t\tstrvec_push(&cmd.args, opt_keep);\n \tif (opt_depth)\n-\t\tstrvec_push(&args, opt_depth);\n+\t\tstrvec_push(&cmd.args, opt_depth);\n \tif (opt_unshallow)\n-\t\tstrvec_push(&args, opt_unshallow);\n+\t\tstrvec_push(&cmd.args, opt_unshallow);\n \tif (opt_update_shallow)\n-\t\tstrvec_push(&args, opt_update_shallow);\n+\t\tstrvec_push(&cmd.args, opt_update_shallow);\n \tif (opt_refmap)\n-\t\tstrvec_push(&args, opt_refmap);\n+\t\tstrvec_push(&cmd.args, opt_refmap);\n \tif (opt_ipv4)\n-\t\tstrvec_push(&args, opt_ipv4);\n+\t\tstrvec_push(&cmd.args, opt_ipv4);\n \tif (opt_ipv6)\n-\t\tstrvec_push(&args, opt_ipv6);\n+\t\tstrvec_push(&cmd.args, opt_ipv6);\n \tif (opt_show_forced_updates > 0)\n-\t\tstrvec_push(&args, \"--show-forced-updates\");\n+\t\tstrvec_push(&cmd.args, \"--show-forced-updates\");\n \telse if (opt_show_forced_updates == 0)\n-\t\tstrvec_push(&args, \"--no-show-forced-updates\");\n+\t\tstrvec_push(&cmd.args, \"--no-show-forced-updates\");\n \tif (set_upstream)\n-\t\tstrvec_push(&args, set_upstream);\n-\tstrvec_pushv(&args, opt_fetch.v);\n+\t\tstrvec_push(&cmd.args, set_upstream);\n+\tstrvec_pushv(&cmd.args, opt_fetch.v);\n\n \tif (repo) {\n-\t\tstrvec_push(&args, repo);\n-\t\tstrvec_pushv(&args, refspecs);\n+\t\tstrvec_push(&cmd.args, repo);\n+\t\tstrvec_pushv(&cmd.args, refspecs);\n \t} else if (*refspecs)\n \t\tBUG(\"refspecs without repo?\");\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE);\n-\tstrvec_clear(&args);\n-\treturn ret;\n+\tcmd.git_cmd = 1;\n+\tcmd.close_object_store = 1;\n+\treturn run_command(&cmd);\n }\n\n /**\n@@ -653,52 +652,50 @@ static int update_submodules(void)\n  */\n static int run_merge(void)\n {\n-\tint ret;\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_pushl(&args, \"merge\", NULL);\n+\tstrvec_pushl(&cmd.args, \"merge\", NULL);\n\n \t/* Shared options */\n-\targv_push_verbosity(&args);\n+\targv_push_verbosity(&cmd.args);\n \tif (opt_progress)\n-\t\tstrvec_push(&args, opt_progress);\n+\t\tstrvec_push(&cmd.args, opt_progress);\n\n \t/* Options passed to git-merge */\n \tif (opt_diffstat)\n-\t\tstrvec_push(&args, opt_diffstat);\n+\t\tstrvec_push(&cmd.args, opt_diffstat);\n \tif (opt_log)\n-\t\tstrvec_push(&args, opt_log);\n+\t\tstrvec_push(&cmd.args, opt_log);\n \tif (opt_signoff)\n-\t\tstrvec_push(&args, opt_signoff);\n+\t\tstrvec_push(&cmd.args, opt_signoff);\n \tif (opt_squash)\n-\t\tstrvec_push(&args, opt_squash);\n+\t\tstrvec_push(&cmd.args, opt_squash);\n \tif (opt_commit)\n-\t\tstrvec_push(&args, opt_commit);\n+\t\tstrvec_push(&cmd.args, opt_commit);\n \tif (opt_edit)\n-\t\tstrvec_push(&args, opt_edit);\n+\t\tstrvec_push(&cmd.args, opt_edit);\n \tif (cleanup_arg)\n-\t\tstrvec_pushf(&args, \"--cleanup=%s\", cleanup_arg);\n+\t\tstrvec_pushf(&cmd.args, \"--cleanup=%s\", cleanup_arg);\n \tif (opt_ff)\n-\t\tstrvec_push(&args, opt_ff);\n+\t\tstrvec_push(&cmd.args, opt_ff);\n \tif (opt_verify)\n-\t\tstrvec_push(&args, opt_verify);\n+\t\tstrvec_push(&cmd.args, opt_verify);\n \tif (opt_verify_signatures)\n-\t\tstrvec_push(&args, opt_verify_signatures);\n-\tstrvec_pushv(&args, opt_strategies.v);\n-\tstrvec_pushv(&args, opt_strategy_opts.v);\n+\t\tstrvec_push(&cmd.args, opt_verify_signatures);\n+\tstrvec_pushv(&cmd.args, opt_strategies.v);\n+\tstrvec_pushv(&cmd.args, opt_strategy_opts.v);\n \tif (opt_gpg_sign)\n-\t\tstrvec_push(&args, opt_gpg_sign);\n+\t\tstrvec_push(&cmd.args, opt_gpg_sign);\n \tif (opt_autostash == 0)\n-\t\tstrvec_push(&args, \"--no-autostash\");\n+\t\tstrvec_push(&cmd.args, \"--no-autostash\");\n \telse if (opt_autostash == 1)\n-\t\tstrvec_push(&args, \"--autostash\");\n+\t\tstrvec_push(&cmd.args, \"--autostash\");\n \tif (opt_allow_unrelated_histories > 0)\n-\t\tstrvec_push(&args, \"--allow-unrelated-histories\");\n+\t\tstrvec_push(&cmd.args, \"--allow-unrelated-histories\");\n\n-\tstrvec_push(&args, \"FETCH_HEAD\");\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n-\treturn ret;\n+\tstrvec_push(&cmd.args, \"FETCH_HEAD\");\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n /**\n@@ -879,43 +876,41 @@ static int get_rebase_newbase_and_upstream(struct object_id *newbase,\n static int run_rebase(const struct object_id *newbase,\n \t\tconst struct object_id *upstream)\n {\n-\tint ret;\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_push(&args, \"rebase\");\n+\tstrvec_push(&cmd.args, \"rebase\");\n\n \t/* Shared options */\n-\targv_push_verbosity(&args);\n+\targv_push_verbosity(&cmd.args);\n\n \t/* Options passed to git-rebase */\n \tif (opt_rebase == REBASE_MERGES)\n-\t\tstrvec_push(&args, \"--rebase-merges\");\n+\t\tstrvec_push(&cmd.args, \"--rebase-merges\");\n \telse if (opt_rebase == REBASE_INTERACTIVE)\n-\t\tstrvec_push(&args, \"--interactive\");\n+\t\tstrvec_push(&cmd.args, \"--interactive\");\n \tif (opt_diffstat)\n-\t\tstrvec_push(&args, opt_diffstat);\n-\tstrvec_pushv(&args, opt_strategies.v);\n-\tstrvec_pushv(&args, opt_strategy_opts.v);\n+\t\tstrvec_push(&cmd.args, opt_diffstat);\n+\tstrvec_pushv(&cmd.args, opt_strategies.v);\n+\tstrvec_pushv(&cmd.args, opt_strategy_opts.v);\n \tif (opt_gpg_sign)\n-\t\tstrvec_push(&args, opt_gpg_sign);\n+\t\tstrvec_push(&cmd.args, opt_gpg_sign);\n \tif (opt_signoff)\n-\t\tstrvec_push(&args, opt_signoff);\n+\t\tstrvec_push(&cmd.args, opt_signoff);\n \tif (opt_autostash == 0)\n-\t\tstrvec_push(&args, \"--no-autostash\");\n+\t\tstrvec_push(&cmd.args, \"--no-autostash\");\n \telse if (opt_autostash == 1)\n-\t\tstrvec_push(&args, \"--autostash\");\n+\t\tstrvec_push(&cmd.args, \"--autostash\");\n \tif (opt_verify_signatures &&\n \t    !strcmp(opt_verify_signatures, \"--verify-signatures\"))\n \t\twarning(_(\"ignoring --verify-signatures for rebase\"));\n\n-\tstrvec_push(&args, \"--onto\");\n-\tstrvec_push(&args, oid_to_hex(newbase));\n+\tstrvec_push(&cmd.args, \"--onto\");\n+\tstrvec_push(&cmd.args, oid_to_hex(newbase));\n\n-\tstrvec_push(&args, oid_to_hex(upstream));\n+\tstrvec_push(&cmd.args, oid_to_hex(upstream));\n\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n-\treturn ret;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n static int get_can_ff(struct object_id *orig_head,\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex f0c11fa301..729f6f3643 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -1510,37 +1510,35 @@ static int update(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"prune remotes after fetching\")),\n \t\tOPT_END()\n \t};\n-\tstruct strvec fetch_argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint default_defined = 0;\n-\tint retval;\n\n \targc = parse_options(argc, argv, prefix, options,\n \t\t\t     builtin_remote_update_usage,\n \t\t\t     PARSE_OPT_KEEP_ARGV0);\n\n-\tstrvec_push(&fetch_argv, \"fetch\");\n+\tstrvec_push(&cmd.args, \"fetch\");\n\n \tif (prune != -1)\n-\t\tstrvec_push(&fetch_argv, prune ? \"--prune\" : \"--no-prune\");\n+\t\tstrvec_push(&cmd.args, prune ? \"--prune\" : \"--no-prune\");\n \tif (verbose)\n-\t\tstrvec_push(&fetch_argv, \"-v\");\n-\tstrvec_push(&fetch_argv, \"--multiple\");\n+\t\tstrvec_push(&cmd.args, \"-v\");\n+\tstrvec_push(&cmd.args, \"--multiple\");\n \tif (argc < 2)\n-\t\tstrvec_push(&fetch_argv, \"default\");\n+\t\tstrvec_push(&cmd.args, \"default\");\n \tfor (i = 1; i < argc; i++)\n-\t\tstrvec_push(&fetch_argv, argv[i]);\n+\t\tstrvec_push(&cmd.args, argv[i]);\n\n-\tif (strcmp(fetch_argv.v[fetch_argv.nr-1], \"default\") == 0) {\n+\tif (strcmp(cmd.args.v[cmd.args.nr-1], \"default\") == 0) {\n \t\tgit_config(get_remote_default, &default_defined);\n \t\tif (!default_defined) {\n-\t\t\tstrvec_pop(&fetch_argv);\n-\t\t\tstrvec_push(&fetch_argv, \"--all\");\n+\t\t\tstrvec_pop(&cmd.args);\n+\t\t\tstrvec_push(&cmd.args, \"--all\");\n \t\t}\n \t}\n\n-\tretval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&fetch_argv);\n-\treturn retval;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n static int remove_all_fetch_refspecs(const char *key)\ndiff --git a/diff.c b/diff.c\nindex 285d6e2d57..2f1bb18772 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4301,35 +4301,34 @@ static void run_external_diff(const char *pgm,\n \t\t\t      const char *xfrm_msg,\n \t\t\t      struct diff_options *o)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n-\tstruct strvec env = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n\n-\tstrvec_push(&argv, pgm);\n-\tstrvec_push(&argv, name);\n+\tstrvec_push(&cmd.args, pgm);\n+\tstrvec_push(&cmd.args, name);\n\n \tif (one && two) {\n-\t\tadd_external_diff_name(o->repo, &argv, name, one);\n+\t\tadd_external_diff_name(o->repo, &cmd.args, name, one);\n \t\tif (!other)\n-\t\t\tadd_external_diff_name(o->repo, &argv, name, two);\n+\t\t\tadd_external_diff_name(o->repo, &cmd.args, name, two);\n \t\telse {\n-\t\t\tadd_external_diff_name(o->repo, &argv, other, two);\n-\t\t\tstrvec_push(&argv, other);\n-\t\t\tstrvec_push(&argv, xfrm_msg);\n+\t\t\tadd_external_diff_name(o->repo, &cmd.args, other, two);\n+\t\t\tstrvec_push(&cmd.args, other);\n+\t\t\tstrvec_push(&cmd.args, xfrm_msg);\n \t\t}\n \t}\n\n-\tstrvec_pushf(&env, \"GIT_DIFF_PATH_COUNTER=%d\", ++o->diff_path_counter);\n-\tstrvec_pushf(&env, \"GIT_DIFF_PATH_TOTAL=%d\", q->nr);\n+\tstrvec_pushf(&cmd.env, \"GIT_DIFF_PATH_COUNTER=%d\",\n+\t\t     ++o->diff_path_counter);\n+\tstrvec_pushf(&cmd.env, \"GIT_DIFF_PATH_TOTAL=%d\", q->nr);\n\n \tdiff_free_filespec_data(one);\n \tdiff_free_filespec_data(two);\n-\tif (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v))\n+\tcmd.use_shell = 1;\n+\tif (run_command(&cmd))\n \t\tdie(_(\"external diff died, stopping at %s\"), name);\n\n \tremove_tempfile();\n-\tstrvec_clear(&argv);\n-\tstrvec_clear(&env);\n }\n\n static int similarity_index(struct diff_filepair *p)\ndiff --git a/git.c b/git.c\nindex ee7758dcb0..6662548986 100644\n--- a/git.c\n+++ b/git.c\n@@ -787,7 +787,7 @@ static int run_argv(int *argcp, const char ***argv)\n \t\tif (!done_alias)\n \t\t\thandle_builtin(*argcp, *argv);\n \t\telse if (get_builtin(**argv)) {\n-\t\t\tstruct strvec args = STRVEC_INIT;\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \t\t\tint i;\n\n \t\t\t/*\n@@ -804,18 +804,21 @@ static int run_argv(int *argcp, const char ***argv)\n\n \t\t\tcommit_pager_choice();\n\n-\t\t\tstrvec_push(&args, \"git\");\n+\t\t\tstrvec_push(&cmd.args, \"git\");\n \t\t\tfor (i = 0; i < *argcp; i++)\n-\t\t\t\tstrvec_push(&args, (*argv)[i]);\n+\t\t\t\tstrvec_push(&cmd.args, (*argv)[i]);\n\n-\t\t\ttrace_argv_printf(args.v, \"trace: exec:\");\n+\t\t\ttrace_argv_printf(cmd.args.v, \"trace: exec:\");\n\n \t\t\t/*\n \t\t\t * if we fail because the command is not found, it is\n \t\t\t * OK to return. Otherwise, we just pass along the status code.\n \t\t\t */\n-\t\t\ti = run_command_v_opt_tr2(args.v, RUN_SILENT_EXEC_FAILURE |\n-\t\t\t\t\t\t  RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, \"git_alias\");\n+\t\t\tcmd.silent_exec_failure = 1;\n+\t\t\tcmd.clean_on_exit = 1;\n+\t\t\tcmd.wait_after_clean = 1;\n+\t\t\tcmd.trace2_child_class = \"git_alias\";\n+\t\t\ti = run_command(&cmd);\n \t\t\tif (i >= 0 || errno != ENOENT)\n \t\t\t\texit(i);\n \t\t\tdie(\"could not execute builtin %s\", **argv);\ndiff --git a/merge.c b/merge.c\nindex 2382ff66d3..445b4f19aa 100644\n--- a/merge.c\n+++ b/merge.c\n@@ -19,22 +19,22 @@ int try_merge_command(struct repository *r,\n \t\t      const char **xopts, struct commit_list *common,\n \t\t      const char *head_arg, struct commit_list *remotes)\n {\n-\tstruct strvec args = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint i, ret;\n \tstruct commit_list *j;\n\n-\tstrvec_pushf(&args, \"merge-%s\", strategy);\n+\tstrvec_pushf(&cmd.args, \"merge-%s\", strategy);\n \tfor (i = 0; i < xopts_nr; i++)\n-\t\tstrvec_pushf(&args, \"--%s\", xopts[i]);\n+\t\tstrvec_pushf(&cmd.args, \"--%s\", xopts[i]);\n \tfor (j = common; j; j = j->next)\n-\t\tstrvec_push(&args, merge_argument(j->item));\n-\tstrvec_push(&args, \"--\");\n-\tstrvec_push(&args, head_arg);\n+\t\tstrvec_push(&cmd.args, merge_argument(j->item));\n+\tstrvec_push(&cmd.args, \"--\");\n+\tstrvec_push(&cmd.args, head_arg);\n \tfor (j = remotes; j; j = j->next)\n-\t\tstrvec_push(&args, merge_argument(j->item));\n+\t\tstrvec_push(&cmd.args, merge_argument(j->item));\n\n-\tret = run_command_v_opt(args.v, RUN_GIT_CMD);\n-\tstrvec_clear(&args);\n+\tcmd.git_cmd = 1;\n+\tret = run_command(&cmd);\n\n \tdiscard_index(r->index);\n \tif (repo_read_index(r) < 0)\ndiff --git a/scalar.c b/scalar.c\nindex 6de9c0ee52..03f9e480dd 100644\n--- a/scalar.c\n+++ b/scalar.c\n@@ -69,21 +69,18 @@ static void setup_enlistment_directory(int argc, const char **argv,\n\n static int run_git(const char *arg, ...)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tva_list args;\n \tconst char *p;\n-\tint res;\n\n \tva_start(args, arg);\n-\tstrvec_push(&argv, arg);\n+\tstrvec_push(&cmd.args, arg);\n \twhile ((p = va_arg(args, const char *)))\n-\t\tstrvec_push(&argv, p);\n+\t\tstrvec_push(&cmd.args, p);\n \tva_end(args);\n\n-\tres = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\n-\tstrvec_clear(&argv);\n-\treturn res;\n+\tcmd.git_cmd = 1;\n+\treturn run_command(&cmd);\n }\n\n struct scalar_config {\ndiff --git a/sequencer.c b/sequencer.c\nindex 66eedd2c76..31e24f38f8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3183,18 +3183,15 @@ static int rollback_is_safe(void)\n\n static int reset_merge(const struct object_id *oid)\n {\n-\tint ret;\n-\tstruct strvec argv = STRVEC_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\tstrvec_pushl(&argv, \"reset\", \"--merge\", NULL);\n+\tcmd.git_cmd = 1;\n+\tstrvec_pushl(&cmd.args, \"reset\", \"--merge\", NULL);\n\n \tif (!is_null_oid(oid))\n-\t\tstrvec_push(&argv, oid_to_hex(oid));\n+\t\tstrvec_push(&cmd.args, oid_to_hex(oid));\n\n-\tret = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&argv);\n-\n-\treturn ret;\n+\treturn run_command(&cmd);\n }\n\n static int rollback_single_pick(struct repository *r)\n@@ -4866,14 +4863,14 @@ static int pick_commits(struct repository *r,\n\n static int continue_single_pick(struct repository *r, struct replay_opts *opts)\n {\n-\tstruct strvec argv = STRVEC_INIT;\n-\tint ret;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \tif (!refs_ref_exists(get_main_ref_store(r), \"CHERRY_PICK_HEAD\") &&\n \t    !refs_ref_exists(get_main_ref_store(r), \"REVERT_HEAD\"))\n \t\treturn error(_(\"no cherry-pick or revert in progress\"));\n\n-\tstrvec_push(&argv, \"commit\");\n+\tcmd.git_cmd = 1;\n+\tstrvec_push(&cmd.args, \"commit\");\n\n \t/*\n \t * continue_single_pick() handles the case of recovering from a\n@@ -4886,11 +4883,9 @@ static int continue_single_pick(struct repository *r, struct replay_opts *opts)\n \t\t * Include --cleanup=strip as well because we don't want the\n \t\t * \"# Conflicts:\" messages.\n \t\t */\n-\t\tstrvec_pushl(&argv, \"--no-edit\", \"--cleanup=strip\", NULL);\n+\t\tstrvec_pushl(&cmd.args, \"--no-edit\", \"--cleanup=strip\", NULL);\n\n-\tret = run_command_v_opt(argv.v, RUN_GIT_CMD);\n-\tstrvec_clear(&argv);\n-\treturn ret;\n+\treturn run_command(&cmd);\n }\n\n static int commit_staged_changes(struct repository *r,\n--\n2.38.1\n"},{"id":"466053","messageId":"d5896e6f-df69-d683-ec16-f83a0179a2fb@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 09/12] replace and remove run_command_v_opt_cd_env()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:51:55Z","receivedAt":"2022-10-30T11:52:06Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"run_command_v_opt_cd_env() is only used in an example in a comment.  Use\nthe struct child_process member \"env\" and run_command() directly instead\nand then remove the unused convenience function.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n run-command.c | 7 +------\n run-command.h | 3 +--\n tmp-objdir.h  | 6 ++++--\n 3 files changed, 6 insertions(+), 10 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex c772acd743..954257abb5 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1006,7 +1006,7 @@ int run_command(struct child_process *cmd)\n\n int run_command_v_opt(const char **argv, int opt)\n {\n-\treturn run_command_v_opt_cd_env(argv, opt, NULL, NULL);\n+\treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, NULL);\n }\n\n int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n@@ -1014,11 +1014,6 @@ int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n \treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class);\n }\n\n-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)\n-{\n-\treturn run_command_v_opt_cd_env_tr2(argv, opt, dir, env, NULL);\n-}\n-\n int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \t\t\t\t const char *const *env, const char *tr2_class)\n {\ndiff --git a/run-command.h b/run-command.h\nindex 820fc25b02..05c02485df 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -151,7 +151,7 @@ struct child_process {\n\n /**\n  * The functions: start_command, finish_command, run_command,\n- * run_command_v_opt, run_command_v_opt_cd_env do the following:\n+ * run_command_v_opt do the following:\n  *\n  * - If a system call failed, errno is set and -1 is returned. A diagnostic\n  *   is printed.\n@@ -249,7 +249,6 @@ int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class);\n  * env (the environment) is to be formatted like environ: \"VAR=VALUE\".\n  * To unset an environment variable use just \"VAR\".\n  */\n-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);\n int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \t\t\t\t const char *const *env, const char *tr2_class);\n\ndiff --git a/tmp-objdir.h b/tmp-objdir.h\nindex 76efc7edee..237d96b660 100644\n--- a/tmp-objdir.h\n+++ b/tmp-objdir.h\n@@ -10,9 +10,11 @@\n  *\n  * Example:\n  *\n+ *\tstruct child_process child = CHILD_PROCESS_INIT;\n  *\tstruct tmp_objdir *t = tmp_objdir_create(\"incoming\");\n- *\tif (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&\n- *\t    !tmp_objdir_migrate(t))\n+ *\tstrvec_push(&child.args, cmd);\n+ *\tstrvec_pushv(&child.env, tmp_objdir_env(t));\n+ *\tif (!run_command(&child)) && !tmp_objdir_migrate(t))\n  *\t\tprintf(\"success!\\n\");\n  *\telse\n  *\t\tdie(\"failed...tmp_objdir will clean up for us\");\n--\n2.38.1\n"},{"id":"466054","messageId":"be37291d-d77d-611c-feed-b4a9c932f2d1@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 10/12] replace and remove run_command_v_opt_tr2()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:52:40Z","receivedAt":"2022-10-30T11:52:53Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"The convenience function run_command_v_opt_tr2() is only used by a\nsingle caller.  Use struct child_process and run_command() directly\ninstead and remove the underused function.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n fsmonitor-ipc.c | 10 +++++++---\n run-command.c   |  5 -----\n run-command.h   |  1 -\n 3 files changed, 7 insertions(+), 9 deletions(-)\n\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex c0f42301c8..19d772f0f3 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -54,10 +54,14 @@ enum ipc_active_state fsmonitor_ipc__get_state(void)\n\n static int spawn_daemon(void)\n {\n-\tconst char *args[] = { \"fsmonitor--daemon\", \"start\", NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\treturn run_command_v_opt_tr2(args, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,\n-\t\t\t\t    \"fsmonitor\");\n+\tcmd.git_cmd = 1;\n+\tcmd.no_stdin = 1;\n+\tcmd.trace2_child_class = \"fsmonitor\";\n+\tstrvec_pushl(&cmd.args, \"fsmonitor--daemon\", \"start\", NULL);\n+\n+\treturn run_command(&cmd);\n }\n\n int fsmonitor_ipc__send_query(const char *since_token,\ndiff --git a/run-command.c b/run-command.c\nindex 954257abb5..3a8d578f72 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1009,11 +1009,6 @@ int run_command_v_opt(const char **argv, int opt)\n \treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, NULL);\n }\n\n-int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class)\n-{\n-\treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class);\n-}\n-\n int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \t\t\t\t const char *const *env, const char *tr2_class)\n {\ndiff --git a/run-command.h b/run-command.h\nindex 05c02485df..ac67791c81 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -244,7 +244,6 @@ int run_auto_maintenance(int quiet);\n  * corresponds to the member .env.\n  */\n int run_command_v_opt(const char **argv, int opt);\n-int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class);\n /*\n  * env (the environment) is to be formatted like environ: \"VAR=VALUE\".\n  * To unset an environment variable use just \"VAR\".\n--\n2.38.1\n"},{"id":"466055","messageId":"44d50eb3-210e-d946-625b-6df06898b629@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 11/12] replace and remove run_command_v_opt_cd_env_tr2()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:53:40Z","receivedAt":"2022-10-30T11:53:53Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"The convenience function run_command_v_opt_cd_env_tr2() has no external\ncallers left.  Inline it and remove it from the API.\n\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n run-command.c | 10 ----------\n run-command.h | 10 +---------\n 2 files changed, 1 insertion(+), 19 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex 3a8d578f72..f8f810d755 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1005,12 +1005,6 @@ int run_command(struct child_process *cmd)\n }\n\n int run_command_v_opt(const char **argv, int opt)\n-{\n-\treturn run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, NULL);\n-}\n-\n-int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n-\t\t\t\t const char *const *env, const char *tr2_class)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tstrvec_pushv(&cmd.args, argv);\n@@ -1022,10 +1016,6 @@ int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n \tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n \tcmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;\n \tcmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0;\n-\tcmd.dir = dir;\n-\tif (env)\n-\t\tstrvec_pushv(&cmd.env, (const char **)env);\n-\tcmd.trace2_child_class = tr2_class;\n \treturn run_command(&cmd);\n }\n\ndiff --git a/run-command.h b/run-command.h\nindex ac67791c81..4391696bf0 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -233,23 +233,15 @@ int run_auto_maintenance(int quiet);\n #define RUN_CLOSE_OBJECT_STORE\t\t(1<<7)\n\n /**\n- * Convenience functions that encapsulate a sequence of\n+ * Convenience function that encapsulates a sequence of\n  * start_command() followed by finish_command(). The argument argv\n  * specifies the program and its arguments. The argument opt is zero\n  * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,\n  * `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`\n  * that correspond to the members .no_stdin, .git_cmd,\n  * .stdout_to_stderr, .silent_exec_failure of `struct child_process`.\n- * The argument dir corresponds the member .dir. The argument env\n- * corresponds to the member .env.\n  */\n int run_command_v_opt(const char **argv, int opt);\n-/*\n- * env (the environment) is to be formatted like environ: \"VAR=VALUE\".\n- * To unset an environment variable use just \"VAR\".\n- */\n-int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,\n-\t\t\t\t const char *const *env, const char *tr2_class);\n\n /**\n  * Execute the given command, sending \"in\" to its stdin, and capturing its\n--\n2.38.1\n"},{"id":"466056","messageId":"0c13812c-dcb6-7319-4b41-ccba3f27a5fd@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"[PATCH v2 12/12] replace and remove run_command_v_opt()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:55:06Z","receivedAt":"2022-10-30T11:55:19Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Replace the remaining calls of run_command_v_opt() with run_command()\ncalls and explict struct child_process variables.  This is more verbose,\nbut not by much overall.  The code becomes more flexible, e.g. it's easy\nto extend to conditionally add a new argument.\n\nThen remove the now unused function and its own flag names, simplifying\nthe run-command API.\n\nSuggested-by: Jeff King <peff@peff.net>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n bisect.c                 |  9 +++++----\n builtin/am.c             |  9 +++++----\n builtin/bisect--helper.c | 15 ++++++++++-----\n builtin/clone.c          |  8 ++++++--\n builtin/difftool.c       |  7 +++++--\n builtin/fetch.c          |  9 ++++++---\n builtin/gc.c             | 41 +++++++++++++++++++++++++++++++---------\n builtin/merge-index.c    |  4 +++-\n run-command.c            | 15 ---------------\n run-command.h            | 23 +---------------------\n sequencer.c              |  6 ++++--\n shell.c                  | 17 ++++++++++++-----\n t/helper/test-trace2.c   |  4 +++-\n 13 files changed, 92 insertions(+), 75 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 090aa5c4b4..ec7487e683 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -737,11 +737,12 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,\n \t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0,\n \t\t\t   UPDATE_REFS_DIE_ON_ERR);\n \t} else {\n-\t\tconst char *argv_checkout[] = {\n-\t\t\t\"checkout\", \"-q\", oid_to_hex(bisect_rev), \"--\", NULL\n-\t\t};\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\tif (run_command_v_opt(argv_checkout, RUN_GIT_CMD))\n+\t\tcmd.git_cmd = 1;\n+\t\tstrvec_pushl(&cmd.args, \"checkout\", \"-q\",\n+\t\t\t     oid_to_hex(bisect_rev), \"--\", NULL);\n+\t\tif (run_command(&cmd))\n \t\t\t/*\n \t\t\t * Errors in `run_command()` itself, signaled by res < 0,\n \t\t\t * and errors in the child process, signaled by res > 0\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 5781e7a95e..20aea0d248 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -2187,11 +2187,12 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)\n \tint len;\n\n \tif (!is_null_oid(&state->orig_commit)) {\n-\t\tconst char *av[] = {\n-\t\t\t\"show\", oid_to_hex(&state->orig_commit), \"--\", NULL\n-\t\t};\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\treturn run_command_v_opt(av, RUN_GIT_CMD);\n+\t\tstrvec_pushl(&cmd.args, \"show\", oid_to_hex(&state->orig_commit),\n+\t\t\t     \"--\", NULL);\n+\t\tcmd.git_cmd = 1;\n+\t\treturn run_command(&cmd);\n \t}\n\n \tswitch (sub_mode) {\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 5c63ba6994..1d2ce8a0e1 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -764,10 +764,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a\n \t\tstrbuf_read_file(&start_head, git_path_bisect_start(), 0);\n \t\tstrbuf_trim(&start_head);\n \t\tif (!no_checkout) {\n-\t\t\tconst char *argv[] = { \"checkout\", start_head.buf,\n-\t\t\t\t\t       \"--\", NULL };\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\t\t\tif (run_command_v_opt(argv, RUN_GIT_CMD)) {\n+\t\t\tcmd.git_cmd = 1;\n+\t\t\tstrvec_pushl(&cmd.args, \"checkout\", start_head.buf,\n+\t\t\t\t     \"--\", NULL);\n+\t\t\tif (run_command(&cmd)) {\n \t\t\t\tres = error(_(\"checking out '%s' failed.\"\n \t\t\t\t\t\t \" Try 'git bisect start \"\n \t\t\t\t\t\t \"<valid-branch>'.\"),\n@@ -1141,9 +1143,12 @@ static int get_first_good(const char *refname UNUSED,\n\n static int do_bisect_run(const char *command)\n {\n-\tconst char *argv[] = { command, NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n \tprintf(_(\"running %s\\n\"), command);\n-\treturn run_command_v_opt(argv, RUN_USING_SHELL);\n+\tcmd.use_shell = 1;\n+\tstrvec_push(&cmd.args, command);\n+\treturn run_command(&cmd);\n }\n\n static int verify_good(const struct bisect_terms *terms, const char *command)\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 56e7775dae..0e4348686b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -865,11 +865,15 @@ static void write_refspec_config(const char *src_ref_prefix,\n\n static void dissociate_from_references(void)\n {\n-\tstatic const char* argv[] = { \"repack\", \"-a\", \"-d\", NULL };\n \tchar *alternates = git_pathdup(\"objects/info/alternates\");\n\n \tif (!access(alternates, F_OK)) {\n-\t\tif (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\tcmd.git_cmd = 1;\n+\t\tcmd.no_stdin = 1;\n+\t\tstrvec_pushl(&cmd.args, \"repack\", \"-a\", \"-d\", NULL);\n+\t\tif (run_command(&cmd))\n \t\t\tdie(_(\"cannot repack to clean up\"));\n \t\tif (unlink(alternates) && errno != ENOENT)\n \t\t\tdie_errno(_(\"cannot unlink temporary alternates file\"));\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 22bcc3444b..d7f08c8a7f 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -44,8 +44,11 @@ static int difftool_config(const char *var, const char *value, void *cb)\n\n static int print_tool_help(void)\n {\n-\tconst char *argv[] = { \"mergetool\", \"--tool-help=diff\", NULL };\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tcmd.git_cmd = 1;\n+\tstrvec_pushl(&cmd.args, \"mergetool\", \"--tool-help=diff\", NULL);\n+\treturn run_command(&cmd);\n }\n\n static int parse_index_info(char *p, int *mode1, int *mode2,\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b06e454cbd..7378cafeec 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1972,14 +1972,17 @@ static int fetch_multiple(struct string_list *list, int max_children)\n \t} else\n \t\tfor (i = 0; i < list->nr; i++) {\n \t\t\tconst char *name = list->items[i].string;\n-\t\t\tstrvec_push(&argv, name);\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\t\tstrvec_pushv(&cmd.args, argv.v);\n+\t\t\tstrvec_push(&cmd.args, name);\n \t\t\tif (verbosity >= 0)\n \t\t\t\tprintf(_(\"Fetching %s\\n\"), name);\n-\t\t\tif (run_command_v_opt(argv.v, RUN_GIT_CMD)) {\n+\t\t\tcmd.git_cmd = 1;\n+\t\t\tif (run_command(&cmd)) {\n \t\t\t\terror(_(\"could not fetch %s\"), name);\n \t\t\t\tresult = 1;\n \t\t\t}\n-\t\t\tstrvec_pop(&argv);\n \t\t}\n\n \tstrvec_clear(&argv);\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex a185bec813..6504c0b729 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -167,9 +167,11 @@ static void gc_config(void)\n struct maintenance_run_opts;\n static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)\n {\n-\tconst char *argv[] = { \"pack-refs\", \"--all\", \"--prune\", NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+\tcmd.git_cmd = 1;\n+\tstrvec_pushl(&cmd.args, \"pack-refs\", \"--all\", \"--prune\", NULL);\n+\treturn run_command(&cmd);\n }\n\n static int too_many_loose_objects(void)\n@@ -535,8 +537,14 @@ static void gc_before_repack(void)\n \tif (pack_refs && maintenance_task_pack_refs(NULL))\n \t\tdie(FAILED_RUN, \"pack-refs\");\n\n-\tif (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD))\n-\t\tdie(FAILED_RUN, reflog.v[0]);\n+\tif (prune_reflogs) {\n+\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\t\tcmd.git_cmd = 1;\n+\t\tstrvec_pushv(&cmd.args, reflog.v);\n+\t\tif (run_command(&cmd))\n+\t\t\tdie(FAILED_RUN, reflog.v[0]);\n+\t}\n }\n\n int cmd_gc(int argc, const char **argv, const char *prefix)\n@@ -550,6 +558,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \tint daemonized = 0;\n \tint keep_largest_pack = -1;\n \ttimestamp_t dummy;\n+\tstruct child_process rerere_cmd = CHILD_PROCESS_INIT;\n\n \tstruct option builtin_gc_options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"suppress progress reporting\")),\n@@ -671,11 +680,17 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \tgc_before_repack();\n\n \tif (!repository_format_precious_objects) {\n-\t\tif (run_command_v_opt(repack.v,\n-\t\t\t\t      RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE))\n+\t\tstruct child_process repack_cmd = CHILD_PROCESS_INIT;\n+\n+\t\trepack_cmd.git_cmd = 1;\n+\t\trepack_cmd.close_object_store = 1;\n+\t\tstrvec_pushv(&repack_cmd.args, repack.v);\n+\t\tif (run_command(&repack_cmd))\n \t\t\tdie(FAILED_RUN, repack.v[0]);\n\n \t\tif (prune_expire) {\n+\t\t\tstruct child_process prune_cmd = CHILD_PROCESS_INIT;\n+\n \t\t\t/* run `git prune` even if using cruft packs */\n \t\t\tstrvec_push(&prune, prune_expire);\n \t\t\tif (quiet)\n@@ -683,18 +698,26 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \t\t\tif (has_promisor_remote())\n \t\t\t\tstrvec_push(&prune,\n \t\t\t\t\t    \"--exclude-promisor-objects\");\n-\t\t\tif (run_command_v_opt(prune.v, RUN_GIT_CMD))\n+\t\t\tprune_cmd.git_cmd = 1;\n+\t\t\tstrvec_pushv(&prune_cmd.args, prune.v);\n+\t\t\tif (run_command(&prune_cmd))\n \t\t\t\tdie(FAILED_RUN, prune.v[0]);\n \t\t}\n \t}\n\n \tif (prune_worktrees_expire) {\n+\t\tstruct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;\n+\n \t\tstrvec_push(&prune_worktrees, prune_worktrees_expire);\n-\t\tif (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD))\n+\t\tprune_worktrees_cmd.git_cmd = 1;\n+\t\tstrvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v);\n+\t\tif (run_command(&prune_worktrees_cmd))\n \t\t\tdie(FAILED_RUN, prune_worktrees.v[0]);\n \t}\n\n-\tif (run_command_v_opt(rerere.v, RUN_GIT_CMD))\n+\trerere_cmd.git_cmd = 1;\n+\tstrvec_pushv(&rerere_cmd.args, rerere.v);\n+\tif (run_command(&rerere_cmd))\n \t\tdie(FAILED_RUN, rerere.v[0]);\n\n \treport_garbage = report_pack_garbage;\ndiff --git a/builtin/merge-index.c b/builtin/merge-index.c\nindex c0383fe9df..012f52bd00 100644\n--- a/builtin/merge-index.c\n+++ b/builtin/merge-index.c\n@@ -12,6 +12,7 @@ static int merge_entry(int pos, const char *path)\n \tconst char *arguments[] = { pgm, \"\", \"\", \"\", path, \"\", \"\", \"\", NULL };\n \tchar hexbuf[4][GIT_MAX_HEXSZ + 1];\n \tchar ownbuf[4][60];\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n\n \tif (pos >= active_nr)\n \t\tdie(\"git merge-index: %s not in the cache\", path);\n@@ -31,7 +32,8 @@ static int merge_entry(int pos, const char *path)\n \tif (!found)\n \t\tdie(\"git merge-index: %s not in the cache\", path);\n\n-\tif (run_command_v_opt(arguments, 0)) {\n+\tstrvec_pushv(&cmd.args, arguments);\n+\tif (run_command(&cmd)) {\n \t\tif (one_shot)\n \t\t\terr++;\n \t\telse {\ndiff --git a/run-command.c b/run-command.c\nindex f8f810d755..48b9ba6d6f 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1004,21 +1004,6 @@ int run_command(struct child_process *cmd)\n \treturn finish_command(cmd);\n }\n\n-int run_command_v_opt(const char **argv, int opt)\n-{\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tstrvec_pushv(&cmd.args, argv);\n-\tcmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;\n-\tcmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;\n-\tcmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;\n-\tcmd.silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;\n-\tcmd.use_shell = opt & RUN_USING_SHELL ? 1 : 0;\n-\tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n-\tcmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;\n-\tcmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0;\n-\treturn run_command(&cmd);\n-}\n-\n #ifndef NO_PTHREADS\n static pthread_t main_thread;\n static int main_thread_set;\ndiff --git a/run-command.h b/run-command.h\nindex 4391696bf0..072db56a4d 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -150,8 +150,7 @@ struct child_process {\n }\n\n /**\n- * The functions: start_command, finish_command, run_command,\n- * run_command_v_opt do the following:\n+ * The functions: start_command, finish_command, run_command do the following:\n  *\n  * - If a system call failed, errno is set and -1 is returned. A diagnostic\n  *   is printed.\n@@ -223,26 +222,6 @@ int run_command(struct child_process *);\n  */\n int run_auto_maintenance(int quiet);\n\n-#define RUN_COMMAND_NO_STDIN\t\t(1<<0)\n-#define RUN_GIT_CMD\t\t\t(1<<1)\n-#define RUN_COMMAND_STDOUT_TO_STDERR\t(1<<2)\n-#define RUN_SILENT_EXEC_FAILURE\t\t(1<<3)\n-#define RUN_USING_SHELL\t\t\t(1<<4)\n-#define RUN_CLEAN_ON_EXIT\t\t(1<<5)\n-#define RUN_WAIT_AFTER_CLEAN\t\t(1<<6)\n-#define RUN_CLOSE_OBJECT_STORE\t\t(1<<7)\n-\n-/**\n- * Convenience function that encapsulates a sequence of\n- * start_command() followed by finish_command(). The argument argv\n- * specifies the program and its arguments. The argument opt is zero\n- * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,\n- * `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`\n- * that correspond to the members .no_stdin, .git_cmd,\n- * .stdout_to_stderr, .silent_exec_failure of `struct child_process`.\n- */\n-int run_command_v_opt(const char **argv, int opt);\n-\n /**\n  * Execute the given command, sending \"in\" to its stdin, and capturing its\n  * stdout and stderr in the \"out\" and \"err\" strbufs. Any of the three may\ndiff --git a/sequencer.c b/sequencer.c\nindex 31e24f38f8..643744fb9b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3555,11 +3555,13 @@ static int error_failed_squash(struct repository *r,\n\n static int do_exec(struct repository *r, const char *command_line)\n {\n-\tconst char *child_argv[] = { command_line, NULL };\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint dirty, status;\n\n \tfprintf(stderr, _(\"Executing: %s\\n\"), command_line);\n-\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n+\tcmd.use_shell = 1;\n+\tstrvec_push(&cmd.args, command_line);\n+\tstatus = run_command(&cmd);\n\n \t/* force re-reading of the cache */\n \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0)\ndiff --git a/shell.c b/shell.c\nindex 7ff4109db7..af0d7c734f 100644\n--- a/shell.c\n+++ b/shell.c\n@@ -52,21 +52,24 @@ static void cd_to_homedir(void)\n static void run_shell(void)\n {\n \tint done = 0;\n-\tstatic const char *help_argv[] = { HELP_COMMAND, NULL };\n+\tstruct child_process help_cmd = CHILD_PROCESS_INIT;\n\n \tif (!access(NOLOGIN_COMMAND, F_OK)) {\n \t\t/* Interactive login disabled. */\n-\t\tconst char *argv[] = { NOLOGIN_COMMAND, NULL };\n+\t\tstruct child_process nologin_cmd = CHILD_PROCESS_INIT;\n \t\tint status;\n\n-\t\tstatus = run_command_v_opt(argv, 0);\n+\t\tstrvec_push(&nologin_cmd.args, NOLOGIN_COMMAND);\n+\t\tstatus = run_command(&nologin_cmd);\n \t\tif (status < 0)\n \t\t\texit(127);\n \t\texit(status);\n \t}\n\n \t/* Print help if enabled */\n-\trun_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);\n+\thelp_cmd.silent_exec_failure = 1;\n+\tstrvec_push(&help_cmd.args, HELP_COMMAND);\n+\trun_command(&help_cmd);\n\n \tdo {\n \t\tconst char *prog;\n@@ -125,9 +128,13 @@ static void run_shell(void)\n \t\t\t   !strcmp(prog, \"exit\") || !strcmp(prog, \"bye\")) {\n \t\t\tdone = 1;\n \t\t} else if (is_valid_cmd_name(prog)) {\n+\t\t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n \t\t\tfull_cmd = make_cmd(prog);\n \t\t\targv[0] = full_cmd;\n-\t\t\tcode = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);\n+\t\t\tcmd.silent_exec_failure = 1;\n+\t\t\tstrvec_pushv(&cmd.args, argv);\n+\t\t\tcode = run_command(&cmd);\n \t\t\tif (code == -1 && errno == ENOENT) {\n \t\t\t\tfprintf(stderr, \"unrecognized command '%s'\\n\", prog);\n \t\t\t}\ndiff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c\nindex a714130ece..94fd8ccf51 100644\n--- a/t/helper/test-trace2.c\n+++ b/t/helper/test-trace2.c\n@@ -132,6 +132,7 @@ static int ut_003error(int argc, const char **argv)\n  */\n static int ut_004child(int argc, const char **argv)\n {\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint result;\n\n \t/*\n@@ -141,7 +142,8 @@ static int ut_004child(int argc, const char **argv)\n \tif (!argc)\n \t\treturn 0;\n\n-\tresult = run_command_v_opt(argv, 0);\n+\tstrvec_pushv(&cmd.args, argv);\n+\tresult = run_command(&cmd);\n \texit(result);\n }\n\n--\n2.38.1\n"},{"id":"466057","messageId":"c14503ea-0b76-464a-02d5-0b001a462083@web.de","threadId":"58704","inReplyTo":"ea061164-b36b-485c-963f-8c13e813a47e@web.de","subject":"Re: [PATCH v2 0/12] run-command: remove run_command_v_*()","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2022-10-30T11:58:13Z","receivedAt":"2022-10-30T11:58:25Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 30.10.22 um 12:40 schrieb René Scharfe:\n> Replace the convenience functions run_command_v_opt() et. al. and use\n> struct child_process and run_command() directly instead, for an overall\n> code reduction and a simpler and more flexible API that allows creating\n> argument lists without magic numbers and reduced risk of memory leaks.\n>\n> Changes since v1:\n> - Do the return value fix earlier; it was only an afterthought before.\n>   Keep the colon (no \"while at it, ...\").\n> - Break out the xstrdup(), oid_to_hex_r() and C99 cleanups.\n> - Convert tricky string arrays before strvecs because Ævar didn't like\n>   the opposite order.\n> - Extend the example code in tmp-objdir.h so it still only requires\n>   \"cmd\".\n\nForgot one:\n- Fix grammar error in run-command.h added by the series in a comment\n  that goes away at the end.\n\nRené\n"},{"id":"466061","messageId":"Y1685tnqts37KHnm@nand.local","threadId":"58704","inReplyTo":"c14503ea-0b76-464a-02d5-0b001a462083@web.de","subject":"Re: [PATCH v2 0/12] run-command: remove run_command_v_*()","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-10-30T18:05:26Z","receivedAt":"2022-10-30T18:05:31Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Sun, Oct 30, 2022 at 12:58:13PM +0100, René Scharfe wrote:\n> > Changes since v1:\n> > - Do the return value fix earlier; it was only an afterthought before.\n> >   Keep the colon (no \"while at it, ...\").\n> > - Break out the xstrdup(), oid_to_hex_r() and C99 cleanups.\n> > - Convert tricky string arrays before strvecs because Ævar didn't like\n> >   the opposite order.\n> > - Extend the example code in tmp-objdir.h so it still only requires\n> >   \"cmd\".\n>\n> Forgot one:\n> - Fix grammar error in run-command.h added by the series in a comment\n>   that goes away at the end.\n>\n> René\n\nThanks, replaced. This round is looking good to me, though let's hear\nfrom ÆVar before we start merging this down.\n\nThanks,\nTaylor\n"},{"id":"466097","messageId":"221031.86leow6mew.gmgdl@evledraar.gmail.com","threadId":"58704","inReplyTo":"Y1685tnqts37KHnm@nand.local","subject":"Re: [PATCH v2 0/12] run-command: remove run_command_v_*()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-31T13:35:01Z","receivedAt":"2022-10-31T15:30:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Oct 30 2022, Taylor Blau wrote:\n\n> On Sun, Oct 30, 2022 at 12:58:13PM +0100, René Scharfe wrote:\n>> > Changes since v1:\n>> > - Do the return value fix earlier; it was only an afterthought before.\n>> >   Keep the colon (no \"while at it, ...\").\n>> > - Break out the xstrdup(), oid_to_hex_r() and C99 cleanups.\n>> > - Convert tricky string arrays before strvecs because Ævar didn't like\n>> >   the opposite order.\n>> > - Extend the example code in tmp-objdir.h so it still only requires\n>> >   \"cmd\".\n>>\n>> Forgot one:\n>> - Fix grammar error in run-command.h added by the series in a comment\n>>   that goes away at the end.\n>>\n>> René\n>\n> Thanks, replaced. This round is looking good to me, though let's hear\n> from ÆVar before we start merging this down.\n\nThis LGTM. I don't think any of these are worth a re-roll, but just to\nclarify in reply to the comment in the v2 CL:\n\n * I thought the C99-specific stuff, free() etc. might be worth\n   splitting out[1], but not to the extent of churning through first\n   moving the existing API use around, and then converting it to\n   run_command().\n\n   I.e. v1 had e.g. the am.c change as part of the large 3/8. Here\n   there's 3/12, and we then re-visit that same caller in 12/12.\n\n   I think that part of the v2 was better in v1. I.e. maybe split them\n   out, but to the extent that it needs special explanation we could\n   have just done it in one go with some explanation..\n\n * I suggested just squashing what's now the v2's 08-11/12 into the\n   respective commits that represented the last API user, or if they're\n   worth splitting out do split-out with that last API user converted\n   (and also removing the run-commit.h flags that went unused then, if\n   applicable).\n\nAnyway, LGTM, and the stuff that was odd in the end state (e.g. 02/12)\nis now fixed. Thanks both!\n\nhttps://lore.kernel.org/git/Y11%2F2hyApN5NLruq@nand.local/T/#md2b7af02b1af34fa118779d3e1f4444604f95cb9\n"}]}