Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- builtin/worktree.c | 130 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 49 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index 8d59199..3ebb9e9 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -184,34 +184,15 @@ static const char *worktree_basename(const char *path, int *olen) return name; } -static int add_worktree(const char *path, const char *refname, - const struct add_opts *opts) +static void prepare_new_worktree(const char *path, + struct worktree *wt, + int create_gitdir_and_head) { + const char *name; 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; - struct argv_array child_env = ARGV_ARRAY_INIT; - int counter = 0, len, ret; - struct strbuf symref = STRBUF_INIT; - struct commit *commit = NULL; - - if (file_exists(path) && !is_empty_dir(path)) - die(_("'%s' already exists"), path); - - /* is 'refname' a branch or commit? */ - if (opts->force_new_branch) /* definitely a branch */ - ; - else if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && - ref_exists(symref.buf)) { /* it's a branch */ - if (!opts->force) - die_if_checked_out(symref.buf); - } else { /* must be a commit */ - commit = lookup_commit_reference_by_name(refname); - if (!commit) - die(_("invalid reference: %s"), refname); - } + int counter = 0, len; name = worktree_basename(path, &len); strbuf_addstr(&sb_repo, @@ -227,6 +208,10 @@ static int add_worktree(const char *path, const char *refname, } name = strrchr(sb_repo.buf, '/') + 1; + memset(wt, 0, sizeof(*wt)); + wt->path = xstrdup(path); + wt->id = xstrdup(name); + junk_pid = getpid(); atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); @@ -252,25 +237,81 @@ static int add_worktree(const char *path, const char *refname, 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, sha1_to_hex(null_sha1)); + if (create_gitdir_and_head) { + 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, 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); + strbuf_release(&sb); + strbuf_release(&sb_repo); + strbuf_release(&sb_git); +} + +static void new_worktree_complete(void) +{ + is_junk = 0; + free(junk_work_tree); + free(junk_git_dir); + junk_work_tree = NULL; + junk_git_dir = NULL; +} + +static void cleanup_new_worktree(struct worktree *wt) +{ + struct strbuf sb = STRBUF_INIT; + + strbuf_git_common_path(&sb, "worktrees/%s/locked", wt->id); + unlink_or_warn(sb.buf); + strbuf_release(&sb); + clear_worktree(wt); +} + +static int add_worktree(const char *path, const char *refname, + const struct add_opts *opts) +{ + struct worktree wt; + struct child_process cp; + struct argv_array child_env = ARGV_ARRAY_INIT; + int ret; + struct strbuf symref = STRBUF_INIT; + struct commit *commit = NULL; + + /* is 'refname' a branch or commit? */ + if (opts->force_new_branch) /* definitely a branch */ + ; + else if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && + ref_exists(symref.buf)) { /* it's a branch */ + if (!opts->force) + die_if_checked_out(symref.buf); + } else { /* must be a commit */ + commit = lookup_commit_reference_by_name(refname); + if (!commit) + die(_("invalid reference: %s"), refname); + } + + if (file_exists(path) && !is_empty_dir(path)) + die(_("'%s' already exists"), path); + + prepare_new_worktree(path, &wt, 1); - argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); + fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, wt.id); + + argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, + get_worktree_git_dir(&wt)); argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); memset(&cp, 0, sizeof(cp)); cp.git_cmd = 1; @@ -292,21 +333,12 @@ static int add_worktree(const char *path, const char *refname, cp.env = child_env.argv; ret = run_command(&cp); if (!ret) { - is_junk = 0; - free(junk_work_tree); - free(junk_git_dir); - junk_work_tree = NULL; - junk_git_dir = NULL; + new_worktree_complete(); } done: - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/locked", sb_repo.buf); - unlink_or_warn(sb.buf); argv_array_clear(&child_env); - strbuf_release(&sb); strbuf_release(&symref); - strbuf_release(&sb_repo); - strbuf_release(&sb_git); + cleanup_new_worktree(&wt); return ret; } -- 2.7.0.377.g4cd97dd -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html