void start_pager(char *pager) { const char *pager_argv[] = { "sh", "-c", NULL, NULL }; if (pager == NULL) return; /* spawn the pager */ pager_argv[2] = pager; if (start_command(pager_argv)) return; /* original process continues, but writes to the pipe */ dup2(pager_fd, STDOUT_FILENO); if (isatty(STDERR_FILENO)) dup2(pager_fd, STDERR_FILENO); close(pager_fd); atexit(wait_for_pager); }
static void run_update_post_hook(struct command *commands) { struct command *cmd; int argc; const char **argv; struct child_process proc; for (argc = 0, cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string || cmd->did_not_exist) continue; argc++; } if (!argc || access(update_post_hook, X_OK) < 0) return; argv = xmalloc(sizeof(*argv) * (2 + argc)); argv[0] = update_post_hook; for (argc = 1, cmd = commands; cmd; cmd = cmd->next) { char *p; if (cmd->error_string || cmd->did_not_exist) continue; p = xmalloc(strlen(cmd->ref_name) + 1); strcpy(p, cmd->ref_name); argv[argc] = p; argc++; } argv[argc] = NULL; memset(&proc, 0, sizeof(proc)); proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; proc.argv = argv; if (!start_command(&proc)) { if (use_sideband) copy_to_sideband(proc.err, -1, NULL); finish_command(&proc); } }
int ok_to_remove_submodule(const char *path) { ssize_t len; struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = { "status", "--porcelain", "-u", "--ignore-submodules=none", NULL, }; struct strbuf buf = STRBUF_INIT; int ok_to_remove = 1; if (!file_exists(path) || is_empty_dir(path)) return 1; if (!submodule_uses_gitfile(path)) return 0; cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; cp.out = -1; cp.dir = path; if (start_command(&cp)) die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path); len = strbuf_read(&buf, cp.out, 1024); if (len > 2) ok_to_remove = 0; close(cp.out); if (finish_command(&cp)) die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path); strbuf_release(&buf); return ok_to_remove; }
/* * Note, "git status --porcelain" is used to determine if it's safe to * delete a whole worktree. "git status" does not ignore user * configuration, so if a normal "git status" shows "clean" for the * user, then it's ok to remove it. * * This assumption may be a bad one. We may want to ignore * (potentially bad) user settings and only delete a worktree when * it's absolutely safe to do so from _our_ point of view because we * know better. */ static void check_clean_worktree(struct worktree *wt, const char *original_path) { struct argv_array child_env = ARGV_ARRAY_INIT; struct child_process cp; char buf[1]; int ret; /* * Until we sort this out, all submodules are "dirty" and * will abort this function. */ validate_no_submodules(wt); argv_array_pushf(&child_env, "%s=%s/.git", GIT_DIR_ENVIRONMENT, wt->path); argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, wt->path); memset(&cp, 0, sizeof(cp)); argv_array_pushl(&cp.args, "status", "--porcelain", "--ignore-submodules=none", NULL); cp.env = child_env.argv; cp.git_cmd = 1; cp.dir = wt->path; cp.out = -1; ret = start_command(&cp); if (ret) die_errno(_("failed to run 'git status' on '%s'"), original_path); ret = xread(cp.out, buf, sizeof(buf)); if (ret) die(_("'%s' is dirty, use --force to delete it"), original_path); close(cp.out); ret = finish_command(&cp); if (ret) die_errno(_("failed to run 'git status' on '%s', code %d"), original_path, ret); }
static int run_credential_helper(struct credential *c, const char *cmd, int want_output) { struct child_process helper; const char *argv[] = { NULL, NULL }; FILE *fp; memset(&helper, 0, sizeof(helper)); argv[0] = cmd; helper.argv = argv; helper.use_shell = 1; helper.in = -1; if (want_output) helper.out = -1; else helper.no_stdout = 1; if (start_command(&helper) < 0) return -1; fp = xfdopen(helper.in, "w"); credential_write(c, fp); fclose(fp); if (want_output) { int r; fp = xfdopen(helper.out, "r"); r = credential_read(c, fp); fclose(fp); if (r < 0) { finish_command(&helper); return -1; } } if (finish_command(&helper)) return -1; return 0; }
static int run_receive_hook(const char *hook_name) { static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4]; struct command *cmd; struct child_process proc; const char *argv[2]; int have_input = 0, code; for (cmd = commands; !have_input && cmd; cmd = cmd->next) { if (!cmd->error_string) have_input = 1; } if (!have_input || access(hook_name, X_OK) < 0) return 0; argv[0] = hook_name; argv[1] = NULL; memset(&proc, 0, sizeof(proc)); proc.argv = argv; proc.in = -1; proc.stdout_to_stderr = 1; code = start_command(&proc); if (code) return hook_status(code, hook_name); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) { size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1), cmd->ref_name); if (write_in_full(proc.in, buf, n) != n) break; } } close(proc.in); return hook_status(finish_command(&proc), hook_name); }
/* returns * 0 if a new task was started. * 1 if no new jobs was started (get_next_task ran out of work, non critical * problem with starting a new command) * <0 no new job was started, user wishes to shutdown early. Use negative code * to signal the children. */ static int pp_start_one(struct parallel_processes *pp) { int i, code; for (i = 0; i < pp->max_processes; i++) if (pp->children[i].state == GIT_CP_FREE) break; if (i == pp->max_processes) die("BUG: bookkeeping is hard"); code = pp->get_next_task(&pp->children[i].process, &pp->children[i].err, pp->data, &pp->children[i].data); if (!code) { strbuf_addbuf(&pp->buffered_output, &pp->children[i].err); strbuf_reset(&pp->children[i].err); return 1; } pp->children[i].process.err = -1; pp->children[i].process.stdout_to_stderr = 1; pp->children[i].process.no_stdin = 1; if (start_command(&pp->children[i].process)) { code = pp->start_failure(&pp->children[i].process, &pp->children[i].err, pp->data, &pp->children[i].data); strbuf_addbuf(&pp->buffered_output, &pp->children[i].err); strbuf_reset(&pp->children[i].err); if (code) pp->shutdown = 1; return code; } pp->nr_processes++; pp->children[i].state = GIT_CP_WORKING; pp->pfd[i].fd = pp->children[i].process.err; return 0; }
static int run_gpg_verify(const char *buf, unsigned long size, int verbose) { struct child_process gpg; const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL}; char path[PATH_MAX]; size_t len; int fd, ret; fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX"); if (fd < 0) return error("could not create temporary file '%s': %s", path, strerror(errno)); if (write_in_full(fd, buf, size) < 0) return error("failed writing temporary file '%s': %s", path, strerror(errno)); close(fd); /* find the length without signature */ len = parse_signature(buf, size); if (verbose) write_in_full(1, buf, len); memset(&gpg, 0, sizeof(gpg)); gpg.argv = args_gpg; gpg.in = -1; args_gpg[2] = path; if (start_command(&gpg)) { unlink(path); return error("could not run gpg."); } write_in_full(gpg.in, buf, len); close(gpg.in); ret = finish_command(&gpg); unlink_or_warn(path); return ret; }
int launch_editor(const char *path, struct strbuf *buffer, const char *const *env) { const char *editor = git_editor(); if (!editor) return error("Terminal is dumb, but EDITOR unset"); if (strcmp(editor, ":")) { const char *args[] = { editor, path, NULL }; struct child_process p; int ret, sig; memset(&p, 0, sizeof(p)); p.argv = args; p.env = env; p.use_shell = 1; if (start_command(&p) < 0) return error("unable to start editor '%s'", editor); sigchain_push(SIGINT, SIG_IGN); sigchain_push(SIGQUIT, SIG_IGN); ret = finish_command(&p); sig = ret + 128; sigchain_pop(SIGINT); sigchain_pop(SIGQUIT); if (sig == SIGINT || sig == SIGQUIT) raise(sig); if (ret) return error("There was a problem with the editor '%s'.", editor); } if (!buffer) return 0; if (strbuf_read_file(buffer, path, 0) < 0) return error("could not read file '%s': %s", path, strerror(errno)); return 0; }
char *git_getpass(const char *prompt) { const char *askpass; struct child_process pass; const char *args[3]; static struct strbuf buffer = STRBUF_INIT; askpass = getenv("GIT_ASKPASS"); if (!askpass) askpass = askpass_program; if (!askpass) askpass = getenv("SSH_ASKPASS"); if (!askpass || !(*askpass)) return getpass(prompt); args[0] = askpass; args[1] = prompt; args[2] = NULL; memset(&pass, 0, sizeof(pass)); pass.argv = args; pass.out = -1; if (start_command(&pass)) exit(1); strbuf_reset(&buffer); if (strbuf_read(&buffer, pass.out, 20) < 0) die("failed to read password from %s\n", askpass); close(pass.out); if (finish_command(&pass)) exit(1); strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n")); return buffer.buf; }
static void spawn_daemon(const char *socket) { struct child_process daemon = CHILD_PROCESS_INIT; const char *argv[] = { NULL, NULL, NULL }; char buf[128]; int r; argv[0] = "git-credential-cache--daemon"; argv[1] = socket; daemon.argv = argv; daemon.no_stdin = 1; daemon.out = -1; if (start_command(&daemon)) die_errno("unable to start cache daemon"); r = read_in_full(daemon.out, buf, sizeof(buf)); if (r < 0) die_errno("unable to read result code from cache daemon"); if (r != 3 || memcmp(buf, "ok\n", 3)) die("cache daemon did not start: %.*s", r, buf); close(daemon.out); }
static void run_update_post_hook(struct command *commands) { struct command *cmd; int argc; const char **argv; struct child_process proc = CHILD_PROCESS_INIT; const char *hook; hook = find_hook("post-update"); for (argc = 0, cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string || cmd->did_not_exist) continue; argc++; } if (!argc || !hook) return; argv = xmalloc(sizeof(*argv) * (2 + argc)); argv[0] = hook; for (argc = 1, cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string || cmd->did_not_exist) continue; argv[argc] = xstrdup(cmd->ref_name); argc++; } argv[argc] = NULL; proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; proc.argv = argv; if (!start_command(&proc)) { if (use_sideband) copy_to_sideband(proc.err, -1, NULL); finish_command(&proc); } }
static int compute_and_write_prerequisites(int bundle_fd, struct rev_info *revs, int argc, const char **argv) { struct child_process rls = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; FILE *rls_fout; int i; argv_array_pushl(&rls.args, "rev-list", "--boundary", "--pretty=oneline", NULL); for (i = 1; i < argc; i++) argv_array_push(&rls.args, argv[i]); rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) { unsigned char sha1[20]; if (buf.len > 0 && buf.buf[0] == '-') { write_or_die(bundle_fd, buf.buf, buf.len); if (!get_sha1_hex(buf.buf + 1, sha1)) { struct object *object = parse_object_or_die(sha1, buf.buf); object->flags |= UNINTERESTING; add_pending_object(revs, object, buf.buf); } } else if (!get_sha1_hex(buf.buf, sha1)) { struct object *object = parse_object_or_die(sha1, buf.buf); object->flags |= SHOWN; } } strbuf_release(&buf); fclose(rls_fout); if (finish_command(&rls)) return error(_("rev-list died")); return 0; }
void setup_pager(void) { const char *pager = getenv("PERF_PAGER"); if (!isatty(1)) return; if (!pager) { if (!pager_program) perf_config(perf_default_config, NULL); pager = pager_program; } if (!pager) pager = getenv("PAGER"); if (!pager) pager = "less"; else if (!*pager || !strcmp(pager, "cat")) return; spawned_pager = 1; /* means we are emitting to terminal */ /* spawn the pager */ pager_argv[2] = pager; pager_process.argv = pager_argv; pager_process.in = -1; pager_process.preexec_cb = pager_preexec; if (start_command(&pager_process)) return; /* original process continues, but writes to the pipe */ dup2(pager_process.in, 1); if (isatty(2)) dup2(pager_process.in, 2); close(pager_process.in); /* this makes sure that the parent terminates after the pager */ sigchain_push_common(wait_for_pager_signal); atexit(wait_for_pager); }
static struct child_process *get_helper(struct transport *transport) { struct helper_data *data = transport->data; struct strbuf buf = STRBUF_INIT; struct child_process *helper; FILE *file; if (data->helper) return data->helper; helper = xcalloc(1, sizeof(*helper)); helper->in = -1; helper->out = -1; helper->err = 0; helper->argv = xcalloc(4, sizeof(*helper->argv)); strbuf_addf(&buf, "remote-%s", data->name); helper->argv[0] = strbuf_detach(&buf, NULL); helper->argv[1] = transport->remote->name; helper->argv[2] = transport->url; helper->git_cmd = 1; if (start_command(helper)) die("Unable to run helper: git %s", helper->argv[0]); data->helper = helper; write_str_in_full(helper->in, "capabilities\n"); file = xfdopen(helper->out, "r"); while (1) { if (strbuf_getline(&buf, file, '\n') == EOF) exit(128); /* child died, message supplied already */ if (!*buf.buf) break; if (!strcmp(buf.buf, "fetch")) data->fetch = 1; } return data->helper; }
void setup_pager(void) { const char *pager = getenv(subcmd_config.pager_env); if (!isatty(1)) return; if (!pager) pager = getenv("PAGER"); if (!(pager || access("/usr/bin/pager", X_OK))) pager = "/usr/bin/pager"; if (!(pager || access("/usr/bin/less", X_OK))) pager = "/usr/bin/less"; if (!pager) pager = "cat"; if (!*pager || !strcmp(pager, "cat")) return; spawned_pager = 1; /* means we are emitting to terminal */ /* spawn the pager */ pager_argv[2] = pager; pager_process.argv = pager_argv; pager_process.in = -1; pager_process.preexec_cb = pager_preexec; if (start_command(&pager_process)) return; /* original process continues, but writes to the pipe */ dup2(pager_process.in, 1); if (isatty(2)) dup2(pager_process.in, 2); close(pager_process.in); /* this makes sure that the parent terminates after the pager */ sigchain_push_common(wait_for_pager_signal); atexit(wait_for_pager); }
static int run_post_upload_pack_hook(size_t total, struct timeval *tv) { const char *argv[2]; struct child_process proc; int err, i; argv[0] = "hooks/post-upload-pack"; argv[1] = NULL; if (access(argv[0], X_OK) < 0) return 0; memset(&proc, 0, sizeof(proc)); proc.argv = argv; proc.in = -1; proc.stdout_to_stderr = 1; err = start_command(&proc); if (err) return err; for (i = 0; !err && i < want_obj.nr; i++) err |= feed_obj_to_hook("want", &want_obj, i, proc.in); for (i = 0; !err && i < have_obj.nr; i++) err |= feed_obj_to_hook("have", &have_obj, i, proc.in); if (!err) err |= feed_msg_to_hook(proc.in, "time %ld.%06ld\n", (long)tv->tv_sec, (long)tv->tv_usec); if (!err) err |= feed_msg_to_hook(proc.in, "size %ld\n", (long)total); if (!err) err |= feed_msg_to_hook(proc.in, "kind %s\n", (nr_our_refs == want_obj.nr && !have_obj.nr) ? "clone" : "fetch"); if (close(proc.in)) err = 1; if (finish_command(&proc)) err = 1; return err; }
static int write_tar_filter_archive(const struct archiver *ar, struct archiver_args *args) { struct strbuf cmd = STRBUF_INIT; struct child_process filter; const char *argv[2]; int r; if (!ar->data) die("BUG: tar-filter archiver called with no filter defined"); strbuf_addstr(&cmd, ar->data); if (args->compression_level >= 0) strbuf_addf(&cmd, " -%d", args->compression_level); memset(&filter, 0, sizeof(filter)); argv[0] = cmd.buf; argv[1] = NULL; filter.argv = argv; filter.use_shell = 1; filter.in = -1; if (start_command(&filter) < 0) die_errno("unable to start '%s' filter", argv[0]); close(1); if (dup2(filter.in, 1) < 0) die_errno("unable to redirect descriptor"); close(filter.in); r = write_tar_archive(ar, args); close(1); if (finish_command(&filter) != 0) die("'%s' filter reported error", argv[0]); strbuf_release(&cmd); return r; }
static void write_commented_object(int fd, const unsigned char *object) { const char *show_args[5] = {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL}; struct child_process show; struct strbuf buf = STRBUF_INIT; FILE *show_out; /* Invoke "git show --stat --no-notes $object" */ memset(&show, 0, sizeof(show)); show.argv = show_args; show.no_stdin = 1; show.out = -1; show.err = 0; show.git_cmd = 1; if (start_command(&show)) die("unable to start 'show' for object '%s'", sha1_to_hex(object)); /* Open the output as FILE* so strbuf_getline() can be used. */ show_out = xfdopen(show.out, "r"); if (show_out == NULL) die_errno("can't fdopen 'show' output fd"); /* Prepend "# " to each output line and write result to 'fd' */ while (strbuf_getline(&buf, show_out, '\n') != EOF) { write_or_die(fd, "# ", 2); write_or_die(fd, buf.buf, buf.len); write_or_die(fd, "\n", 1); } strbuf_release(&buf); if (fclose(show_out)) die_errno("failed to close pipe to 'show' for object '%s'", sha1_to_hex(object)); if (finish_command(&show)) die("failed to finish 'show' for object '%s'", sha1_to_hex(object)); }
static struct child_process *git_proxy_connect(int fd[2], char *host) { const char *port = STR(DEFAULT_GIT_PORT); const char **argv; struct child_process *proxy; get_host_and_port(&host, &port); argv = xmalloc(sizeof(*argv) * 4); argv[0] = git_proxy_command; argv[1] = host; argv[2] = port; argv[3] = NULL; proxy = xcalloc(1, sizeof(*proxy)); proxy->argv = argv; proxy->in = -1; proxy->out = -1; if (start_command(proxy)) die("cannot start proxy %s", argv[0]); fd[0] = proxy->out; /* read from proxy stdout */ fd[1] = proxy->in; /* write to proxy stdin */ return proxy; }
void setup_pager(void) { const char *pager = git_pager(isatty(1)); if (!pager) return; /* * After we redirect standard output, we won't be able to use an ioctl * to get the terminal size. Let's grab it now, and then set $COLUMNS * to communicate it to any sub-processes. */ { char buf[64]; xsnprintf(buf, sizeof(buf), "%d", term_columns()); setenv("COLUMNS", buf, 0); } setenv("GIT_PAGER_IN_USE", "true", 1); /* spawn the pager */ prepare_pager_args(&pager_process, pager); pager_process.in = -1; argv_array_push(&pager_process.env_array, "GIT_PAGER_IN_USE"); if (start_command(&pager_process)) return; /* original process continues, but writes to the pipe */ dup2(pager_process.in, 1); if (isatty(2)) dup2(pager_process.in, 2); close(pager_process.in); /* this makes sure that the parent terminates after the pager */ sigchain_push_common(wait_for_pager_signal); atexit(wait_for_pager_atexit); }
static void git_proxy_connect(int fd[2], char *host) { const char *port = STR(DEFAULT_GIT_PORT); char *colon, *end; const char *argv[4]; struct child_process proxy; if (host[0] == '[') { end = strchr(host + 1, ']'); if (end) { *end = 0; end++; host++; } else end = host; } else end = host; colon = strchr(end, ':'); if (colon) { *colon = 0; port = colon + 1; } argv[0] = git_proxy_command; argv[1] = host; argv[2] = port; argv[3] = NULL; memset(&proxy, 0, sizeof(proxy)); proxy.argv = argv; proxy.in = -1; proxy.out = -1; if (start_command(&proxy)) die("cannot start proxy %s", argv[0]); fd[0] = proxy.out; /* read from proxy stdout */ fd[1] = proxy.in; /* write to proxy stdin */ }
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; pass.argv = args; pass.out = -1; if (start_command(&pass)) return NULL; strbuf_reset(&buffer); if (strbuf_read(&buffer, pass.out, 20) < 0) err = 1; close(pass.out); if (finish_command(&pass)) err = 1; if (err) { error("unable to read askpass response from '%s'", cmd); strbuf_release(&buffer); return NULL; } strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n")); return buffer.buf; }
static int run_child(const char *arg, const char *service) { int r; struct child_process child; memset(&child, 0, sizeof(child)); child.in = -1; child.out = -1; child.err = 0; child.argv = parse_argv(arg, service); if (start_command(&child) < 0) die("Can't run specified command"); if (git_req) send_git_request(child.in, service, git_req, git_req_vhost); r = bidirectional_transfer_loop(child.out, child.in); if (!r) r = finish_command(&child); else finish_command(&child); return r; }
int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, subprocess_start_fn startfn) { int err; struct child_process *process; const char *argv[] = { cmd, NULL }; entry->cmd = cmd; process = &entry->process; child_process_init(process); process->argv = argv; process->use_shell = 1; process->in = -1; process->out = -1; process->clean_on_exit = 1; process->clean_on_exit_handler = subprocess_exit_handler; err = start_command(process); if (err) { error("cannot fork to run subprocess '%s'", cmd); return err; } hashmap_entry_init(entry, strhash(cmd)); err = startfn(entry); if (err) { error("initialization for subprocess '%s' failed", cmd); subprocess_stop(hashmap, entry); return err; } hashmap_add(hashmap, entry); return 0; }
void setup_pager(void) { const char *pager = getenv("PAGER"); if (!isatty(1)) return; if (!pager) pager = "less"; else if (!*pager || !strcmp(pager, "cat")) return; /* spawn the pager */ pager_argv[2] = pager; pager_process.argv = pager_argv; pager_process.in = -1; pager_process.preexec_cb = pager_preexec; if (start_command(&pager_process)) return; /* original process continues, but writes to the pipe */ dup2(pager_process.in, 1); if (isatty(2)) dup2(pager_process.in, 2); close(pager_process.in); /* this makes sure that the parent terminates after the pager */ signal(SIGINT, wait_for_pager_signal); signal(SIGHUP, wait_for_pager_signal); signal(SIGTERM, wait_for_pager_signal); signal(SIGQUIT, wait_for_pager_signal); signal(SIGPIPE, wait_for_pager_signal); atexit(wait_for_pager); }
static int get_pack(struct fetch_pack_args *args, int xd[2], char **pack_lockfile) { struct async demux; const char *argv[22]; char keep_arg[256]; char hdr_arg[256]; const char **av, *cmd_name; int do_keep = args->keep_pack; struct child_process cmd; int ret; memset(&demux, 0, sizeof(demux)); if (use_sideband) { /* xd[] is talking with upload-pack; subprocess reads from * xd[0], spits out band#2 to stderr, and feeds us band#1 * through demux->out. */ demux.proc = sideband_demux; demux.data = xd; demux.out = -1; if (start_async(&demux)) die("fetch-pack: unable to fork off sideband" " demultiplexer"); } else demux.out = xd[0]; memset(&cmd, 0, sizeof(cmd)); cmd.argv = argv; av = argv; *hdr_arg = 0; if (!args->keep_pack && unpack_limit) { struct pack_header header; if (read_pack_header(demux.out, &header)) die("protocol error: bad pack header"); snprintf(hdr_arg, sizeof(hdr_arg), "--pack_header=%"PRIu32",%"PRIu32, ntohl(header.hdr_version), ntohl(header.hdr_entries)); if (ntohl(header.hdr_entries) < unpack_limit) do_keep = 0; else do_keep = 1; } if (alternate_shallow_file) { *av++ = "--shallow-file"; *av++ = alternate_shallow_file; } if (do_keep) { if (pack_lockfile) cmd.out = -1; *av++ = cmd_name = "index-pack"; *av++ = "--stdin"; if (!args->quiet && !args->no_progress) *av++ = "-v"; if (args->use_thin_pack) *av++ = "--fix-thin"; if (args->lock_pack || unpack_limit) { int s = sprintf(keep_arg, "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid()); if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) strcpy(keep_arg + s, "localhost"); *av++ = keep_arg; } if (args->check_self_contained_and_connected) *av++ = "--check-self-contained-and-connected"; } else { *av++ = cmd_name = "unpack-objects"; if (args->quiet || args->no_progress) *av++ = "-q"; args->check_self_contained_and_connected = 0; } if (*hdr_arg) *av++ = hdr_arg; if (fetch_fsck_objects >= 0 ? fetch_fsck_objects : transfer_fsck_objects >= 0 ? transfer_fsck_objects : 0) *av++ = "--strict"; *av++ = NULL; cmd.in = demux.out; cmd.git_cmd = 1; if (start_command(&cmd)) die("fetch-pack: unable to fork off %s", cmd_name); if (do_keep && pack_lockfile) { *pack_lockfile = index_pack_lockfile(cmd.out); close(cmd.out); } if (!use_sideband) /* Closed by start_command() */ xd[0] = -1; ret = finish_command(&cmd); if (!ret || (args->check_self_contained_and_connected && ret == 1)) args->self_contained_and_connected = args->check_self_contained_and_connected && ret == 0; else die("%s failed", cmd_name); if (use_sideband && finish_async(&demux)) die("error in sideband demultiplexer"); return 0; }
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, int argc, const char **argv) { char tmpdir[PATH_MAX]; struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT; struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT; struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT; struct strbuf wtdir = STRBUF_INIT; char *lbase_dir, *rbase_dir; size_t ldir_len, rdir_len, wtdir_len; const char *workdir, *tmp; int ret = 0, i; FILE *fp; struct hashmap working_tree_dups, submodules, symlinks2; struct hashmap_iter iter; struct pair_entry *entry; struct index_state wtindex; struct checkout lstate, rstate; int rc, flags = RUN_GIT_CMD, err = 0; struct child_process child = CHILD_PROCESS_INIT; const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL }; struct hashmap wt_modified, tmp_modified; int indices_loaded = 0; workdir = get_git_work_tree(); /* Setup temp directories */ tmp = getenv("TMPDIR"); xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp"); if (!mkdtemp(tmpdir)) return error("could not create '%s'", tmpdir); strbuf_addf(&ldir, "%s/left/", tmpdir); strbuf_addf(&rdir, "%s/right/", tmpdir); strbuf_addstr(&wtdir, workdir); if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1])) strbuf_addch(&wtdir, '/'); mkdir(ldir.buf, 0700); mkdir(rdir.buf, 0700); memset(&wtindex, 0, sizeof(wtindex)); memset(&lstate, 0, sizeof(lstate)); lstate.base_dir = lbase_dir = xstrdup(ldir.buf); lstate.base_dir_len = ldir.len; lstate.force = 1; memset(&rstate, 0, sizeof(rstate)); rstate.base_dir = rbase_dir = xstrdup(rdir.buf); rstate.base_dir_len = rdir.len; rstate.force = 1; ldir_len = ldir.len; rdir_len = rdir.len; wtdir_len = wtdir.len; hashmap_init(&working_tree_dups, (hashmap_cmp_fn)working_tree_entry_cmp, NULL, 0); hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, NULL, 0); hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, NULL, 0); child.no_stdin = 1; child.git_cmd = 1; child.use_shell = 0; child.clean_on_exit = 1; child.dir = prefix; child.out = -1; argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z", NULL); for (i = 0; i < argc; i++) argv_array_push(&child.args, argv[i]); if (start_command(&child)) die("could not obtain raw diff"); fp = xfdopen(child.out, "r"); /* Build index info for left and right sides of the diff */ i = 0; while (!strbuf_getline_nul(&info, fp)) { int lmode, rmode; struct object_id loid, roid; char status; const char *src_path, *dst_path; if (starts_with(info.buf, "::")) die(N_("combined diff formats('-c' and '--cc') are " "not supported in\n" "directory diff mode('-d' and '--dir-diff').")); if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid, &status)) break; if (strbuf_getline_nul(&lpath, fp)) break; src_path = lpath.buf; i++; if (status != 'C' && status != 'R') { dst_path = src_path; } else { if (strbuf_getline_nul(&rpath, fp)) break; dst_path = rpath.buf; } if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) { strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&loid)); add_left_or_right(&submodules, src_path, buf.buf, 0); strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&roid)); if (!oidcmp(&loid, &roid)) strbuf_addstr(&buf, "-dirty"); add_left_or_right(&submodules, dst_path, buf.buf, 1); continue; } if (S_ISLNK(lmode)) { char *content = get_symlink(&loid, src_path); add_left_or_right(&symlinks2, src_path, content, 0); free(content); } if (S_ISLNK(rmode)) { char *content = get_symlink(&roid, dst_path); add_left_or_right(&symlinks2, dst_path, content, 1); free(content); } if (lmode && status != 'C') { if (checkout_path(lmode, &loid, src_path, &lstate)) { ret = error("could not write '%s'", src_path); goto finish; } } if (rmode && !S_ISLNK(rmode)) { struct working_tree_entry *entry; /* Avoid duplicate working_tree entries */ FLEX_ALLOC_STR(entry, path, dst_path); hashmap_entry_init(entry, strhash(dst_path)); if (hashmap_get(&working_tree_dups, entry, NULL)) { free(entry); continue; } hashmap_add(&working_tree_dups, entry); if (!use_wt_file(workdir, dst_path, &roid)) { if (checkout_path(rmode, &roid, dst_path, &rstate)) { ret = error("could not write '%s'", dst_path); goto finish; } } else if (!is_null_oid(&roid)) { /* * Changes in the working tree need special * treatment since they are not part of the * index. */ struct cache_entry *ce2 = make_cache_entry(rmode, roid.hash, dst_path, 0, 0); add_index_entry(&wtindex, ce2, ADD_CACHE_JUST_APPEND); add_path(&rdir, rdir_len, dst_path); if (ensure_leading_directories(rdir.buf)) { ret = error("could not create " "directory for '%s'", dst_path); goto finish; } add_path(&wtdir, wtdir_len, dst_path); if (symlinks) { if (symlink(wtdir.buf, rdir.buf)) { ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } else { struct stat st; if (stat(wtdir.buf, &st)) st.st_mode = 0644; if (copy_file(rdir.buf, wtdir.buf, st.st_mode)) { ret = error("could not copy '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } } } } fclose(fp); fp = NULL; if (finish_command(&child)) { ret = error("error occurred running diff --raw"); goto finish; } if (!i) goto finish; /* * Changes to submodules require special treatment.This loop writes a * temporary file to both the left and right directories to show the * change in the recorded SHA1 for the submodule. */ hashmap_iter_init(&submodules, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } /* * Symbolic links require special treatment.The standard "git diff" * shows only the link itself, not the contents of the link target. * This loop replicates that behavior. */ hashmap_iter_init(&symlinks2, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } strbuf_release(&buf); strbuf_setlen(&ldir, ldir_len); helper_argv[1] = ldir.buf; strbuf_setlen(&rdir, rdir_len); helper_argv[2] = rdir.buf; if (extcmd) { helper_argv[0] = extcmd; flags = 0; } else setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1); rc = run_command_v_opt(helper_argv, flags); /* * If the diff includes working copy files and those * files were modified during the diff, then the changes * should be copied back to the working tree. * Do not copy back files when symlinks are used and the * external tool did not replace the original link with a file. * * These hashes are loaded lazily since they aren't needed * in the common case of --symlinks and the difftool updating * files through the symlink. */ hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); for (i = 0; i < wtindex.cache_nr; i++) { struct hashmap_entry dummy; const char *name = wtindex.cache[i]->name; struct stat st; add_path(&rdir, rdir_len, name); if (lstat(rdir.buf, &st)) continue; if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode)) continue; if (!indices_loaded) { static struct lock_file lock; strbuf_reset(&buf); strbuf_addf(&buf, "%s/wtindex", tmpdir); if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 || write_locked_index(&wtindex, &lock, COMMIT_LOCK)) { ret = error("could not write %s", buf.buf); rollback_lock_file(&lock); goto finish; } changed_files(&wt_modified, buf.buf, workdir); strbuf_setlen(&rdir, rdir_len); changed_files(&tmp_modified, buf.buf, rdir.buf); add_path(&rdir, rdir_len, name); indices_loaded = 1; } hashmap_entry_init(&dummy, strhash(name)); if (hashmap_get(&tmp_modified, &dummy, name)) { add_path(&wtdir, wtdir_len, name); if (hashmap_get(&wt_modified, &dummy, name)) { warning(_("both files modified: '%s' and '%s'."), wtdir.buf, rdir.buf); warning(_("working tree file has been left.")); warning("%s", ""); err = 1; } else if (unlink(wtdir.buf) || copy_file(wtdir.buf, rdir.buf, st.st_mode)) warning_errno(_("could not copy '%s' to '%s'"), rdir.buf, wtdir.buf); } } if (err) { warning(_("temporary files exist in '%s'."), tmpdir); warning(_("you may want to cleanup or recover these.")); exit(1); } else exit_cleanup(tmpdir, rc); finish: if (fp) fclose(fp); free(lbase_dir); free(rbase_dir); strbuf_release(&ldir); strbuf_release(&rdir); strbuf_release(&wtdir); strbuf_release(&buf); return ret; }
int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(6 * sizeof(const char *)); int i, ref_count = 0; struct strbuf buf = STRBUF_INIT; struct rev_info revs; struct child_process rls; FILE *rls_fout; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *)); argv_boundary[0] = "rev-list"; argv_boundary[1] = "--boundary"; argv_boundary[2] = "--pretty=oneline"; argv_boundary[argc + 2] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_boundary; rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) { unsigned char sha1[20]; if (buf.len > 0 && buf.buf[0] == '-') { write_or_die(bundle_fd, buf.buf, buf.len); if (!get_sha1_hex(buf.buf + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; add_pending_object(&revs, object, xstrdup(buf.buf)); } } else if (!get_sha1_hex(buf.buf, sha1)) { struct object *object = parse_object(sha1); object->flags |= SHOWN; } } strbuf_release(&buf); fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error("unrecognized argument: %s", argv[1]); object_array_remove_duplicates(&revs.pending); for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; char *ref; const char *display_ref; int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) continue; if (read_ref_full(e->name, sha1, 1, &flag)) flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; if (e->item->type == OBJ_TAG && !is_tag_in_date_range(e->item, &revs)) { e->item->flags |= UNINTERESTING; continue; } /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. * * Non commit objects such as tags and blobs do not have * this issue as they are not affected by those extra * constraints. */ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); free(ref); continue; } /* * If you run "git bundle create bndl v1.0..v2.0", the * name of the positive ref is "v2.0" but that is the * commit that is referenced by the tag, and not the tag * itself. */ if (hashcmp(sha1, e->item->sha1)) { /* * Is this the positive end of a range expressed * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ struct commit *one = lookup_commit_reference(sha1); struct object *obj; if (e->item == &(one->object)) { /* * Need to include e->name as an * independent ref to the pack-objects * input, so that the tag is included * in the output; otherwise we would * end up triggering "empty bundle" * error. */ obj = parse_object(sha1); obj->flags |= SHOWN; add_pending_object(&revs, obj, e->name); } free(ref); continue; } ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); free(ref); } if (!ref_count) die ("Refusing to create empty bundle."); /* end header */ write_or_die(bundle_fd, "\n", 1); /* write pack */ argv_pack[0] = "pack-objects"; argv_pack[1] = "--all-progress-implied"; argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; argv_pack[4] = "--delta-base-offset"; argv_pack[5] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_pack; rls.in = -1; rls.out = bundle_fd; rls.git_cmd = 1; if (start_command(&rls)) return error("Could not spawn pack-objects"); /* * start_command closed bundle_fd if it was > 1 * so set the lock fd to -1 so commit_lock_file() * won't fail trying to close it. */ lock.fd = -1; for (i = 0; i < revs.pending.nr; i++) { struct object *object = revs.pending.objects[i].item; if (object->flags & UNINTERESTING) write_or_die(rls.in, "^", 1); write_or_die(rls.in, sha1_to_hex(object->sha1), 40); write_or_die(rls.in, "\n", 1); } close(rls.in); if (finish_command(&rls)) return error ("pack-objects died"); if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno("cannot create '%s'", path); } return 0; }
static int run_pre_push_hook(struct transport *transport, struct ref *remote_refs) { int ret = 0, x; struct ref *r; struct child_process proc = CHILD_PROCESS_INIT; struct strbuf buf; const char *argv[4]; if (!(argv[0] = find_hook("pre-push"))) return 0; argv[1] = transport->remote->name; argv[2] = transport->url; argv[3] = NULL; proc.argv = argv; proc.in = -1; if (start_command(&proc)) { finish_command(&proc); return -1; } sigchain_push(SIGPIPE, SIG_IGN); strbuf_init(&buf, 256); for (r = remote_refs; r; r = r->next) { if (!r->peer_ref) continue; if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue; if (r->status == REF_STATUS_REJECT_STALE) continue; if (r->status == REF_STATUS_UPTODATE) continue; strbuf_reset(&buf); strbuf_addf( &buf, "%s %s %s %s\n", r->peer_ref->name, oid_to_hex(&r->new_oid), r->name, oid_to_hex(&r->old_oid)); if (write_in_full(proc.in, buf.buf, buf.len) < 0) { /* We do not mind if a hook does not read all refs. */ if (errno != EPIPE) ret = -1; break; } } strbuf_release(&buf); x = close(proc.in); if (!ret) ret = x; sigchain_pop(SIGPIPE); x = finish_command(&proc); if (!ret) ret = x; return ret; }