Merge branch 'ab/run-command'

API clean-up.

* ab/run-command:
  run-command API: remove "env" member, always use "env_array"
  difftool: use "env_array" to simplify memory management
  run-command API: remove "argv" member, always use "args"
  run-command API users: use strvec_push(), not argv construction
  run-command API users: use strvec_pushl(), not argv construction
  run-command tests: use strvec_pushv(), not argv assignment
  run-command API users: use strvec_pushv(), not argv assignment
  upload-archive: use regular "struct child_process" pattern
  worktree: stop being overly intimate with run_command() internals
pull/948/merge
Junio C Hamano 10 months ago
commit 832ec72c3e
  1. 4
      add-patch.c
  2. 9
      archive-tar.c
  3. 7
      builtin/add.c
  4. 14
      builtin/difftool.c
  5. 12
      builtin/fsck.c
  6. 3
      builtin/help.c
  7. 3
      builtin/merge.c
  8. 5
      builtin/notes.c
  9. 80
      builtin/receive-pack.c
  10. 3
      builtin/replace.c
  11. 5
      builtin/upload-archive.c
  12. 13
      builtin/worktree.c
  13. 3
      connected.c
  14. 20
      daemon.c
  15. 8
      diff.c
  16. 8
      editor.c
  17. 2
      http-backend.c
  18. 5
      http.c
  19. 2
      object-file.c
  20. 7
      prompt.c
  21. 2
      remote-curl.c
  22. 62
      run-command.c
  23. 54
      run-command.h
  24. 10
      sequencer.c
  25. 2
      sub-process.c
  26. 9
      t/helper/test-run-command.c
  27. 2
      t/helper/test-subprocess.c
  28. 2
      trace2/tr2_tgt_event.c
  29. 2
      trace2/tr2_tgt_normal.c
  30. 4
      trace2/tr2_tgt_perf.c
  31. 2
      trailer.c
  32. 11
      transport.c
  33. 5
      upload-pack.c

@ -413,7 +413,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
strvec_push(&args, ps->items[i].original);
setup_child_process(s, &cp, NULL);
cp.argv = args.v;
strvec_pushv(&cp.args, args.v);
res = capture_command(&cp, plain, 0);
if (res) {
strvec_clear(&args);
@ -431,7 +431,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
setup_child_process(s, &colored_cp, NULL);
xsnprintf((char *)args.v[color_arg_index], 8, "--color");
colored_cp.argv = args.v;
strvec_pushv(&colored_cp.args, args.v);
colored = &s->colored;
res = capture_command(&colored_cp, colored, 0);
strvec_clear(&args);

@ -430,7 +430,6 @@ static int write_tar_filter_archive(const struct archiver *ar,
{
struct strbuf cmd = STRBUF_INIT;
struct child_process filter = CHILD_PROCESS_INIT;
const char *argv[2];
int r;
if (!ar->data)
@ -440,14 +439,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
if (args->compression_level >= 0)
strbuf_addf(&cmd, " -%d", args->compression_level);
argv[0] = cmd.buf;
argv[1] = NULL;
filter.argv = argv;
strvec_push(&filter.args, cmd.buf);
filter.use_shell = 1;
filter.in = -1;
if (start_command(&filter) < 0)
die_errno(_("unable to start '%s' filter"), argv[0]);
die_errno(_("unable to start '%s' filter"), cmd.buf);
close(1);
if (dup2(filter.in, 1) < 0)
die_errno(_("unable to redirect descriptor"));
@ -457,7 +454,7 @@ static int write_tar_filter_archive(const struct archiver *ar,
close(1);
if (finish_command(&filter) != 0)
die(_("'%s' filter reported error"), argv[0]);
die(_("'%s' filter reported error"), cmd.buf);
strbuf_release(&cmd);
return r;

@ -302,15 +302,11 @@ int interactive_add(const char **argv, const char *prefix, int patch)
static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = git_pathdup("ADD_EDIT.patch");
const char *apply_argv[] = { "apply", "--recount", "--cached",
NULL, NULL };
struct child_process child = CHILD_PROCESS_INIT;
struct rev_info rev;
int out;
struct stat st;
apply_argv[3] = file;
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
if (read_cache() < 0)
@ -338,7 +334,8 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
die(_("Empty patch. Aborted."));
child.git_cmd = 1;
child.argv = apply_argv;
strvec_pushl(&child.args, "apply", "--recount", "--cached", file,
NULL);
if (run_command(&child))
die(_("Could not apply '%s'"), file);

@ -202,15 +202,10 @@ static void changed_files(struct hashmap *result, const char *index_path,
{
struct child_process update_index = CHILD_PROCESS_INIT;
struct child_process diff_files = CHILD_PROCESS_INIT;
struct strbuf index_env = STRBUF_INIT, buf = STRBUF_INIT;
const char *git_dir = absolute_path(get_git_dir()), *env[] = {
NULL, NULL
};
struct strbuf buf = STRBUF_INIT;
const char *git_dir = absolute_path(get_git_dir());
FILE *fp;
strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path);
env[0] = index_env.buf;
strvec_pushl(&update_index.args,
"--git-dir", git_dir, "--work-tree", workdir,
"update-index", "--really-refresh", "-q",
@ -222,7 +217,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
update_index.use_shell = 0;
update_index.clean_on_exit = 1;
update_index.dir = workdir;
update_index.env = env;
strvec_pushf(&update_index.env_array, "GIT_INDEX_FILE=%s", index_path);
/* Ignore any errors of update-index */
run_command(&update_index);
@ -235,7 +230,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
diff_files.clean_on_exit = 1;
diff_files.out = -1;
diff_files.dir = workdir;
diff_files.env = env;
strvec_pushf(&diff_files.env_array, "GIT_INDEX_FILE=%s", index_path);
if (start_command(&diff_files))
die("could not obtain raw diff");
fp = xfdopen(diff_files.out, "r");
@ -248,7 +243,6 @@ static void changed_files(struct hashmap *result, const char *index_path,
fclose(fp);
if (finish_command(&diff_files))
die("diff-files did not exit properly");
strbuf_release(&index_env);
strbuf_release(&buf);
}

@ -944,15 +944,13 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
if (the_repository->settings.core_commit_graph) {
struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
prepare_alt_odb(the_repository);
for (odb = the_repository->objects->odb; odb; odb = odb->next) {
child_process_init(&commit_graph_verify);
commit_graph_verify.argv = verify_argv;
commit_graph_verify.git_cmd = 1;
verify_argv[2] = "--object-dir";
verify_argv[3] = odb->path;
strvec_pushl(&commit_graph_verify.args, "commit-graph",
"verify", "--object-dir", odb->path, NULL);
if (run_command(&commit_graph_verify))
errors_found |= ERROR_COMMIT_GRAPH;
}
@ -960,15 +958,13 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
if (the_repository->settings.core_multi_pack_index) {
struct child_process midx_verify = CHILD_PROCESS_INIT;
const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
prepare_alt_odb(the_repository);
for (odb = the_repository->objects->odb; odb; odb = odb->next) {
child_process_init(&midx_verify);
midx_verify.argv = midx_argv;
midx_verify.git_cmd = 1;
midx_argv[2] = "--object-dir";
midx_argv[3] = odb->path;
strvec_pushl(&midx_verify.args, "multi-pack-index",
"verify", "--object-dir", odb->path, NULL);
if (run_command(&midx_verify))
errors_found |= ERROR_MULTI_PACK_INDEX;
}

@ -212,11 +212,10 @@ static int check_emacsclient_version(void)
{
struct strbuf buffer = STRBUF_INIT;
struct child_process ec_process = CHILD_PROCESS_INIT;
const char *argv_ec[] = { "emacsclient", "--version", NULL };
int version;
/* emacsclient prints its version number on stderr */
ec_process.argv = argv_ec;
strvec_pushl(&ec_process.args, "emacsclient", "--version", NULL);
ec_process.err = -1;
ec_process.stdout_to_stderr = 1;
if (start_command(&ec_process))

@ -310,10 +310,9 @@ static int save_state(struct object_id *stash)
int len;
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buffer = STRBUF_INIT;
const char *argv[] = {"stash", "create", NULL};
int rc = -1;
cp.argv = argv;
strvec_pushl(&cp.args, "stash", "create", NULL);
cp.out = -1;
cp.git_cmd = 1;

@ -134,14 +134,13 @@ static void copy_obj_to_fd(int fd, const struct object_id *oid)
static void write_commented_object(int fd, const struct object_id *object)
{
const char *show_args[5] =
{"show", "--stat", "--no-notes", oid_to_hex(object), NULL};
struct child_process show = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
struct strbuf cbuf = STRBUF_INIT;
/* Invoke "git show --stat --no-notes $object" */
show.argv = show_args;
strvec_pushl(&show.args, "show", "--stat", "--no-notes",
oid_to_hex(object), NULL);
show.no_stdin = 1;
show.out = -1;
show.err = 0;

@ -812,16 +812,13 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
{
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
const char *argv[2];
int code;
const char *hook_path = find_hook(hook_name);
argv[0] = find_hook(hook_name);
if (!argv[0])
if (!hook_path)
return 0;
argv[1] = NULL;
proc.argv = argv;
strvec_push(&proc.args, hook_path);
proc.in = -1;
proc.stdout_to_stderr = 1;
proc.trace2_hook_name = hook_name;
@ -943,23 +940,21 @@ static int run_receive_hook(struct command *commands,
static int run_update_hook(struct command *cmd)
{
const char *argv[5];
struct child_process proc = CHILD_PROCESS_INIT;
int code;
const char *hook_path = find_hook("update");
argv[0] = find_hook("update");
if (!argv[0])
if (!hook_path)
return 0;
argv[1] = cmd->ref_name;
argv[2] = oid_to_hex(&cmd->old_oid);
argv[3] = oid_to_hex(&cmd->new_oid);
argv[4] = NULL;
strvec_push(&proc.args, hook_path);
strvec_push(&proc.args, cmd->ref_name);
strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
proc.argv = argv;
proc.trace2_hook_name = "update";
code = start_command(&proc);
@ -1117,22 +1112,20 @@ static int run_proc_receive_hook(struct command *commands,
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
struct command *cmd;
const char *argv[2];
struct packet_reader reader;
struct strbuf cap = STRBUF_INIT;
struct strbuf errmsg = STRBUF_INIT;
int hook_use_push_options = 0;
int version = 0;
int code;
const char *hook_path = find_hook("proc-receive");
argv[0] = find_hook("proc-receive");
if (!argv[0]) {
if (!hook_path) {
rp_error("cannot find hook 'proc-receive'");
return -1;
}
argv[1] = NULL;
proc.argv = argv;
strvec_push(&proc.args, hook_path);
proc.in = -1;
proc.out = -1;
proc.trace2_hook_name = "proc-receive";
@ -1370,23 +1363,11 @@ static const char *push_to_deploy(unsigned char *sha1,
struct strvec *env,
const char *work_tree)
{
const char *update_refresh[] = {
"update-index", "-q", "--ignore-submodules", "--refresh", NULL
};
const char *diff_files[] = {
"diff-files", "--quiet", "--ignore-submodules", "--", NULL
};
const char *diff_index[] = {
"diff-index", "--quiet", "--cached", "--ignore-submodules",
NULL, "--", NULL
};
const char *read_tree[] = {
"read-tree", "-u", "-m", NULL, NULL
};
struct child_process child = CHILD_PROCESS_INIT;
child.argv = update_refresh;
child.env = env->v;
strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
"--refresh", NULL);
strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@ -1396,8 +1377,9 @@ static const char *push_to_deploy(unsigned char *sha1,
/* run_command() does not clean up completely; reinitialize */
child_process_init(&child);
child.argv = diff_files;
child.env = env->v;
strvec_pushl(&child.args, "diff-files", "--quiet",
"--ignore-submodules", "--", NULL);
strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@ -1405,12 +1387,13 @@ static const char *push_to_deploy(unsigned char *sha1,
if (run_command(&child))
return "Working directory has unstaged changes";
/* diff-index with either HEAD or an empty tree */
diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex();
child_process_init(&child);
child.argv = diff_index;
child.env = env->v;
strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
"--ignore-submodules",
/* diff-index with either HEAD or an empty tree */
head_has_history() ? "HEAD" : empty_tree_oid_hex(),
"--", NULL);
strvec_pushv(&child.env_array, env->v);
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
@ -1418,10 +1401,10 @@ static const char *push_to_deploy(unsigned char *sha1,
if (run_command(&child))
return "Working directory has staged changes";
read_tree[3] = hash_to_hex(sha1);
child_process_init(&child);
child.argv = read_tree;
child.env = env->v;
strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
NULL);
strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.no_stdout = 1;
@ -2219,7 +2202,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)
close(err_fd);
return "unable to create temporary object directory";
}
child.env = tmp_objdir_env(tmp_objdir);
if (tmp_objdir)
strvec_pushv(&child.env_array, tmp_objdir_env(tmp_objdir));
/*
* Normally we just pass the tmp_objdir environment to the child
@ -2575,16 +2559,14 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
run_update_post_hook(commands);
string_list_clear(&push_options, 0);
if (auto_gc) {
const char *argv_gc_auto[] = {
"gc", "--auto", "--quiet", NULL,
};
struct child_process proc = CHILD_PROCESS_INIT;
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
proc.git_cmd = proc.close_object_store = 1;
proc.argv = argv_gc_auto;
strvec_pushl(&proc.args, "gc", "--auto", "--quiet",
NULL);
if (!start_command(&proc)) {
if (use_sideband)

@ -258,11 +258,10 @@ static int import_object(struct object_id *oid, enum object_type type,
return error_errno(_("unable to open %s for reading"), filename);
if (!raw && type == OBJ_TREE) {
const char *argv[] = { "mktree", NULL };
struct child_process cmd = CHILD_PROCESS_INIT;
struct strbuf result = STRBUF_INIT;
cmd.argv = argv;
strvec_push(&cmd.args, "mktree");
cmd.git_cmd = 1;
cmd.in = fd;
cmd.out = -1;

@ -77,7 +77,7 @@ static ssize_t process_input(int child_fd, int band)
int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
struct child_process writer = { argv };
struct child_process writer = CHILD_PROCESS_INIT;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
@ -89,9 +89,10 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
* multiplexed out to our fd#1. If the child dies, we tell the other
* end over channel #3.
*/
argv[0] = "upload-archive--writer";
writer.out = writer.err = -1;
writer.git_cmd = 1;
strvec_push(&writer.args, "upload-archive--writer");
strvec_pushv(&writer.args, argv + 1);
if (start_command(&writer)) {
int err = errno;
packet_write_fmt(1, "NACK unable to spawn subprocess\n");

@ -349,18 +349,18 @@ static int add_worktree(const char *path, const char *refname,
strvec_push(&cp.args, "--quiet");
}
cp.env = child_env.v;
strvec_pushv(&cp.env_array, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
if (opts->checkout) {
cp.argv = NULL;
strvec_clear(&cp.args);
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
cp.env = child_env.v;
strvec_pushv(&cp.env_array, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
@ -385,12 +385,11 @@ done:
const char *hook = find_hook("post-checkout");
if (hook) {
const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
cp.git_cmd = 0;
struct child_process cp = CHILD_PROCESS_INIT;
cp.no_stdin = 1;
cp.stdout_to_stderr = 1;
cp.dir = path;
cp.env = env;
cp.argv = NULL;
strvec_pushv(&cp.env_array, env);
cp.trace2_hook_name = "post-checkout";
strvec_pushl(&cp.args, absolute_path(hook),
oid_to_hex(null_oid()),

@ -109,7 +109,8 @@ no_promisor_pack_found:
_("Checking connectivity"));
rev_list.git_cmd = 1;
rev_list.env = opt->env;
if (opt->env)
strvec_pushv(&rev_list.env_array, opt->env);
rev_list.in = -1;
rev_list.no_stdout = 1;
if (opt->err_fd)

@ -326,22 +326,18 @@ static int run_access_hook(struct daemon_service *service, const char *dir,
{
struct child_process child = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
const char *argv[8];
const char **arg = argv;
char *eol;
int seen_errors = 0;
*arg++ = access_hook;
*arg++ = service->name;
*arg++ = path;
*arg++ = hi->hostname.buf;
*arg++ = get_canon_hostname(hi);
*arg++ = get_ip_address(hi);
*arg++ = hi->tcp_port.buf;
*arg = NULL;
strvec_push(&child.args, access_hook);
strvec_push(&child.args, service->name);
strvec_push(&child.args, path);
strvec_push(&child.args, hi->hostname.buf);
strvec_push(&child.args, get_canon_hostname(hi));
strvec_push(&child.args, get_ip_address(hi));
strvec_push(&child.args, hi->tcp_port.buf);
child.use_shell = 1;
child.argv = argv;
child.no_stdin = 1;
child.no_stderr = 1;
child.out = -1;
@ -922,7 +918,7 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
#endif
}
cld.argv = cld_argv.v;
strvec_pushv(&cld.args, cld_argv.v);
cld.in = incoming;
cld.out = dup(incoming);

@ -6921,19 +6921,15 @@ static char *run_textconv(struct repository *r,
size_t *outsize)
{
struct diff_tempfile *temp;
const char *argv[3];
const char **arg = argv;
struct child_process child = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
int err = 0;
temp = prepare_temp_file(r, spec->path, spec);
*arg++ = pgm;
*arg++ = temp->name;
*arg = NULL;
strvec_push(&child.args, pgm);
strvec_push(&child.args, temp->name);
child.use_shell = 1;
child.argv = argv;
child.out = -1;
if (start_command(&child)) {
remove_tempfile();

@ -1,6 +1,7 @@
#include "cache.h"
#include "config.h"
#include "strbuf.h"
#include "strvec.h"
#include "run-command.h"
#include "sigchain.h"
@ -55,7 +56,6 @@ static int launch_specified_editor(const char *editor, const char *path,
if (strcmp(editor, ":")) {
struct strbuf realpath = STRBUF_INIT;
const char *args[] = { editor, NULL, NULL };
struct child_process p = CHILD_PROCESS_INIT;
int ret, sig;
int print_waiting_for_editor = advice_enabled(ADVICE_WAITING_FOR_EDITOR) && isatty(2);
@ -77,10 +77,10 @@ static int launch_specified_editor(const char *editor, const char *path,
}
strbuf_realpath(&realpath, path, 1);
args[1] = realpath.buf;
p.argv = args;
p.env = env;
strvec_pushl(&p.args, editor, realpath.buf, NULL);
if (env)
strvec_pushv(&p.env_array, (const char **)env);
p.use_shell = 1;
p.trace2_child_class = "editor";
if (start_command(&p) < 0) {

@ -480,7 +480,7 @@ static void run_service(const char **argv, int buffer_input)
strvec_pushf(&cld.env_array,
"GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
cld.argv = argv;
strvec_pushv(&cld.args, argv);
if (buffer_input || gzipped_request || req_len >= 0)
cld.in = -1;
cld.git_cmd = 1;

@ -2126,8 +2126,9 @@ int finish_http_pack_request(struct http_pack_request *preq)
ip.git_cmd = 1;
ip.in = tmpfile_fd;
ip.argv = preq->index_pack_args ? preq->index_pack_args
: default_index_pack_args;
strvec_pushv(&ip.args, preq->index_pack_args ?
preq->index_pack_args :
default_index_pack_args);
if (preq->preserve_index_pack_stdout)
ip.out = 0;

@ -794,7 +794,7 @@ static void fill_alternate_refs_command(struct child_process *cmd,
}
}
cmd->env = local_repo_env;
strvec_pushv(&cmd->env_array, (const char **)local_repo_env);
cmd->out = -1;
}

@ -8,15 +8,12 @@
static char *do_askpass(const char *cmd, const char *prompt)
{
struct child_process pass = CHILD_PROCESS_INIT;
const char *args[3];
static struct strbuf buffer = STRBUF_INIT;
int err = 0;
args[0] = cmd;
args[1] = prompt;
args[2] = NULL;
strvec_push(&pass.args, cmd);
strvec_push(&pass.args, prompt);
pass.argv = args;
pass.out = -1;
if (start_command(&pass))

@ -1061,7 +1061,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
client.in = -1;
client.out = -1;
client.git_cmd = 1;
client.argv = client_argv;
strvec_pushv(&client.args, client_argv);
if (start_command(&client))
exit(1);
write_or_die(client.in, preamble->buf, preamble->len);

@ -380,7 +380,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
switch (cerr->err) {
case CHILD_ERR_CHDIR:
error_errno("exec '%s': cd to '%s' failed",
cmd->argv[0], cmd->dir);
cmd->args.v[0], cmd->dir);
break;
case CHILD_ERR_DUP2:
error_errno("dup2() in child failed");
@ -392,12 +392,12 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
error_errno("sigprocmask failed restoring signals");
break;
case CHILD_ERR_ENOENT:
error_errno("cannot run %s", cmd->argv[0]);
error_errno("cannot run %s", cmd->args.v[0]);
break;
case CHILD_ERR_SILENT:
break;
case CHILD_ERR_ERRNO:
error_errno("cannot exec '%s'", cmd->argv[0]);
error_errno("cannot exec '%s'", cmd->args.v[0]);
break;
}
set_error_routine(old_errfn);
@ -405,7 +405,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
static int prepare_cmd(struct strvec *out, const struct child_process *cmd)
{
if (!cmd->argv[0])
if (!cmd->args.v[0])
BUG("command is empty");
/*
@ -415,11 +415,11 @@ static int prepare_cmd(struct strvec *out, const struct child_process *cmd)
strvec_push(out, SHELL_PATH);
if (cmd->git_cmd) {
prepare_git_cmd(out, cmd->argv);
prepare_git_cmd(out, cmd->args.v);
} else if (cmd->use_shell) {
prepare_shell_cmd(out, cmd->argv);
prepare_shell_cmd(out, cmd->args.v);
} else {
strvec_pushv(out, cmd->argv);
strvec_pushv(out, cmd->args.v);
}
/*
@ -654,15 +654,10 @@ static void trace_run_command(const struct child_process *cp)
sq_quote_buf_pretty(&buf, cp->dir);
strbuf_addch(&buf, ';');
}
/*
* The caller is responsible for initializing cp->env from
* cp->env_array if needed. We only check one place.
*/
if (cp->env)
trace_add_env(&buf, cp->env);
trace_add_env(&buf, cp->env_array.v);
if (cp->git_cmd)
strbuf_addstr(&buf, " git");
sq_quote_argv_pretty(&buf, cp->argv);
sq_quote_argv_pretty(&buf, cp->args.v);
trace_printf("%s", buf.buf);
strbuf_release(&buf);
@ -675,11 +670,6 @@ int start_command(struct child_process *cmd)
int failed_errno;
char *str;
if (!cmd->argv)
cmd->argv = cmd->args.v;
if (!cmd->env)
cmd->env = cmd->env_array.v;
/*
* In case of errors we must keep the promise to close FDs
* that have been passed in via ->in and ->out.
@ -728,7 +718,7 @@ int start_command(struct child_process *cmd)
str = "standard error";
fail_pipe:
error("cannot create %s pipe for %s: %s",
str, cmd->argv[0], strerror(failed_errno));
str, cmd->args.v[0], strerror(failed_errno));
child_process_clear(cmd);
errno = failed_errno;
return -1;
@ -757,7 +747,7 @@ fail_pipe:
failed_errno = errno;
cmd->pid = -1;
if (!cmd->silent_exec_failure)
error_errno("cannot run %s", cmd->argv[0]);
error_errno("cannot run %s", cmd->args.v[0]);
goto end_of_spawn;
}
@ -769,7 +759,7 @@ fail_pipe:
set_cloexec(null_fd);
}
childenv = prep_childenv(cmd->env);
childenv = prep_childenv(cmd->env_array.v);
atfork_prepare(&as);
/*
@ -867,7 +857,7 @@ fail_pipe:
}
atfork_parent(&as);
if (cmd->pid < 0)
error_errno("cannot fork() for %s", cmd->argv[0]);
error_errno("cannot fork() for %s", cmd->args.v[0]);
else if (cmd->clean_on_exit)
mark_child_for_cleanup(cmd->pid, cmd);
@ -884,7 +874,7 @@ fail_pipe:
* At this point we know that fork() succeeded, but exec()
* failed. Errors have been reported to our stderr.
*/
wait_or_whine(cmd->pid, cmd->argv[0], 0);
wait_or_whine(cmd->pid, cmd->args.v[0], 0);
child_err_spew(cmd, &cerr);
failed_errno = errno;
cmd->pid = -1;
@ -901,7 +891,7 @@ end_of_spawn:
#else
{
int fhin = 0, fhout = 1, fherr = 2;
const char **sargv = cmd->argv;
const char **sargv = cmd->args.v;
struct strvec nargv = STRVEC_INIT;
if (cmd->no_stdin)
@ -928,20 +918,20 @@ end_of_spawn:
fhout = dup(cmd->out);
if (cmd->git_cmd)
cmd->argv = prepare_git_cmd(&nargv, cmd->argv);
cmd->args.v = prepare_git_cmd(&nargv, sargv);
else if (cmd->use_shell)
cmd->argv = prepare_shell_cmd(&nargv, cmd->argv);
cmd->args.v = prepare_shell_cmd(&nargv, sargv);
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env,
cmd->pid = mingw_spawnvpe(cmd->args.v[0], cmd->args.v, (char**) cmd->env_array.v,
cmd->dir, fhin, fhout, fherr);
failed_errno = errno;
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
error_errno("cannot spawn %s", cmd->argv[0]);
error_errno("cannot spawn %s", cmd->args.v[0]);
if (cmd->clean_on_exit && cmd->pid >= 0)
mark_child_for_cleanup(cmd->pid, cmd);
strvec_clear(&nargv);
cmd->argv = sargv;
cmd->args.v = sargv;
if (fhin != 0)
close(fhin);
if (fhout != 1)
@ -991,7 +981,7 @@ end_of_spawn:
int finish_command(struct child_process *cmd)
{
int ret = wait_or_whine(cmd->pid, cmd->argv[0], 0);
int ret = wait_or_whine(cmd->pid, cmd->args.v[0], 0);
trace2_child_exit(cmd, ret);
child_process_clear(cmd);
invalidate_lstat_cache();
@ -1000,7 +990,7 @@ int finish_command(struct child_process *cmd)
int finish_command_in_signal(struct child_process *cmd)
{
int ret = wait_or_whine(cmd->pid, cmd->argv[0], 1);
int ret = wait_or_whine(cmd->pid, cmd->args.v[0], 1);
trace2_child_exit(cmd, ret);
return ret;
}
@ -1038,7 +1028,7 @@ int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
const char *const *env, const char *tr2_class)
{
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.argv = argv;
strvec_pushv(&cmd.args, argv);
cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
@ -1048,7 +1038,8 @@ int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
cmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;
cmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0;
cmd.dir = dir;
cmd.env = env;
if (env)
strvec_pushv(&cmd.env_array, (const char **)env);
cmd.trace2_child_class = tr2_class;
return run_command(&cmd);
}
@ -1334,7 +1325,8 @@ int run_hook_ve(const char *const *env, const char *name, va_list args)
strvec_push(&hook.args, p);
while ((p = va_arg(args, const char *)))
strvec_push(&hook.args, p);
hook.env = env;
if (env)
strvec_pushv(&hook.env_array, (const char **)env);
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
hook.trace2_hook_name = name;

@ -44,22 +44,35 @@
struct child_process {
/**
* The .argv member is set up as an array of string pointers (NULL
* terminated), of which .argv[0] is the program name to run (usually
* without a path). If the command to run is a git command, set argv[0] to
* the command name without the 'git-' prefix and set .git_cmd = 1.
* The .args is a `struct strvec', use that API to manipulate
* it, e.g. strvec_pushv() to add an existing "const char **"
* vector.
*
* Note that the ownership of the memory pointed to by .argv stays with the
* caller, but it should survive until `finish_command` completes. If the
* .argv member is NULL, `start_command` will point it at the .args
* `strvec` (so you may use one or the other, but you must use exactly
* one). The memory in .args will be cleaned up automatically during
* `finish_command` (or during `start_command` when it is unsuccessful).
* If the command to run is a git command, set the first
* element in the strvec to the command name without the
* 'git-' prefix and set .git_cmd = 1.
*
* The memory in .args will be cleaned up automatically during
* `finish_command` (or during `start_command` when it is unsuccessful).
*/
const char **argv;
struct strvec args;
/**
* Like .args the .env_array is a `struct strvec'.
*
* To modify the environment of the sub-process, specify an array of
* environment settings. Each string in the array manipulates the
* environment.
*
* - If the string is of the form "VAR=value", i.e. it contains '='
* the variable is added to the child process's environment.
*
* - If the string does not contain '=', it names an environment
* variable that will be removed from the child process's environment.
*
* The memory in .env_array will be cleaned up automatically during
* `finish_command` (or during `start_command` when it is unsuccessful).
*/
struct strvec env_array;
pid_t pid;
@ -96,23 +109,6 @@ struct child_process {
*/
const char *dir;
/**
* To modify the environment of the sub-process, specify an array of
* string pointers (NULL terminated) in .env:
*
* - If the string is of the form "VAR=value", i.e. it contains '='
* the variable is added to the child process's environment.
*
* - If the string does not contain '=', it names an environment
* variable that will be removed from the child process's environment.
*
* If the .env member is NULL, `start_command` will point it at the
* .env_array `strvec` (so you may use one or the other, but not both).
* The memory in .env_array will be cleaned up automatically during
* `finish_command` (or during `start_command` when it is unsuccessful).
*/
const char *const *env;
unsigned no_stdin:1;
unsigned no_stdout:1;
unsigned no_stderr:1;

@ -1164,18 +1164,14 @@ static int run_rewrite_hook(const struct object_id *oldoid,
const struct object_id *newoid)
{
struct child_process proc = CHILD_PROCESS_INIT;
const char *argv[3];
int code;
struct strbuf sb = STRBUF_INIT;
const char *hook_path = find_hook("post-rewrite");
argv[0] = find_hook("post-rewrite");
if (!argv[0])
if (!hook_path)
return 0;
argv[1] = "amend";
argv[2] = NULL;
proc.argv = argv;
strvec_pushl(&proc.args, hook_path, "amend", NULL);
proc.in = -1;
proc.stdout_to_stderr = 1;
proc.trace2_hook_name = "post-rewrite";

@ -187,7 +187,7 @@ static int handshake_capabilities(struct child_process *process,
*supported_capabilities |= capabilities[i].flag;
} else {
die("subprocess '%s' requested unsupported capability '%s'",
process->argv[0], p);
process->args.v[0], p);
}
}

@ -31,7 +31,7 @@ static int parallel_next(struct child_process *cp,
if (number_callbacks >= 4)
return 0;
strvec_pushv(&cp->args, d->argv);
strvec_pushv(&cp->args, d->args.v);
strbuf_addstr(err, "preloaded output of a child\n");
number_callbacks++;
return 1;
@ -274,7 +274,7 @@ static int quote_stress_test(int argc, const char **argv)
if (i < skip)
continue;
cp.argv = args.v;
strvec_pushv(&cp.args, args.v);
strbuf_reset(&out);
if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
return error("Failed to spawn child process");
@ -396,7 +396,7 @@ int cmd__run_command(int argc, const char **argv)
}
if (argc < 3)
return 1;
proc.argv = (const char **)argv + 2;
strvec_pushv(&proc.args, (const char **)argv + 2);
if (!strcmp(argv[1], "start-command-ENOENT")) {
if (start_command(&proc) < 0 && errno == ENOENT)
@ -408,7 +408,8 @@ int cmd__run_command(int argc, const char **argv)
exit(run_command(&proc));
jobs = atoi(argv[2]);
proc.argv = (const char **)argv + 3;
strvec_clear(&proc.args);
strvec_pushv(&proc.args, (const char **)argv + 3);
if (!strcmp(argv[1], "run-command-parallel"))
exit(run_processes_parallel(jobs, parallel_next,

@ -15,6 +15,6 @@ int cmd__subprocess(int argc, const char **argv)
argv++;
}
cp.git_cmd = 1;
cp.argv = (const char **)argv + 1;
strvec_pushv(&cp.args, (const char **)argv + 1);
return run_command(&cp);
}

@ -354,7 +354,7 @@ static void fn_child_start_fl(const char *file, int line,
jw_object_inline_begin_array(&jw, "argv");
if (cmd->git_cmd)
jw_array_string(&jw, "git");
jw_array_argv(&jw, cmd->argv);
jw_array_argv(&jw, cmd->args.v);
jw_end(&jw);
jw_end(&jw);

@ -232,7 +232,7 @@ static void fn_child_start_fl(const char *file, int line,
strbuf_addch(&buf_payload, ' ');
if (cmd->git_cmd)
strbuf_addstr(&buf_payload, "git ");
sq_append_quote_argv_pretty(&buf_payload, cmd->argv);
sq_append_quote_argv_pretty(&buf_payload, cmd->args.v);
normal_io_write_fl(file, line, &buf_payload);
strbuf_release(&buf_payload);

@ -335,10 +335,10 @@ static void fn_child_start_fl(const char *file, int line,
strbuf_addstr(&buf_payload, " argv:[");
if (cmd->git_cmd) {
strbuf_addstr(&buf_payload, "git");
if (cmd->argv[0])
if (cmd->args.nr)
strbuf_addch(&buf_payload, ' ');
}
sq_append_quote_argv_pretty(&buf_payload, cmd->argv);
sq_append_quote_argv_pretty(&buf_payload, cmd->args.v);
strbuf_addch(&buf_payload, ']');
perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,

@ -236,7 +236,7 @@ static char *apply_command(struct conf_info *conf, const char *arg)
strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
strvec_push(&cp.args, cmd.buf);
}
cp.env = local_repo_env;
strvec_pushv(&cp.env_array, (const char **)local_repo_env);
cp.no_stdin = 1;
cp.use_shell = 1;

@ -1204,16 +1204,15 @@ static int run_pre_push_hook(struct transport *transport,
struct ref *r;
struct child_process proc = CHILD_PROCESS_INIT;
struct strbuf buf;
const char *argv[4];
const char *hook_path = find_hook("pre-push");
if (!(argv[0] = find_hook("pre-push")))
if (!hook_path)
return 0;
argv[1] = transport->remote->name;
argv[2] = transport->url;
argv[3] = NULL;
strvec_push(&proc.args, hook_path);
strvec_push(&proc.args, transport->remote->name);
strvec_push(&proc.args, transport->url);
proc.argv = argv;
proc.in = -1;
proc.trace2_hook_name = "pre-push";

@ -596,14 +596,11 @@ static int do_reachable_revlist(struct child_process *cmd,
struct object_array *reachable,
enum allow_uor allow_uor)
{
static const char *argv[] = {
"rev-list", "--stdin", NULL,
};
struct object *o;
FILE *cmd_in = NULL;
int i;
cmd->argv = argv;
strvec_pushl(&cmd->args, "rev-list", "--stdin", NULL);
cmd->git_cmd = 1;
cmd->no_stderr = 1;
cmd->in = -1;