{"thread":{"id":"39859","subject":"[PATCH v2 07/20] checkout: check_linked_checkout: simplify symref parsing","startedAt":"2015-07-16T08:20:05Z","lastAt":"2015-07-17T01:42:59Z","messageCount":25,"participants":["Eric Sunshine","Junio C Hamano","Duy Nguyen"],"isPatch":true,"patchVersion":2,"patchTotal":20},"messages":[{"id":"266189","messageId":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":null,"subject":"[PATCH v2 00/20] rid git-checkout of too-intimate knowledge of new worktree","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:05Z","receivedAt":"2015-07-16T08:20:05Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"This is v2 of [1] which rids git-checkout of the need to have\nspecialized knowledge that it's operating in a newly created linked\nworktree, and decouples git-worktree from git-checkout. Thanks to Duy\nand Junio for reviews.\n\nA v1 to v2 interdiff is included below.\n\nChanges since v1:\n\n* patch 03/20: relocate from caller to callee a comment explaining why\n  check_linked_checkout() resolves HEAD manually[2]\n\n* patch 06/20 (new): drop \"/.git\" from \"'blorp' already checked out at\n  '/foo/bar/.git'\" diagnostic[3]\n\n* patch 07/20 & 08/20 (new): teach checked_linked_checkout() about\n  symbolic-link HEAD[2] (to augment its existing symref-style HEAD\n  knowledge)\n\n* patch 14/20 (new): take advantage of 'struct child_process.env' to\n  make it obvious that assigned environment variables are intended for\n  child process invocations[2]\n\n* patch 16/20: side-step potential problem with future pluggable\n  backends answering ref_exists() incorrectly from cached information\n  after we've invoked git-branch behind their backs[4,5], and add a few\n  in-code comments explaining the logic\n\n* reword several commit messages to avoid stressing \"git reset --hard\"\n  as endgame, and instead emphasize removal of specialized worktree\n  creation knowledge from git-checkout[2], and resolve conflation of new\n  branch creation, setting of worktree HEAD, and new worktree\n  population.\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/273856\n[2]: http://article.gmane.org/gmane.comp.version-control.git/273921\n[3]: http://thread.gmane.org/gmane.comp.version-control.git/274001\n[4]: http://article.gmane.org/gmane.comp.version-control.git/273885\n[5]: http://article.gmane.org/gmane.comp.version-control.git/273901\n\nEric Sunshine (20):\n  checkout: avoid resolving HEAD unnecessarily\n  checkout: name check_linked_checkouts() more meaningfully\n  checkout: improve die_if_checked_out() robustness\n  checkout: die_if_checked_out: simplify strbuf management\n  checkout: generalize die_if_checked_out() branch name argument\n  checkout: check_linked_checkout: improve \"already checked out\"\n    aesthetic\n  checkout: check_linked_checkout: simplify symref parsing\n  checkout: teach check_linked_checkout() about symbolic link HEAD\n  branch: publish die_if_checked_out()\n  worktree: simplify new branch (-b/-B) option checking\n  worktree: introduce options container\n  worktree: make --detach mutually exclusive with -b/-B\n  worktree: make branch creation distinct from worktree population\n  worktree: elucidate environment variables intended for child processes\n  worktree: add_worktree: construct worktree-population command locally\n  worktree: detect branch-name/detached and error conditions locally\n  worktree: make setup of new HEAD distinct from worktree population\n  worktree: avoid resolving HEAD unnecessarily\n  worktree: populate via \"git reset --hard\" rather than \"git checkout\"\n  checkout: drop intimate knowledge of newly created worktree\n\n branch.c                |  67 +++++++++++++++++++++++++++\n branch.h                |   7 +++\n builtin/checkout.c      |  82 +++------------------------------\n builtin/worktree.c      | 120 ++++++++++++++++++++++++++++++++----------------\n t/t2025-worktree-add.sh |   8 ++++\n 5 files changed, 169 insertions(+), 115 deletions(-)\n\n-- \n2.5.0.rc2.378.g0af52e8\n\n\n---- 8< ----\ndiff --git a/branch.c b/branch.c\nindex 7b8b9a3..dfd7698 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -315,22 +315,28 @@ static void check_linked_checkout(const char *branch, const char *id)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf gitdir = STRBUF_INIT;\n-\tconst char *start, *end;\n \n+\t/*\n+\t * $GIT_COMMON_DIR/HEAD is practically outside\n+\t * $GIT_DIR so resolve_ref_unsafe() won't work (it\n+\t * uses git_path). Parse the ref ourselves.\n+\t */\n \tif (id)\n \t\tstrbuf_addf(&path, \"%s/worktrees/%s/HEAD\", get_git_common_dir(), id);\n \telse\n \t\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\n \n-\tif (strbuf_read_file(&sb, path.buf, 0) < 0 ||\n-\t    !skip_prefix(sb.buf, \"ref:\", &start))\n+\tif (!strbuf_readlink(&sb, path.buf, 0)) {\n+\t\tif (!starts_with(sb.buf, \"refs/\") ||\n+\t\t    check_refname_format(sb.buf, 0))\n+\t\t\tgoto done;\n+\t} else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n+\t    starts_with(sb.buf, \"ref:\")) {\n+\t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n+\t\tstrbuf_trim(&sb);\n+\t} else\n \t\tgoto done;\n-\twhile (isspace(*start))\n-\t\tstart++;\n-\tend = start;\n-\twhile (*end && !isspace(*end))\n-\t\tend++;\n-\tif (strncmp(start, branch, end - start) || branch[end - start] != '\\0')\n+\tif (strcmp(sb.buf, branch))\n \t\tgoto done;\n \tif (id) {\n \t\tstrbuf_reset(&path);\n@@ -341,6 +347,7 @@ static void check_linked_checkout(const char *branch, const char *id)\n \t} else\n \t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n \tskip_prefix(branch, \"refs/heads/\", &branch);\n+\tstrbuf_strip_suffix(&gitdir, \"/.git\");\n \tdie(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n done:\n \tstrbuf_release(&path);\n@@ -354,11 +361,6 @@ void die_if_checked_out(const char *branch)\n \tDIR *dir;\n \tstruct dirent *d;\n \n-\t/*\n-\t * $GIT_COMMON_DIR/HEAD is practically outside\n-\t * $GIT_DIR so resolve_ref_unsafe() won't work (it\n-\t * uses git_path). Parse the ref ourselves.\n-\t */\n \tcheck_linked_checkout(branch, NULL);\n \n \tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 51c57bc..2873064 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -188,6 +188,7 @@ static int add_worktree(const char *path, const char *refname,\n \tconst char *name;\n \tstruct stat st;\n \tstruct child_process cp;\n+\tstruct argv_array child_env = ARGV_ARRAY_INIT;\n \tint counter = 0, len, ret;\n \tstruct strbuf symref = STRBUF_INIT;\n \tstruct commit *commit = NULL;\n@@ -195,11 +196,14 @@ static int add_worktree(const char *path, const char *refname,\n \tif (file_exists(path) && !is_empty_dir(path))\n \t\tdie(_(\"'%s' already exists\"), path);\n \n-\tif (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&\n-\t    ref_exists(symref.buf)) {\n+\t/* is 'refname' a branch or commit? */\n+\tif (opts->force_new_branch) /* definitely a branch */\n+\t\t;\n+\telse if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&\n+\t\t ref_exists(symref.buf)) { /* it's a branch */\n \t\tif (!opts->force)\n \t\t\tdie_if_checked_out(symref.buf);\n-\t} else {\n+\t} else { /* must be a commit */\n \t\tcommit = lookup_commit_reference_by_name(refname);\n \t\tif (!commit)\n \t\t\tdie(_(\"invalid reference: %s\"), refname);\n@@ -262,8 +266,8 @@ static int add_worktree(const char *path, const char *refname,\n \n \tfprintf_ln(stderr, _(\"Enter %s (identifier %s)\"), path, name);\n \n-\tsetenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);\n-\tsetenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);\n+\targv_array_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n+\targv_array_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tmemset(&cp, 0, sizeof(cp));\n \tcp.git_cmd = 1;\n \n@@ -273,6 +277,7 @@ static int add_worktree(const char *path, const char *refname,\n \telse\n \t\targv_array_pushl(&cp.args, \"symbolic-ref\", \"HEAD\",\n \t\t\t\t symref.buf, NULL);\n+\tcp.env = child_env.argv;\n \tret = run_command(&cp);\n \tif (ret)\n \t\tgoto done;\n@@ -280,6 +285,7 @@ static int add_worktree(const char *path, const char *refname,\n \tcp.argv = NULL;\n \targv_array_clear(&cp.args);\n \targv_array_pushl(&cp.args, \"reset\", \"--hard\", NULL);\n+\tcp.env = child_env.argv;\n \tret = run_command(&cp);\n \tif (!ret) {\n \t\tis_junk = 0;\n@@ -292,6 +298,7 @@ done:\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/locked\", sb_repo.buf);\n \tunlink_or_warn(sb.buf);\n+\targv_array_clear(&child_env);\n \tstrbuf_release(&sb);\n \tstrbuf_release(&symref);\n \tstrbuf_release(&sb_repo);\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex ead8aa2..9e30690 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -83,6 +83,14 @@ test_expect_success 'die the same branch is already checked out' '\n \t)\n '\n \n+test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '\n+\thead=$(git -C there rev-parse --git-path HEAD) &&\n+\tref=$(git -C there symbolic-ref HEAD) &&\n+\trm \"$head\" &&\n+\tln -s \"$ref\" \"$head\" &&\n+\ttest_must_fail git -C here checkout newmaster\n+'\n+\n test_expect_success 'not die the same branch is already checked out' '\n \t(\n \t\tcd here &&\n---- 8< ----\n"},{"id":"266175","messageId":"1437034825-32054-2-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 01/20] checkout: avoid resolving HEAD unnecessarily","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:06Z","receivedAt":"2015-07-16T08:20:06Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"When --ignore-other-worktree is specified, we unconditionally skip the\ncheck to see if the requested branch is already checked out in a linked\nworktree. Since we know that we will be skipping that check, there is no\nneed to resolve HEAD in order to detect other conditions under which we\nmay skip the check.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/checkout.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5754554..75f90a9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1145,13 +1145,13 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new->name);\n \n-\tif (new->path && !opts->force_detach && !opts->new_branch) {\n+\tif (new->path && !opts->force_detach && !opts->new_branch &&\n+\t    !opts->ignore_other_worktrees) {\n \t\tunsigned char sha1[20];\n \t\tint flag;\n \t\tchar *head_ref = resolve_refdup(\"HEAD\", 0, sha1, &flag);\n \t\tif (head_ref &&\n-\t\t    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&\n-\t\t    !opts->ignore_other_worktrees)\n+\t\t    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))\n \t\t\tcheck_linked_checkouts(new);\n \t\tfree(head_ref);\n \t}\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266191","messageId":"1437034825-32054-3-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 02/20] checkout: name check_linked_checkouts() more meaningfully","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:07Z","receivedAt":"2015-07-16T08:20:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"check_linked_checkouts() doesn't just \"check\" linked checkouts for\n\"something\"; specifically, it aborts the operation if the branch about\nto be checked out is already checked out elsewhere. Therefore, rename it\nto die_if_checked_out() to give a better indication of its function.\nThe more meaningful name will be particularly important when this\nfunction is later published for use by other callers.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/checkout.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 75f90a9..e75fb5e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -910,7 +910,7 @@ done:\n \tstrbuf_release(&gitdir);\n }\n \n-static void check_linked_checkouts(struct branch_info *new)\n+static void die_if_checked_out(struct branch_info *new)\n {\n \tstruct strbuf path = STRBUF_INIT;\n \tDIR *dir;\n@@ -1152,7 +1152,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tchar *head_ref = resolve_refdup(\"HEAD\", 0, sha1, &flag);\n \t\tif (head_ref &&\n \t\t    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))\n-\t\t\tcheck_linked_checkouts(new);\n+\t\t\tdie_if_checked_out(new);\n \t\tfree(head_ref);\n \t}\n \n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266192","messageId":"1437034825-32054-4-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 03/20] checkout: improve die_if_checked_out() robustness","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:08Z","receivedAt":"2015-07-16T08:20:08Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"die_if_checked_out() is intended to check if the branch about to be\nchecked out is already checked out either in the main worktree or in a\nlinked worktree. However, if .git/worktrees directory does not exist,\nthen it never bothers checking the main worktree, even though the\nspecified branch might indeed be checked out there, which is fragile\nbehavior.\n\nThis hasn't been a problem in practice since the current implementation\nof \"git worktree add\" (and, earlier, \"git checkout --to\") always creates\n.git/worktrees before die_if_checked_out() is called by the child \"git\ncheckout\" invocation which populates the new worktree.\n\nHowever, git-worktree will eventually want to call die_if_checked_out()\nitself rather than only doing so indirectly as a side-effect of invoking\ngit-checkout, and reliance upon order of operations (creating\n.git/worktrees before checking if a branch is already checked out) is\nfragile. As a general function, callers should not be expected to abide\nby this undocumented and unwarranted restriction. Therefore, make\ndie_if_checked_out() more robust by checking the main worktree whether\n.git/worktrees exists or not.\n\nWhile here, also move a comment explaining why die_if_checked_out()'s\nhelper parses HEAD manually. Such information resides more naturally\nwith the helper itself rather than at its first point of call.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: relocate comment; reword commit message.\n\n builtin/checkout.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex e75fb5e..1992c41 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -880,6 +880,11 @@ static void check_linked_checkout(struct branch_info *new, const char *id)\n \tstruct strbuf gitdir = STRBUF_INIT;\n \tconst char *start, *end;\n \n+\t/*\n+\t * $GIT_COMMON_DIR/HEAD is practically outside\n+\t * $GIT_DIR so resolve_ref_unsafe() won't work (it\n+\t * uses git_path). Parse the ref ourselves.\n+\t */\n \tif (id)\n \t\tstrbuf_addf(&path, \"%s/worktrees/%s/HEAD\", get_git_common_dir(), id);\n \telse\n@@ -916,19 +921,14 @@ static void die_if_checked_out(struct branch_info *new)\n \tDIR *dir;\n \tstruct dirent *d;\n \n+\tcheck_linked_checkout(new, NULL);\n+\n \tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n \tif ((dir = opendir(path.buf)) == NULL) {\n \t\tstrbuf_release(&path);\n \t\treturn;\n \t}\n \n-\t/*\n-\t * $GIT_COMMON_DIR/HEAD is practically outside\n-\t * $GIT_DIR so resolve_ref_unsafe() won't work (it\n-\t * uses git_path). Parse the ref ourselves.\n-\t */\n-\tcheck_linked_checkout(new, NULL);\n-\n \twhile ((d = readdir(dir)) != NULL) {\n \t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n \t\t\tcontinue;\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266193","messageId":"1437034825-32054-5-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 04/20] checkout: die_if_checked_out: simplify strbuf management","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:09Z","receivedAt":"2015-07-16T08:20:09Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"There is no reason to keep the strbuf active long after its last use.\nBy releasing it as early as possible, resource management is simplified\nand there is less worry about future changes resulting in a leak.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/checkout.c | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1992c41..c36bf8a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -924,17 +924,16 @@ static void die_if_checked_out(struct branch_info *new)\n \tcheck_linked_checkout(new, NULL);\n \n \tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n-\tif ((dir = opendir(path.buf)) == NULL) {\n-\t\tstrbuf_release(&path);\n+\tdir = opendir(path.buf);\n+\tstrbuf_release(&path);\n+\tif (!dir)\n \t\treturn;\n-\t}\n \n \twhile ((d = readdir(dir)) != NULL) {\n \t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n \t\t\tcontinue;\n \t\tcheck_linked_checkout(new, d->d_name);\n \t}\n-\tstrbuf_release(&path);\n \tclosedir(dir);\n }\n \n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266194","messageId":"1437034825-32054-6-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 05/20] checkout: generalize die_if_checked_out() branch name argument","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:10Z","receivedAt":"2015-07-16T08:20:10Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"The plan is to publish die_if_checked_out() so that callers other than\ngit-checkout can take advantage of it, however, those callers won't have\naccess to git-checkout's \"struct branch_info\". Therefore, change it to\naccept the full name of the branch as a simple string instead.\n\nWhile here, also give the argument a more meaningful name (\"branch\"\ninstead of \"new\").\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/checkout.c | 15 ++++++++-------\n 1 file changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c36bf8a..177ad6a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -873,7 +873,7 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)\n \treturn NULL;\n }\n \n-static void check_linked_checkout(struct branch_info *new, const char *id)\n+static void check_linked_checkout(const char *branch, const char *id)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf path = STRBUF_INIT;\n@@ -898,7 +898,7 @@ static void check_linked_checkout(struct branch_info *new, const char *id)\n \tend = start;\n \twhile (*end && !isspace(*end))\n \t\tend++;\n-\tif (strncmp(start, new->path, end - start) || new->path[end - start] != '\\0')\n+\tif (strncmp(start, branch, end - start) || branch[end - start] != '\\0')\n \t\tgoto done;\n \tif (id) {\n \t\tstrbuf_reset(&path);\n@@ -908,20 +908,21 @@ static void check_linked_checkout(struct branch_info *new, const char *id)\n \t\tstrbuf_rtrim(&gitdir);\n \t} else\n \t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n-\tdie(_(\"'%s' is already checked out at '%s'\"), new->name, gitdir.buf);\n+\tskip_prefix(branch, \"refs/heads/\", &branch);\n+\tdie(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n done:\n \tstrbuf_release(&path);\n \tstrbuf_release(&sb);\n \tstrbuf_release(&gitdir);\n }\n \n-static void die_if_checked_out(struct branch_info *new)\n+static void die_if_checked_out(const char *branch)\n {\n \tstruct strbuf path = STRBUF_INIT;\n \tDIR *dir;\n \tstruct dirent *d;\n \n-\tcheck_linked_checkout(new, NULL);\n+\tcheck_linked_checkout(branch, NULL);\n \n \tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n \tdir = opendir(path.buf);\n@@ -932,7 +933,7 @@ static void die_if_checked_out(struct branch_info *new)\n \twhile ((d = readdir(dir)) != NULL) {\n \t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n \t\t\tcontinue;\n-\t\tcheck_linked_checkout(new, d->d_name);\n+\t\tcheck_linked_checkout(branch, d->d_name);\n \t}\n \tclosedir(dir);\n }\n@@ -1151,7 +1152,7 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tchar *head_ref = resolve_refdup(\"HEAD\", 0, sha1, &flag);\n \t\tif (head_ref &&\n \t\t    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))\n-\t\t\tdie_if_checked_out(new);\n+\t\t\tdie_if_checked_out(new->path);\n \t\tfree(head_ref);\n \t}\n \n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266190","messageId":"1437034825-32054-7-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 06/20] checkout: check_linked_checkout: improve \"already checked out\" aesthetic","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:11Z","receivedAt":"2015-07-16T08:20:11Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"When check_linked_checkout() discovers that the branch is already\nchecked out elsewhere, it emits the diagnostic:\n\n    'blorp' is already checked out at '/some/path/.git'\n\nwhich is mildly misleading and a bit unsightly due to the trailing\n\"/.git\". For the user, \"/some/path\" is significant, whereas \"/.git\" is\nmere noise, so drop it.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNew in v2.\n\n builtin/checkout.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 177ad6a..a331345 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -909,6 +909,7 @@ static void check_linked_checkout(const char *branch, const char *id)\n \t} else\n \t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n \tskip_prefix(branch, \"refs/heads/\", &branch);\n+\tstrbuf_strip_suffix(&gitdir, \"/.git\");\n \tdie(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n done:\n \tstrbuf_release(&path);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266174","messageId":"1437034825-32054-8-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 07/20] checkout: check_linked_checkout: simplify symref parsing","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:12Z","receivedAt":"2015-07-16T08:20:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"check_linked_checkout() only understands symref-style HEAD (i.e. \"ref:\nrefs/heads/master\"), however, HEAD may also be a an actual symbolic link\n(on platforms which support it), thus it will need to check that style\nHEAD, as well (via readlink()). As a preparatory step, simplify parsing\nof symref-style HEAD so the actual branch check can be re-used easily\nfor symbolic links (in an upcoming patch).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNew in v2: code cleanup in preparation for new patch 08/20.\n\n builtin/checkout.c | 15 ++++++---------\n 1 file changed, 6 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a331345..3487712 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -878,7 +878,6 @@ static void check_linked_checkout(const char *branch, const char *id)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf gitdir = STRBUF_INIT;\n-\tconst char *start, *end;\n \n \t/*\n \t * $GIT_COMMON_DIR/HEAD is practically outside\n@@ -890,15 +889,13 @@ static void check_linked_checkout(const char *branch, const char *id)\n \telse\n \t\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\n \n-\tif (strbuf_read_file(&sb, path.buf, 0) < 0 ||\n-\t    !skip_prefix(sb.buf, \"ref:\", &start))\n+\tif (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n+\t    starts_with(sb.buf, \"ref:\")) {\n+\t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n+\t\tstrbuf_trim(&sb);\n+\t} else\n \t\tgoto done;\n-\twhile (isspace(*start))\n-\t\tstart++;\n-\tend = start;\n-\twhile (*end && !isspace(*end))\n-\t\tend++;\n-\tif (strncmp(start, branch, end - start) || branch[end - start] != '\\0')\n+\tif (strcmp(sb.buf, branch))\n \t\tgoto done;\n \tif (id) {\n \t\tstrbuf_reset(&path);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266182","messageId":"1437034825-32054-9-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 08/20] checkout: teach check_linked_checkout() about symbolic link HEAD","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:13Z","receivedAt":"2015-07-16T08:20:13Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"check_linked_checkout() only understands symref-style HEAD (i.e. \"ref:\nrefs/heads/master\"), however, HEAD may also be a an actual symbolic link\n(on platforms which support it). To accurately detect if a branch is\nchecked out elsewhere, it needs to handle symbolic link HEAD, as well.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNew in v2.\n\nThe new test's title intentionally employs slightly odd grammar to match\nthe grammar of its brother test just above. Test title grammar could be\ncleaned up in a separate patch, but the current series is already overly\nlong.\n\n builtin/checkout.c      | 6 +++++-\n t/t2025-worktree-add.sh | 8 ++++++++\n 2 files changed, 13 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 3487712..3326aa8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -889,7 +889,11 @@ static void check_linked_checkout(const char *branch, const char *id)\n \telse\n \t\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\n \n-\tif (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n+\tif (!strbuf_readlink(&sb, path.buf, 0)) {\n+\t\tif (!starts_with(sb.buf, \"refs/\") ||\n+\t\t    check_refname_format(sb.buf, 0))\n+\t\t\tgoto done;\n+\t} else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n \t    starts_with(sb.buf, \"ref:\")) {\n \t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n \t\tstrbuf_trim(&sb);\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex ead8aa2..9e30690 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -83,6 +83,14 @@ test_expect_success 'die the same branch is already checked out' '\n \t)\n '\n \n+test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '\n+\thead=$(git -C there rev-parse --git-path HEAD) &&\n+\tref=$(git -C there symbolic-ref HEAD) &&\n+\trm \"$head\" &&\n+\tln -s \"$ref\" \"$head\" &&\n+\ttest_must_fail git -C here checkout newmaster\n+'\n+\n test_expect_success 'not die the same branch is already checked out' '\n \t(\n \t\tcd here &&\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266186","messageId":"1437034825-32054-10-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 09/20] branch: publish die_if_checked_out()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:14Z","receivedAt":"2015-07-16T08:20:14Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"git-worktree currently conflates new branch creation, setting of HEAD in\nthe new wortkree, and worktree population into a single sub-invocation\nof git-checkout. However, these operations will eventually be separated,\nand git-worktree itself will need to be able to detect if the branch is\nalready checked out elsewhere, rather than relying upon git-branch to\nmake this determination, so publish die_if_checked_out().\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: reword commit message.\n\n branch.c           | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n branch.h           |  7 ++++++\n builtin/checkout.c | 67 ------------------------------------------------------\n 3 files changed, 74 insertions(+), 67 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 4bab55a..dfd7698 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -309,3 +309,70 @@ void remove_branch_state(void)\n \tunlink(git_path(\"MERGE_MODE\"));\n \tunlink(git_path(\"SQUASH_MSG\"));\n }\n+\n+static void check_linked_checkout(const char *branch, const char *id)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf gitdir = STRBUF_INIT;\n+\n+\t/*\n+\t * $GIT_COMMON_DIR/HEAD is practically outside\n+\t * $GIT_DIR so resolve_ref_unsafe() won't work (it\n+\t * uses git_path). Parse the ref ourselves.\n+\t */\n+\tif (id)\n+\t\tstrbuf_addf(&path, \"%s/worktrees/%s/HEAD\", get_git_common_dir(), id);\n+\telse\n+\t\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\n+\n+\tif (!strbuf_readlink(&sb, path.buf, 0)) {\n+\t\tif (!starts_with(sb.buf, \"refs/\") ||\n+\t\t    check_refname_format(sb.buf, 0))\n+\t\t\tgoto done;\n+\t} else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n+\t    starts_with(sb.buf, \"ref:\")) {\n+\t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n+\t\tstrbuf_trim(&sb);\n+\t} else\n+\t\tgoto done;\n+\tif (strcmp(sb.buf, branch))\n+\t\tgoto done;\n+\tif (id) {\n+\t\tstrbuf_reset(&path);\n+\t\tstrbuf_addf(&path, \"%s/worktrees/%s/gitdir\", get_git_common_dir(), id);\n+\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n+\t\t\tgoto done;\n+\t\tstrbuf_rtrim(&gitdir);\n+\t} else\n+\t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n+\tskip_prefix(branch, \"refs/heads/\", &branch);\n+\tstrbuf_strip_suffix(&gitdir, \"/.git\");\n+\tdie(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n+done:\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&sb);\n+\tstrbuf_release(&gitdir);\n+}\n+\n+void die_if_checked_out(const char *branch)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tDIR *dir;\n+\tstruct dirent *d;\n+\n+\tcheck_linked_checkout(branch, NULL);\n+\n+\tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n+\tdir = opendir(path.buf);\n+\tstrbuf_release(&path);\n+\tif (!dir)\n+\t\treturn;\n+\n+\twhile ((d = readdir(dir)) != NULL) {\n+\t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n+\t\t\tcontinue;\n+\t\tcheck_linked_checkout(branch, d->d_name);\n+\t}\n+\tclosedir(dir);\n+}\ndiff --git a/branch.h b/branch.h\nindex 64173ab..58aa45f 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -52,4 +52,11 @@ extern void install_branch_config(int flag, const char *local, const char *origi\n  */\n extern int read_branch_desc(struct strbuf *, const char *branch_name);\n \n+/*\n+ * Check if a branch is checked out in the main worktree or any linked\n+ * worktree and die (with a message describing its checkout location) if\n+ * it is.\n+ */\n+extern void die_if_checked_out(const char *branch);\n+\n #endif\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 3326aa8..4ae895c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -873,73 +873,6 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)\n \treturn NULL;\n }\n \n-static void check_linked_checkout(const char *branch, const char *id)\n-{\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tstruct strbuf path = STRBUF_INIT;\n-\tstruct strbuf gitdir = STRBUF_INIT;\n-\n-\t/*\n-\t * $GIT_COMMON_DIR/HEAD is practically outside\n-\t * $GIT_DIR so resolve_ref_unsafe() won't work (it\n-\t * uses git_path). Parse the ref ourselves.\n-\t */\n-\tif (id)\n-\t\tstrbuf_addf(&path, \"%s/worktrees/%s/HEAD\", get_git_common_dir(), id);\n-\telse\n-\t\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\n-\n-\tif (!strbuf_readlink(&sb, path.buf, 0)) {\n-\t\tif (!starts_with(sb.buf, \"refs/\") ||\n-\t\t    check_refname_format(sb.buf, 0))\n-\t\t\tgoto done;\n-\t} else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n-\t    starts_with(sb.buf, \"ref:\")) {\n-\t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n-\t\tstrbuf_trim(&sb);\n-\t} else\n-\t\tgoto done;\n-\tif (strcmp(sb.buf, branch))\n-\t\tgoto done;\n-\tif (id) {\n-\t\tstrbuf_reset(&path);\n-\t\tstrbuf_addf(&path, \"%s/worktrees/%s/gitdir\", get_git_common_dir(), id);\n-\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n-\t\t\tgoto done;\n-\t\tstrbuf_rtrim(&gitdir);\n-\t} else\n-\t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n-\tskip_prefix(branch, \"refs/heads/\", &branch);\n-\tstrbuf_strip_suffix(&gitdir, \"/.git\");\n-\tdie(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n-done:\n-\tstrbuf_release(&path);\n-\tstrbuf_release(&sb);\n-\tstrbuf_release(&gitdir);\n-}\n-\n-static void die_if_checked_out(const char *branch)\n-{\n-\tstruct strbuf path = STRBUF_INIT;\n-\tDIR *dir;\n-\tstruct dirent *d;\n-\n-\tcheck_linked_checkout(branch, NULL);\n-\n-\tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n-\tdir = opendir(path.buf);\n-\tstrbuf_release(&path);\n-\tif (!dir)\n-\t\treturn;\n-\n-\twhile ((d = readdir(dir)) != NULL) {\n-\t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n-\t\t\tcontinue;\n-\t\tcheck_linked_checkout(branch, d->d_name);\n-\t}\n-\tclosedir(dir);\n-}\n-\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new,\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266188","messageId":"1437034825-32054-11-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 10/20] worktree: simplify new branch (-b/-B) option checking","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:15Z","receivedAt":"2015-07-16T08:20:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Make 'new_branch' be the name of the new branch for both forced and\nnon-forced cases; and add boolean 'force_new_branch' to indicate forced\nbranch creation. This will simplify logic later on when git-worktree\nhandles branch creation locally rather than delegating it to\ngit-checkout as part of the worktree population phase.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/worktree.c | 13 ++++++++-----\n 1 file changed, 8 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 69248ba..c267410 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -272,7 +272,7 @@ static int add_worktree(const char *path, const char **child_argv)\n \n static int add(int ac, const char **av, const char *prefix)\n {\n-\tint force = 0, detach = 0;\n+\tint force = 0, detach = 0, force_new_branch;\n \tconst char *new_branch = NULL, *new_branch_force = NULL;\n \tconst char *path, *branch;\n \tstruct argv_array cmd = ARGV_ARRAY_INIT;\n@@ -295,7 +295,11 @@ static int add(int ac, const char **av, const char *prefix)\n \tpath = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];\n \tbranch = ac < 2 ? \"HEAD\" : av[1];\n \n-\tif (ac < 2 && !new_branch && !new_branch_force) {\n+\tforce_new_branch = !!new_branch_force;\n+\tif (force_new_branch)\n+\t\tnew_branch = new_branch_force;\n+\n+\tif (ac < 2 && !new_branch) {\n \t\tint n;\n \t\tconst char *s = worktree_basename(path, &n);\n \t\tnew_branch = xstrndup(s, n);\n@@ -305,9 +309,8 @@ static int add(int ac, const char **av, const char *prefix)\n \tif (force)\n \t\targv_array_push(&cmd, \"--ignore-other-worktrees\");\n \tif (new_branch)\n-\t\targv_array_pushl(&cmd, \"-b\", new_branch, NULL);\n-\tif (new_branch_force)\n-\t\targv_array_pushl(&cmd, \"-B\", new_branch_force, NULL);\n+\t\targv_array_pushl(&cmd, force_new_branch ? \"-B\" : \"-b\",\n+\t\t\t\t new_branch, NULL);\n \tif (detach)\n \t\targv_array_push(&cmd, \"--detach\");\n \targv_array_push(&cmd, branch);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266185","messageId":"1437034825-32054-12-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 11/20] worktree: introduce options container","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:16Z","receivedAt":"2015-07-16T08:20:16Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"add_worktree() will eventually need to deal with some options itself, so\nintroduce a structure into which options can be conveniently bundled,\nand pass it along to add_worktree().\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/worktree.c | 45 +++++++++++++++++++++++++++------------------\n 1 file changed, 27 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex c267410..253382a 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -12,6 +12,13 @@ static const char * const worktree_usage[] = {\n \tNULL\n };\n \n+struct add_opts {\n+\tint force;\n+\tint detach;\n+\tconst char *new_branch;\n+\tint force_new_branch;\n+};\n+\n static int show_only;\n static int verbose;\n static unsigned long expire;\n@@ -171,7 +178,8 @@ static const char *worktree_basename(const char *path, int *olen)\n \treturn name;\n }\n \n-static int add_worktree(const char *path, const char **child_argv)\n+static int add_worktree(const char *path, const char **child_argv,\n+\t\t\tconst struct add_opts *opts)\n {\n \tstruct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -272,22 +280,23 @@ static int add_worktree(const char *path, const char **child_argv)\n \n static int add(int ac, const char **av, const char *prefix)\n {\n-\tint force = 0, detach = 0, force_new_branch;\n-\tconst char *new_branch = NULL, *new_branch_force = NULL;\n+\tstruct add_opts opts;\n+\tconst char *new_branch_force = NULL;\n \tconst char *path, *branch;\n \tstruct argv_array cmd = ARGV_ARRAY_INIT;\n \tstruct option options[] = {\n-\t\tOPT__FORCE(&force, N_(\"checkout <branch> even if already checked out in other worktree\")),\n-\t\tOPT_STRING('b', NULL, &new_branch, N_(\"branch\"),\n+\t\tOPT__FORCE(&opts.force, N_(\"checkout <branch> even if already checked out in other worktree\")),\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create a new branch\")),\n \t\tOPT_STRING('B', NULL, &new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create or reset a branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts.detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_END()\n \t};\n \n+\tmemset(&opts, 0, sizeof(opts));\n \tac = parse_options(ac, av, prefix, options, worktree_usage, 0);\n-\tif (new_branch && new_branch_force)\n+\tif (opts.new_branch && new_branch_force)\n \t\tdie(_(\"-b and -B are mutually exclusive\"));\n \tif (ac < 1 || ac > 2)\n \t\tusage_with_options(worktree_usage, options);\n@@ -295,27 +304,27 @@ static int add(int ac, const char **av, const char *prefix)\n \tpath = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];\n \tbranch = ac < 2 ? \"HEAD\" : av[1];\n \n-\tforce_new_branch = !!new_branch_force;\n-\tif (force_new_branch)\n-\t\tnew_branch = new_branch_force;\n+\topts.force_new_branch = !!new_branch_force;\n+\tif (opts.force_new_branch)\n+\t\topts.new_branch = new_branch_force;\n \n-\tif (ac < 2 && !new_branch) {\n+\tif (ac < 2 && !opts.new_branch) {\n \t\tint n;\n \t\tconst char *s = worktree_basename(path, &n);\n-\t\tnew_branch = xstrndup(s, n);\n+\t\topts.new_branch = xstrndup(s, n);\n \t}\n \n \targv_array_push(&cmd, \"checkout\");\n-\tif (force)\n+\tif (opts.force)\n \t\targv_array_push(&cmd, \"--ignore-other-worktrees\");\n-\tif (new_branch)\n-\t\targv_array_pushl(&cmd, force_new_branch ? \"-B\" : \"-b\",\n-\t\t\t\t new_branch, NULL);\n-\tif (detach)\n+\tif (opts.new_branch)\n+\t\targv_array_pushl(&cmd, opts.force_new_branch ? \"-B\" : \"-b\",\n+\t\t\t\t opts.new_branch, NULL);\n+\tif (opts.detach)\n \t\targv_array_push(&cmd, \"--detach\");\n \targv_array_push(&cmd, branch);\n \n-\treturn add_worktree(path, cmd.argv);\n+\treturn add_worktree(path, cmd.argv, &opts);\n }\n \n int cmd_worktree(int ac, const char **av, const char *prefix)\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266187","messageId":"1437034825-32054-13-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 12/20] worktree: make --detach mutually exclusive with -b/-B","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:17Z","receivedAt":"2015-07-16T08:20:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Be consistent with git-checkout which disallows this (not particularly\nmeaningful) combination.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/worktree.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 253382a..cd06bf5 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -296,8 +296,8 @@ static int add(int ac, const char **av, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \tac = parse_options(ac, av, prefix, options, worktree_usage, 0);\n-\tif (opts.new_branch && new_branch_force)\n-\t\tdie(_(\"-b and -B are mutually exclusive\"));\n+\tif (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1)\n+\t\tdie(_(\"-b, -B, and --detach are mutually exclusive\"));\n \tif (ac < 1 || ac > 2)\n \t\tusage_with_options(worktree_usage, options);\n \n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266181","messageId":"1437034825-32054-14-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 13/20] worktree: make branch creation distinct from worktree population","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:18Z","receivedAt":"2015-07-16T08:20:18Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"git-worktree currently conflates branch creation, setting of HEAD in the\nnew worktree, and worktree population into a single sub-invocation of\ngit-checkout, which requires git-checkout to be specially aware that it\nis operating in a newly-created worktree. The goal is to free\ngit-checkout of that special knowledge, and to do so, git-worktree will\neventually perform those operations separately. Thus, as a first step,\nrather than piggybacking on git-checkout's -b/-B ability to create a new\nbranch at checkout time, make git-worktree responsible for branch\ncreation itself.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: reword commit message.\n\n builtin/worktree.c | 17 ++++++++++++++---\n 1 file changed, 14 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex cd06bf5..9be1c74 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -314,12 +314,23 @@ static int add(int ac, const char **av, const char *prefix)\n \t\topts.new_branch = xstrndup(s, n);\n \t}\n \n+\tif (opts.new_branch) {\n+\t\tstruct child_process cp;\n+\t\tmemset(&cp, 0, sizeof(cp));\n+\t\tcp.git_cmd = 1;\n+\t\targv_array_push(&cp.args, \"branch\");\n+\t\tif (opts.force_new_branch)\n+\t\t\targv_array_push(&cp.args, \"--force\");\n+\t\targv_array_push(&cp.args, opts.new_branch);\n+\t\targv_array_push(&cp.args, branch);\n+\t\tif (run_command(&cp))\n+\t\t\treturn -1;\n+\t\tbranch = opts.new_branch;\n+\t}\n+\n \targv_array_push(&cmd, \"checkout\");\n \tif (opts.force)\n \t\targv_array_push(&cmd, \"--ignore-other-worktrees\");\n-\tif (opts.new_branch)\n-\t\targv_array_pushl(&cmd, opts.force_new_branch ? \"-B\" : \"-b\",\n-\t\t\t\t opts.new_branch, NULL);\n \tif (opts.detach)\n \t\targv_array_push(&cmd, \"--detach\");\n \targv_array_push(&cmd, branch);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266179","messageId":"1437034825-32054-15-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 14/20] worktree: elucidate environment variables intended for child processes","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:19Z","receivedAt":"2015-07-16T08:20:19Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Take advantage of 'struct child_process.env' to make it obvious that\nenvironment variables set by add_worktree() are intended specifically\nfor sub-commands it invokes to operate in the new worktree.\n\nWe assign a local 'struct argv_array' to child_process.env, rather than\nutilizing the child_process.env_array 'struct argv_array', because\nfuture patches will make add_worktree() invoke additional sub-commands,\nand it's simpler to populate the environment array just once, whereas\nchild_process.env_array gets cleared after each invocation, thus would\nrequire re-population for each sub-command.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNew in v2.\n\n builtin/worktree.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 9be1c74..7ae186f 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -186,6 +186,7 @@ static int add_worktree(const char *path, const char **child_argv,\n \tconst char *name;\n \tstruct stat st;\n \tstruct child_process cp;\n+\tstruct argv_array child_env = ARGV_ARRAY_INIT;\n \tint counter = 0, len, ret;\n \tunsigned char rev[20];\n \n@@ -256,11 +257,12 @@ static int add_worktree(const char *path, const char **child_argv,\n \tfprintf_ln(stderr, _(\"Enter %s (identifier %s)\"), path, name);\n \n \tsetenv(\"GIT_CHECKOUT_NEW_WORKTREE\", \"1\", 1);\n-\tsetenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);\n-\tsetenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);\n+\targv_array_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n+\targv_array_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tmemset(&cp, 0, sizeof(cp));\n \tcp.git_cmd = 1;\n \tcp.argv = child_argv;\n+\tcp.env = child_env.argv;\n \tret = run_command(&cp);\n \tif (!ret) {\n \t\tis_junk = 0;\n@@ -272,6 +274,7 @@ static int add_worktree(const char *path, const char **child_argv,\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/locked\", sb_repo.buf);\n \tunlink_or_warn(sb.buf);\n+\targv_array_clear(&child_env);\n \tstrbuf_release(&sb);\n \tstrbuf_release(&sb_repo);\n \tstrbuf_release(&sb_git);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266177","messageId":"1437034825-32054-16-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 15/20] worktree: add_worktree: construct worktree-population command locally","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:20Z","receivedAt":"2015-07-16T08:20:20Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"The caller of add_worktree() provides it with a command to invoke to\npopulate the new worktree. This was a useful abstraction during the\nconversion of \"git checkout --to\" functionality to \"git worktree add\"\nsince git-checkout and git-worktree constructed the population command\ndifferently. However, now that \"git checkout --to\" has been retired, and\nadd_worktree() has access to the options given to \"worktree add\", this\nextra indirection is no longer useful and makes the code a bit\nconvoluted.\n\nMoreover, the eventual goal is for git-worktree to make setting of HEAD\nand worktree population distinct operations, whereas they are currently\nconflated into a single git-checkout invocation. As such, add_worktree()\nwill eventually invoke other commands in addition to the worktree\npopulation command, so it will be doing command construction itself\nanyhow.\n\nTherefore, relocate construction of the worktree population command from\nadd() to add_worktree().\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: reword commit message.\n\n builtin/worktree.c | 19 ++++++++-----------\n 1 file changed, 8 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 7ae186f..28095c1 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -178,7 +178,7 @@ static const char *worktree_basename(const char *path, int *olen)\n \treturn name;\n }\n \n-static int add_worktree(const char *path, const char **child_argv,\n+static int add_worktree(const char *path, const char *refname,\n \t\t\tconst struct add_opts *opts)\n {\n \tstruct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;\n@@ -261,7 +261,12 @@ static int add_worktree(const char *path, const char **child_argv,\n \targv_array_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tmemset(&cp, 0, sizeof(cp));\n \tcp.git_cmd = 1;\n-\tcp.argv = child_argv;\n+\targv_array_push(&cp.args, \"checkout\");\n+\tif (opts->force)\n+\t\targv_array_push(&cp.args, \"--ignore-other-worktrees\");\n+\tif (opts->detach)\n+\t\targv_array_push(&cp.args, \"--detach\");\n+\targv_array_push(&cp.args, refname);\n \tcp.env = child_env.argv;\n \tret = run_command(&cp);\n \tif (!ret) {\n@@ -286,7 +291,6 @@ static int add(int ac, const char **av, const char *prefix)\n \tstruct add_opts opts;\n \tconst char *new_branch_force = NULL;\n \tconst char *path, *branch;\n-\tstruct argv_array cmd = ARGV_ARRAY_INIT;\n \tstruct option options[] = {\n \t\tOPT__FORCE(&opts.force, N_(\"checkout <branch> even if already checked out in other worktree\")),\n \t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n@@ -331,14 +335,7 @@ static int add(int ac, const char **av, const char *prefix)\n \t\tbranch = opts.new_branch;\n \t}\n \n-\targv_array_push(&cmd, \"checkout\");\n-\tif (opts.force)\n-\t\targv_array_push(&cmd, \"--ignore-other-worktrees\");\n-\tif (opts.detach)\n-\t\targv_array_push(&cmd, \"--detach\");\n-\targv_array_push(&cmd, branch);\n-\n-\treturn add_worktree(path, cmd.argv, &opts);\n+\treturn add_worktree(path, branch, &opts);\n }\n \n int cmd_worktree(int ac, const char **av, const char *prefix)\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266176","messageId":"1437034825-32054-17-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 16/20] worktree: detect branch-name/detached and error conditions locally","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:21Z","receivedAt":"2015-07-16T08:20:21Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"git-worktree currently conflates setting of HEAD in the new worktree\nwith initial worktree population via a single git-checkout invocation,\nwhich requires git-checkout to have special knowledge that it is\noperating in a newly created worktree. The eventual goal is to separate\nthese operations and rid git-checkout of that overly-intimate knowledge.\n\nOnce these operations are separate, git-worktree will no longer be able\nto rely upon git-branch to determine the state of the worktree (branch\nname or detached), or to check for error conditions, such as the\nrequested branch already checked out elsewhere, or an invalid reference.\nTherefore, imbue git-worktree with the intelligence to determine a\nbranch name or detached state locally, and to perform error checking on\nits own.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: don't bother calling ref_exists() for newly created\nbranch; add a few in-code comments explaining logic; reword commit\nmessage.\n\n builtin/worktree.c | 18 ++++++++++++++++++\n 1 file changed, 18 insertions(+)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 28095c1..e8f3f30 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -3,6 +3,8 @@\n #include \"dir.h\"\n #include \"parse-options.h\"\n #include \"argv-array.h\"\n+#include \"branch.h\"\n+#include \"refs.h\"\n #include \"run-command.h\"\n #include \"sigchain.h\"\n \n@@ -188,11 +190,26 @@ static int add_worktree(const char *path, const char *refname,\n \tstruct child_process cp;\n \tstruct argv_array child_env = ARGV_ARRAY_INIT;\n \tint counter = 0, len, ret;\n+\tstruct strbuf symref = STRBUF_INIT;\n+\tstruct commit *commit = NULL;\n \tunsigned char rev[20];\n \n \tif (file_exists(path) && !is_empty_dir(path))\n \t\tdie(_(\"'%s' already exists\"), path);\n \n+\t/* is 'refname' a branch or commit? */\n+\tif (opts->force_new_branch) /* definitely a branch */\n+\t\t;\n+\telse if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&\n+\t\t ref_exists(symref.buf)) { /* it's a branch */\n+\t\tif (!opts->force)\n+\t\t\tdie_if_checked_out(symref.buf);\n+\t} else { /* must be a commit */\n+\t\tcommit = lookup_commit_reference_by_name(refname);\n+\t\tif (!commit)\n+\t\t\tdie(_(\"invalid reference: %s\"), refname);\n+\t}\n+\n \tname = worktree_basename(path, &len);\n \tstrbuf_addstr(&sb_repo,\n \t\t      git_path(\"worktrees/%.*s\", (int)(path + len - name), name));\n@@ -281,6 +298,7 @@ static int add_worktree(const char *path, const char *refname,\n \tunlink_or_warn(sb.buf);\n \targv_array_clear(&child_env);\n \tstrbuf_release(&sb);\n+\tstrbuf_release(&symref);\n \tstrbuf_release(&sb_repo);\n \tstrbuf_release(&sb_git);\n \treturn ret;\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266180","messageId":"1437034825-32054-18-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 17/20] worktree: make setup of new HEAD distinct from worktree population","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:22Z","receivedAt":"2015-07-16T08:20:22Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"git-worktree currently conflates setting of HEAD in the new worktree and\ninitial worktree population into a single git-checkout invocation which\nrequires git-checkout to have special knowledge that it is operating on\na newly created worktree. The eventual goal is to rid git-checkout of\nthat overly-intimate knowledge.\n\nOnce these operations are separate, git-worktree will no longer be able\nto delegate to git-branch the setting of the new worktree's HEAD to the\ndesired branch (or commit, if detached). Therefore, make git-worktree\nitself responsible for setting up HEAD as either a symbolic reference,\nif associated with a branch, or detached, if not.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: reword commit message.\n\n builtin/worktree.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex e8f3f30..59609e3 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -278,12 +278,21 @@ static int add_worktree(const char *path, const char *refname,\n \targv_array_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tmemset(&cp, 0, sizeof(cp));\n \tcp.git_cmd = 1;\n+\n+\tif (commit)\n+\t\targv_array_pushl(&cp.args, \"update-ref\", \"HEAD\",\n+\t\t\t\t sha1_to_hex(commit->object.sha1), NULL);\n+\telse\n+\t\targv_array_pushl(&cp.args, \"symbolic-ref\", \"HEAD\",\n+\t\t\t\t symref.buf, NULL);\n+\tcp.env = child_env.argv;\n+\tret = run_command(&cp);\n+\tif (ret)\n+\t\tgoto done;\n+\n+\tcp.argv = NULL;\n+\targv_array_clear(&cp.args);\n \targv_array_push(&cp.args, \"checkout\");\n-\tif (opts->force)\n-\t\targv_array_push(&cp.args, \"--ignore-other-worktrees\");\n-\tif (opts->detach)\n-\t\targv_array_push(&cp.args, \"--detach\");\n-\targv_array_push(&cp.args, refname);\n \tcp.env = child_env.argv;\n \tret = run_command(&cp);\n \tif (!ret) {\n@@ -293,6 +302,7 @@ static int add_worktree(const char *path, const char *refname,\n \t\tjunk_work_tree = NULL;\n \t\tjunk_git_dir = NULL;\n \t}\n+done:\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/locked\", sb_repo.buf);\n \tunlink_or_warn(sb.buf);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266183","messageId":"1437034825-32054-19-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 18/20] worktree: avoid resolving HEAD unnecessarily","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:23Z","receivedAt":"2015-07-16T08:20:23Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Now that git-worktree sets HEAD explicitly to its final value via either\ngit-symbolic-ref or git-update-ref, rather than relying upon\ngit-checkout to do so, the \"hack\" for pacifying is_git_directory() with\na temporary HEAD, though still necessary, can be simplified.\n\nSince the real HEAD is now populated with its proper final value, the\nvalue of the temporary HEAD truly no longer matters, and any value which\nlooks like an object ID is good enough to satisfy is_git_directory().\nTherefore, just set the temporary HEAD to a literal value rather than\ngoing through the effort of resolving the current branch's HEAD.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNo changes since v1.\n\n builtin/worktree.c | 17 +++++------------\n 1 file changed, 5 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 59609e3..54f2f35 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -192,7 +192,6 @@ static int add_worktree(const char *path, const char *refname,\n \tint counter = 0, len, ret;\n \tstruct strbuf symref = STRBUF_INIT;\n \tstruct commit *commit = NULL;\n-\tunsigned char rev[20];\n \n \tif (file_exists(path) && !is_empty_dir(path))\n \t\tdie(_(\"'%s' already exists\"), path);\n@@ -253,20 +252,14 @@ static int add_worktree(const char *path, const char *refname,\n \t\t   real_path(get_git_common_dir()), name);\n \t/*\n \t * This is to keep resolve_ref() happy. We need a valid HEAD\n-\t * or is_git_directory() will reject the directory. Moreover, HEAD\n-\t * in the new worktree must resolve to the same value as HEAD in\n-\t * the current tree since the command invoked to populate the new\n-\t * worktree will be handed the branch/ref specified by the user.\n-\t * For instance, if the user asks for the new worktree to be based\n-\t * at HEAD~5, then the resolved HEAD~5 in the new worktree must\n-\t * match the resolved HEAD~5 in the current tree in order to match\n-\t * the user's expectation.\n+\t * or is_git_directory() will reject the directory. Any value which\n+\t * looks like an object ID will do since it will be immediately\n+\t * replaced by the symbolic-ref or update-ref invocation in the new\n+\t * worktree.\n \t */\n-\tif (!resolve_ref_unsafe(\"HEAD\", 0, rev, NULL))\n-\t\tdie(_(\"unable to resolve HEAD\"));\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/HEAD\", sb_repo.buf);\n-\twrite_file(sb.buf, 1, \"%s\\n\", sha1_to_hex(rev));\n+\twrite_file(sb.buf, 1, \"0000000000000000000000000000000000000000\\n\");\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, 1, \"../..\\n\");\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266184","messageId":"1437034825-32054-20-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 19/20] worktree: populate via \"git reset --hard\" rather than \"git checkout\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:24Z","receivedAt":"2015-07-16T08:20:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Now that git-worktree handles all functionality (--force, --detach,\n-b/-B) previously delegated to git-checkout, actual population of the\nnew worktree can be accomplished more directly and lightweight with\n\"git reset --hard\" in place of \"git checkout\".\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: reword commit message.\n\n builtin/worktree.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 54f2f35..2873064 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -266,7 +266,6 @@ static int add_worktree(const char *path, const char *refname,\n \n \tfprintf_ln(stderr, _(\"Enter %s (identifier %s)\"), path, name);\n \n-\tsetenv(\"GIT_CHECKOUT_NEW_WORKTREE\", \"1\", 1);\n \targv_array_pushf(&child_env, \"%s=%s\", GIT_DIR_ENVIRONMENT, sb_git.buf);\n \targv_array_pushf(&child_env, \"%s=%s\", GIT_WORK_TREE_ENVIRONMENT, path);\n \tmemset(&cp, 0, sizeof(cp));\n@@ -285,7 +284,7 @@ static int add_worktree(const char *path, const char *refname,\n \n \tcp.argv = NULL;\n \targv_array_clear(&cp.args);\n-\targv_array_push(&cp.args, \"checkout\");\n+\targv_array_pushl(&cp.args, \"reset\", \"--hard\", NULL);\n \tcp.env = child_env.argv;\n \tret = run_command(&cp);\n \tif (!ret) {\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266178","messageId":"1437034825-32054-21-git-send-email-sunshine@sunshineco.com","threadId":"39859","inReplyTo":"1437034825-32054-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v2 20/20] checkout: drop intimate knowledge of newly created worktree","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-16T08:20:25Z","receivedAt":"2015-07-16T08:20:25Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Now that git-worktree no longer relies upon git-checkout for new branch\ncreation, new worktree HEAD set up, or initial worktree population,\ngit-checkout no longer needs intimate knowledge that it may be operating\nin a newly created worktree. Therefore, drop 'new_worktree_mode' and the\nprivate GIT_CHECKOUT_NEW_WORKTREE environment variable by which\ngit-worktree communicated to git-checkout that it was being invoked to\nmanipulate a new worktree.\n\nThis reverts the remaining changes to checkout.c by 529fef2 (checkout:\nsupport checking out into a new working directory, 2014-11-30).\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v1: reword commit message.\n\n builtin/checkout.c | 9 ++-------\n 1 file changed, 2 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4ae895c..02d78ba 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -48,8 +48,6 @@ struct checkout_opts {\n \tconst char *prefix;\n \tstruct pathspec pathspec;\n \tstruct tree *source_tree;\n-\n-\tint new_worktree_mode;\n };\n \n static int post_checkout_hook(struct commit *old, struct commit *new,\n@@ -491,7 +489,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\ttopts.dir->flags |= DIR_SHOW_IGNORED;\n \t\t\tsetup_standard_excludes(topts.dir);\n \t\t}\n-\t\ttree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ?\n+\t\ttree = parse_tree_indirect(old->commit ?\n \t\t\t\t\t   old->commit->object.sha1 :\n \t\t\t\t\t   EMPTY_TREE_SHA1_BIN);\n \t\tinit_tree_desc(&trees[0], tree->buffer, tree->size);\n@@ -807,8 +805,7 @@ static int switch_branches(const struct checkout_opts *opts,\n \t\treturn ret;\n \t}\n \n-\tif (!opts->quiet && !old.path && old.commit &&\n-\t    new->commit != old.commit && !opts->new_worktree_mode)\n+\tif (!opts->quiet && !old.path && old.commit && new->commit != old.commit)\n \t\torphaned_commit_warning(old.commit, new->commit);\n \n \tupdate_refs_for_switch(opts, &old, new);\n@@ -1151,8 +1148,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\topts.new_worktree_mode = getenv(\"GIT_CHECKOUT_NEW_WORKTREE\") != NULL;\n-\n \tif (conflict_style) {\n \t\topts.merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266200","messageId":"xmqqy4ig3s3q.fsf@gitster.dls.corp.google.com","threadId":"39859","inReplyTo":"1437034825-32054-7-git-send-email-sunshine@sunshineco.com","subject":"Re: [PATCH v2 06/20] checkout: check_linked_checkout: improve \"already checked out\" aesthetic","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-16T17:55:05Z","receivedAt":"2015-07-16T17:55:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> When check_linked_checkout() discovers that the branch is already\n> checked out elsewhere, it emits the diagnostic:\n>\n>     'blorp' is already checked out at '/some/path/.git'\n>\n> which is mildly misleading and a bit unsightly due to the trailing\n> \"/.git\". For the user, \"/some/path\" is significant, whereas \"/.git\" is\n> mere noise, so drop it.\n\nMore importantly, when a user hears \"a checkout\", the word means the\nworking tree location.  My top-level Makefile is at /some/path/Makefile,\nnot /some/path/.git/Makefile, so having /.git suffix is wrong.\n\nHow does this work with manually configured GIT_DIR environment, by\nthe way?  I think GIT_DIR=/collection/of/repos/foo.git would be OK,\nas strbuf_strip_suffix() would hopefully leave it intact, but I am\nmore interested in the general working of linked checkout feature,\nnot just this error message.\n\nIn the new world order with GIT_DIR and GIT_COMMON_DIR, does\n\"$GIT_DIR\" always have to be the same as \"$GIT_WORK_TREE/.git\"?  Do\nwe need some sanity check if that is the case?  Perhaps: if you have\n$GIT_DIR set to $somewhere/.git/worktrees/$name, then\n\n - $GIT_COMMON_DIR must match $somewhere/.git,\n\n - $somewhere/.git/worktrees/$name/commondir must point at\n   $GIT_COMMON_DIR,\n\n - $GIT_WORK_TREE/.git must match $GIT_DIR\n\nor something like that?\n\n> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n> ---\n>\n> New in v2.\n>\n>  builtin/checkout.c | 1 +\n>  1 file changed, 1 insertion(+)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 177ad6a..a331345 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -909,6 +909,7 @@ static void check_linked_checkout(const char *branch, const char *id)\n>  \t} else\n>  \t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n>  \tskip_prefix(branch, \"refs/heads/\", &branch);\n> +\tstrbuf_strip_suffix(&gitdir, \"/.git\");\n\nSick people have '/.git' and run \"git add etc/passwd\"; do we want to\nconsider such a use case?\n\n>  \tdie(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n>  done:\n>  \tstrbuf_release(&path);\n"},{"id":"266201","messageId":"xmqqtwt43r9p.fsf@gitster.dls.corp.google.com","threadId":"39859","inReplyTo":"1437034825-32054-21-git-send-email-sunshine@sunshineco.com","subject":"Re: [PATCH v2 20/20] checkout: drop intimate knowledge of newly created worktree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-16T18:13:06Z","receivedAt":"2015-07-16T18:13:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> Now that git-worktree no longer relies upon git-checkout for new branch\n> creation, new worktree HEAD set up, or initial worktree population,\n> git-checkout no longer needs intimate knowledge that it may be operating\n> in a newly created worktree. Therefore, drop 'new_worktree_mode' and the\n> private GIT_CHECKOUT_NEW_WORKTREE environment variable by which\n> git-worktree communicated to git-checkout that it was being invoked to\n> manipulate a new worktree.\n>\n> This reverts the remaining changes to checkout.c by 529fef2 (checkout:\n> support checking out into a new working directory, 2014-11-30).\n\nThe diff between 529fef2^ and the version after applying this series\nin builtin/checkout.c looks very sensible; essentially it is just\n10f102be (checkout: pass whole struct to parse_branchname_arg\ninstead of individual flags, 2015-01-03).\n\nThanks.\n\n> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n> ---\n>\n> Changes since v1: reword commit message.\n>\n>  builtin/checkout.c | 9 ++-------\n>  1 file changed, 2 insertions(+), 7 deletions(-)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 4ae895c..02d78ba 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -48,8 +48,6 @@ struct checkout_opts {\n>  \tconst char *prefix;\n>  \tstruct pathspec pathspec;\n>  \tstruct tree *source_tree;\n> -\n> -\tint new_worktree_mode;\n>  };\n>  \n>  static int post_checkout_hook(struct commit *old, struct commit *new,\n> @@ -491,7 +489,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n>  \t\t\ttopts.dir->flags |= DIR_SHOW_IGNORED;\n>  \t\t\tsetup_standard_excludes(topts.dir);\n>  \t\t}\n> -\t\ttree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ?\n> +\t\ttree = parse_tree_indirect(old->commit ?\n>  \t\t\t\t\t   old->commit->object.sha1 :\n>  \t\t\t\t\t   EMPTY_TREE_SHA1_BIN);\n>  \t\tinit_tree_desc(&trees[0], tree->buffer, tree->size);\n> @@ -807,8 +805,7 @@ static int switch_branches(const struct checkout_opts *opts,\n>  \t\treturn ret;\n>  \t}\n>  \n> -\tif (!opts->quiet && !old.path && old.commit &&\n> -\t    new->commit != old.commit && !opts->new_worktree_mode)\n> +\tif (!opts->quiet && !old.path && old.commit && new->commit != old.commit)\n>  \t\torphaned_commit_warning(old.commit, new->commit);\n>  \n>  \tupdate_refs_for_switch(opts, &old, new);\n> @@ -1151,8 +1148,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n>  \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n>  \n> -\topts.new_worktree_mode = getenv(\"GIT_CHECKOUT_NEW_WORKTREE\") != NULL;\n> -\n>  \tif (conflict_style) {\n>  \t\topts.merge = 1; /* implied */\n>  \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n"},{"id":"266219","messageId":"CAPig+cRtCon=jaqbjZyHTvJ3cydiyAz+5OC=3x30VfAJniYYMQ@mail.gmail.com","threadId":"39859","inReplyTo":"xmqqy4ig3s3q.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 06/20] checkout: check_linked_checkout: improve \"already checked out\" aesthetic","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T00:32:38Z","receivedAt":"2015-07-17T00:32:38Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jul 16, 2015 at 1:55 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> How does this work with manually configured GIT_DIR environment, by\n> the way?  I think GIT_DIR=/collection/of/repos/foo.git would be OK,\n> as strbuf_strip_suffix() would hopefully leave it intact, but I am\n> more interested in the general working of linked checkout feature,\n> not just this error message.\n\nI may be misunderstanding your question, but my answer is that\nstrbuf_strip_suffix() is not applied to $GIT_DIR, but rather to the\ncontent of file $GIT_COMMON_DIR/worktrees/<tag>/gitdir, which is the\npath to the .git file in the linked worktree. That is, given:\n\n    git worktree add ../foo HEAD\n\nthen the content of 'gitdir' is the absolute path of \"../foo/.git\",\nand strbuf_strip_suffix() operates on that value.\n\n> In the new world order with GIT_DIR and GIT_COMMON_DIR, does\n> \"$GIT_DIR\" always have to be the same as \"$GIT_WORK_TREE/.git\"?  Do\n> we need some sanity check if that is the case?  Perhaps: if you have\n> $GIT_DIR set to $somewhere/.git/worktrees/$name, then\n>\n>  - $GIT_COMMON_DIR must match $somewhere/.git,\n>\n>  - $somewhere/.git/worktrees/$name/commondir must point at\n>    $GIT_COMMON_DIR,\n>\n>  - $GIT_WORK_TREE/.git must match $GIT_DIR\n>\n> or something like that?\n\nDuy is probably better suited to answer this, as he would likely have\ntaken these issues into consideration when implementing the feature.\n(I've been poking through documentation and code for quite a while\ntrying to answer this email but don't yet have a sufficient grasp to\ndo it justice. I'm not even sure where such a sanity check would be\nplaced.)\n\n>>       } else\n>>               strbuf_addstr(&gitdir, get_git_common_dir());\n>>       skip_prefix(branch, \"refs/heads/\", &branch);\n>> +     strbuf_strip_suffix(&gitdir, \"/.git\");\n>\n> Sick people have '/.git' and run \"git add etc/passwd\"; do we want to\n> consider such a use case?\n\nI originally implemented this as stripping only \".git\" since it felt\nnatural for the result to have a trailing slash, however, when I\nlooked back at your report[1], I saw that you suggested stripping\n\"/.git\", so I changed it to strip the slash as well. Given the above\n\"sick\" use-case, we may indeed want to strip only \".git\".\n\n[1]: http://article.gmane.org/gmane.comp.version-control.git/274001\n\n>>       die(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n>>  done:\n>>       strbuf_release(&path);\n"},{"id":"266221","messageId":"CACsJy8AGp7Y+nJjVvp2sgfTvPuKE2ODdfa3wsJ9YhPk57YOtCg@mail.gmail.com","threadId":"39859","inReplyTo":"CAPig+cRtCon=jaqbjZyHTvJ3cydiyAz+5OC=3x30VfAJniYYMQ@mail.gmail.com","subject":"Re: [PATCH v2 06/20] checkout: check_linked_checkout: improve \"already checked out\" aesthetic","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2015-07-17T01:42:59Z","receivedAt":"2015-07-17T01:42:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Jul 17, 2015 at 7:32 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>> In the new world order with GIT_DIR and GIT_COMMON_DIR, does\n>> \"$GIT_DIR\" always have to be the same as \"$GIT_WORK_TREE/.git\"?  Do\n>> we need some sanity check if that is the case?  Perhaps: if you have\n>> $GIT_DIR set to $somewhere/.git/worktrees/$name, then\n>>\n>>  - $GIT_COMMON_DIR must match $somewhere/.git,\n>>\n>>  - $somewhere/.git/worktrees/$name/commondir must point at\n>>    $GIT_COMMON_DIR,\n>>\n>>  - $GIT_WORK_TREE/.git must match $GIT_DIR\n>>\n>> or something like that?\n>\n> Duy is probably better suited to answer this, as he would likely have\n> taken these issues into consideration when implementing the feature.\n> (I've been poking through documentation and code for quite a while\n> trying to answer this email but don't yet have a sufficient grasp to\n> do it justice. I'm not even sure where such a sanity check would be\n> placed.)\n\nThe thing is, we just don't know where the worktree is. All we know is\nsomewhere there is a .git file sharing this repository. People can\ncreate a linked worktree, then move the actual linked worktree away,\nset GIT_DIR/GIT_WORK_TREE to reflect that, and everything must still\nwork. So, we could say \"foo is already checked out at the worktree\nthat is linked to /some/path/.git\" to be technically correct. But\nthat's not so friendly? We could cache the $GIT_WORK_TREE, when the\nuser accesses the linked checkout, somewhere in .git/worktrees/foo and\nshow it instead of /some/path/.git. But that's not always accurate.\n-- \nDuy\n"}]}