static const char *update_worktree(unsigned char *sha1) { const char *retval; const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : ".."; struct argv_array env = ARGV_ARRAY_INIT; if (is_bare_repository()) return "denyCurrentBranch = updateInstead needs a worktree"; argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir())); if (!find_hook(push_to_checkout_hook)) retval = push_to_deploy(sha1, &env, work_tree); else retval = push_to_checkout(sha1, &env, work_tree); argv_array_clear(&env); return retval; }
static void run_update_post_hook(struct command *commands) { struct command *cmd; int argc; const char **argv; struct child_process proc; 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) { 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); } }
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; }
static int add_worktree(const char *path, const char *refname, const struct add_opts *opts) { struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; const char *name; struct stat st; struct child_process cp = CHILD_PROCESS_INIT; struct argv_array child_env = ARGV_ARRAY_INIT; int counter = 0, len, ret; struct strbuf symref = STRBUF_INIT; struct commit *commit = NULL; int is_branch = 0; if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); /* is 'refname' a branch or commit? */ if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && ref_exists(symref.buf)) { is_branch = 1; if (!opts->force) die_if_checked_out(symref.buf, 0); } commit = lookup_commit_reference_by_name(refname); if (!commit) die(_("invalid reference: %s"), refname); name = worktree_basename(path, &len); git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name); len = sb_repo.len; if (safe_create_leading_directories_const(sb_repo.buf)) die_errno(_("could not create leading directories of '%s'"), sb_repo.buf); while (!stat(sb_repo.buf, &st)) { counter++; strbuf_setlen(&sb_repo, len); strbuf_addf(&sb_repo, "%d", counter); } name = strrchr(sb_repo.buf, '/') + 1; junk_pid = getpid(); atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); if (mkdir(sb_repo.buf, 0777)) die_errno(_("could not create directory of '%s'"), sb_repo.buf); junk_git_dir = xstrdup(sb_repo.buf); is_junk = 1; /* * lock the incomplete repo so prune won't delete it, unlock * after the preparation is over. */ strbuf_addf(&sb, "%s/locked", sb_repo.buf); if (!opts->keep_locked) write_file(sb.buf, "initializing"); else write_file(sb.buf, "added with --lock"); strbuf_addf(&sb_git, "%s/.git", path); if (safe_create_leading_directories_const(sb_git.buf)) die_errno(_("could not create leading directories of '%s'"), sb_git.buf); junk_work_tree = xstrdup(path); strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); write_file(sb.buf, "%s", real_path(sb_git.buf)); write_file(sb_git.buf, "gitdir: %s/worktrees/%s", real_path(get_git_common_dir()), name); /* * This is to keep resolve_ref() happy. We need a valid HEAD * or is_git_directory() will reject the directory. Any value which * looks like an object ID will do since it will be immediately * replaced by the symbolic-ref or update-ref invocation in the new * worktree. */ strbuf_reset(&sb); strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); write_file(sb.buf, "%s", sha1_to_hex(null_sha1)); strbuf_reset(&sb); strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name); argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); cp.git_cmd = 1; if (!is_branch) argv_array_pushl(&cp.args, "update-ref", "HEAD", oid_to_hex(&commit->object.oid), NULL); else argv_array_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL); cp.env = child_env.argv; ret = run_command(&cp); if (ret) goto done; if (opts->checkout) { cp.argv = NULL; argv_array_clear(&cp.args); argv_array_pushl(&cp.args, "reset", "--hard", NULL); cp.env = child_env.argv; ret = run_command(&cp); if (ret) goto done; } is_junk = 0; FREE_AND_NULL(junk_work_tree); FREE_AND_NULL(junk_git_dir); done: if (ret || !opts->keep_locked) { strbuf_reset(&sb); strbuf_addf(&sb, "%s/locked", sb_repo.buf); unlink_or_warn(sb.buf); } /* * Hook failure does not warrant worktree deletion, so run hook after * is_junk is cleared, but do return appropriate code when hook fails. */ if (!ret && opts->checkout) { const char *hook = find_hook("post-checkout"); if (hook) { const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL }; cp.git_cmd = 0; cp.no_stdin = 1; cp.stdout_to_stderr = 1; cp.dir = path; cp.env = env; cp.argv = NULL; argv_array_pushl(&cp.args, absolute_path(hook), oid_to_hex(&null_oid), oid_to_hex(&commit->object.oid), "1", NULL); ret = run_command(&cp); } } argv_array_clear(&child_env); strbuf_release(&sb); strbuf_release(&symref); strbuf_release(&sb_repo); strbuf_release(&sb_git); return ret; }
static int run_and_feed_hook(const char *hook_name, feed_fn feed, struct receive_hook_feed_state *feed_state) { struct child_process proc = CHILD_PROCESS_INIT; struct async muxer; const char *argv[2]; int code; argv[0] = find_hook(hook_name); if (!argv[0]) return 0; argv[1] = NULL; proc.argv = argv; proc.in = -1; proc.stdout_to_stderr = 1; if (feed_state->push_options) { int i; for (i = 0; i < feed_state->push_options->nr; i++) argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_%d=%s", i, feed_state->push_options->items[i].string); argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d", feed_state->push_options->nr); } else argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT"); if (use_sideband) { memset(&muxer, 0, sizeof(muxer)); muxer.proc = copy_to_sideband; muxer.in = -1; code = start_async(&muxer); if (code) return code; proc.err = muxer.in; } prepare_push_cert_sha1(&proc); code = start_command(&proc); if (code) { if (use_sideband) finish_async(&muxer); return code; } sigchain_push(SIGPIPE, SIG_IGN); while (1) { const char *buf; size_t n; if (feed(feed_state, &buf, &n)) break; if (write_in_full(proc.in, buf, n) != n) break; } close(proc.in); if (use_sideband) finish_async(&muxer); sigchain_pop(SIGPIPE); return finish_command(&proc); }