{"thread":{"id":"39880","subject":"[PATCH v3 01/22] checkout: avoid resolving HEAD unnecessarily","startedAt":"2015-07-17T22:59:55Z","lastAt":"2015-07-17T23:20:40Z","messageCount":24,"participants":["Eric Sunshine"],"isPatch":true,"patchVersion":3,"patchTotal":22},"messages":[{"id":"266294","messageId":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":null,"subject":"[PATCH v3 00/22] rid git-checkout of too-intimate knowledge of new worktree","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T22:59:55Z","receivedAt":"2015-07-17T22:59:55Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"This is v3 of [1] which rids git-checkout of specialized knowledge that\nit's operating in a newly created linked worktree. Thanks to Junio for\nhis review, and Duy and Michael J Gruber for additional observations.\n\nA v2 to v3 interdiff is included below.\n\nChanges since v2:\n\n* patch 06/22: strip only trailing \".git\" rather than \"/.git\" from\n  \"'branch' is already checked out at '/some/patch/.git'\" message[2];\n  also reword commit message slightly to better emphasize that the\n  trailing \".git\" is more than a mere cosmetic issue[2]\n\n* patch 10/22 (new): change worktree setup message from \"Enter\n  <path>...\" to \"Preparing <path>...\" to avoid possibly confusing the\n  user into thinking that 'cd <path>' occurred[3]\n\n* patch 13/22: add tests of -b/-B/--detach exclusivity\n\n* patch 14/22 (new): suppress accidental triggering of branch\n  auto-vivication with \"git worktree add --detach <path>\"; instead treat\n  it as shorthand for \"git worktree add --detach <path> HEAD\"[3]\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/274024\n[2]: http://article.gmane.org/gmane.comp.version-control.git/274035\n[3]: http://article.gmane.org/gmane.comp.version-control.git/274083\n\nEric Sunshine (22):\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: improve worktree setup message\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: add: suppress auto-vivication with --detach and no <branch>\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      | 123 ++++++++++++++++++++++++++++++++----------------\n t/t2025-worktree-add.sh |  34 +++++++++++++\n 5 files changed, 197 insertions(+), 116 deletions(-)\n\n-- \n2.5.0.rc2.378.g0af52e8\n\n---- 8< ----\ndiff --git a/branch.c b/branch.c\nindex dfd7698..af9480f 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -347,7 +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+\tstrbuf_strip_suffix(&gitdir, \".git\");\n \tdie(_(\"'%s' is already checked out at '%s'\"), branch, gitdir.buf);\n done:\n \tstrbuf_release(&path);\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 2873064..5d9371c 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -264,7 +264,7 @@ static int add_worktree(const char *path, const char *refname,\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, 1, \"../..\\n\");\n \n-\tfprintf_ln(stderr, _(\"Enter %s (identifier %s)\"), path, name);\n+\tfprintf_ln(stderr, _(\"Preparing %s (identifier %s)\"), path, name);\n \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@@ -335,7 +335,7 @@ static int add(int ac, const char **av, const char *prefix)\n \tif (opts.force_new_branch)\n \t\topts.new_branch = new_branch_force;\n \n-\tif (ac < 2 && !opts.new_branch) {\n+\tif (ac < 2 && !opts.new_branch && !opts.detach) {\n \t\tint n;\n \t\tconst char *s = worktree_basename(path, &n);\n \t\topts.new_branch = xstrndup(s, n);\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex 9e30690..8267411 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -153,6 +153,14 @@ test_expect_success '\"add -b\" with <branch> omitted' '\n \ttest_cmp_rev HEAD burble\n '\n \n+test_expect_success '\"add --detach\" with <branch> omitted' '\n+\tgit worktree add --detach fishhook &&\n+\tgit rev-parse HEAD >expected &&\n+\tgit -C fishhook rev-parse HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_must_fail git -C fishhook symbolic-ref HEAD\n+'\n+\n test_expect_success '\"add\" with <branch> omitted' '\n \tgit worktree add wiffle/bat &&\n \ttest_cmp_rev HEAD bat\n@@ -167,4 +175,22 @@ test_expect_success '\"add\" auto-vivify does not clobber existing branch' '\n \ttest_path_is_missing precious\n '\n \n+test_expect_success '\"add\" no auto-vivify with --detach and <branch> omitted' '\n+\tgit worktree add --detach mish/mash &&\n+\ttest_must_fail git rev-parse mash -- &&\n+\ttest_must_fail git -C mish/mash symbolic-ref HEAD\n+'\n+\n+test_expect_success '\"add\" -b/-B mutually exclusive' '\n+\ttest_must_fail git worktree add -b poodle -B poodle bamboo master\n+'\n+\n+test_expect_success '\"add\" -b/--detach mutually exclusive' '\n+\ttest_must_fail git worktree add -b poodle --detach bamboo master\n+'\n+\n+test_expect_success '\"add\" -B/--detach mutually exclusive' '\n+\ttest_must_fail git worktree add -B poodle --detach bamboo master\n+'\n+\n test_done\n---- 8< ----\n"},{"id":"266293","messageId":"1437174017-81687-2-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 01/22] checkout: avoid resolving HEAD unnecessarily","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T22:59:56Z","receivedAt":"2015-07-17T22:59:56Z","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 v2.\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":"266295","messageId":"1437174017-81687-3-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 02/22] checkout: name check_linked_checkouts() more meaningfully","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T22:59:57Z","receivedAt":"2015-07-17T22:59:57Z","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 v2.\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":"266314","messageId":"1437174017-81687-4-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 03/22] checkout: improve die_if_checked_out() robustness","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T22:59:58Z","receivedAt":"2015-07-17T22:59:58Z","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\nNo changes since v2.\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":"266296","messageId":"1437174017-81687-5-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 04/22] checkout: die_if_checked_out: simplify strbuf management","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T22:59:59Z","receivedAt":"2015-07-17T22:59:59Z","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 v2.\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":"266312","messageId":"1437174017-81687-6-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 05/22] checkout: generalize die_if_checked_out() branch name argument","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:00Z","receivedAt":"2015-07-17T23:00:00Z","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 v2.\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":"266298","messageId":"1437174017-81687-7-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 06/22] checkout: check_linked_checkout: improve \"already checked out\" aesthetic","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:01Z","receivedAt":"2015-07-17T23:00:01Z","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 misleading since \"checked out at\" implies the working tree, but\n\".git\" is the location of the repository administrative files. Fix by\ndropping \".git\" from the message.\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nChanges since v2: strip only trailing \".git\" rather than \"/.git\" from\n\"already checked\" diagnostic[1]; reword commit message to better\nemphasize that trailing \".git\" is more than a mere cosmetic issue[1]\n\n[1]: http://article.gmane.org/gmane.comp.version-control.git/274035\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..de6619f 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":"266315","messageId":"1437174017-81687-8-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 07/22] checkout: check_linked_checkout: simplify symref parsing","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:02Z","receivedAt":"2015-07-17T23:00:02Z","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\nNo changes since v2.\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 de6619f..6f4e492 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":"266311","messageId":"1437174017-81687-9-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 08/22] checkout: teach check_linked_checkout() about symbolic link HEAD","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:03Z","receivedAt":"2015-07-17T23:00:03Z","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\nNo changes since v2.\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 6f4e492..f04dcaa 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":"266297","messageId":"1437174017-81687-10-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 09/22] branch: publish die_if_checked_out()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:04Z","receivedAt":"2015-07-17T23:00:04Z","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\nNo changes since v2.\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..af9480f 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 f04dcaa..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":"266308","messageId":"1437174017-81687-11-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 10/22] worktree: improve worktree setup message","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:05Z","receivedAt":"2015-07-17T23:00:05Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"When git-worktree creates a new worktree, it reports:\n\n    Enter \"<path>\" (identifier <tag>)\n\nwhich misleadingly implies that it is setting <path> as the working\ndirectory (as if \"cd <path>\" had been invoked), whereas it's actually\npreparing the new worktree by creating its administrative files, setting\nHEAD, and populating it. Make this more clear by instead saying:\n\n    Preparing \"<path>\" (identifier <tag>)\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNew in v3.\n\n builtin/worktree.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 69248ba..5f0e3c2 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -245,7 +245,7 @@ static int add_worktree(const char *path, const char **child_argv)\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, 1, \"../..\\n\");\n \n-\tfprintf_ln(stderr, _(\"Enter %s (identifier %s)\"), path, name);\n+\tfprintf_ln(stderr, _(\"Preparing %s (identifier %s)\"), path, name);\n \n \tsetenv(\"GIT_CHECKOUT_NEW_WORKTREE\", \"1\", 1);\n \tsetenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266309","messageId":"1437174017-81687-12-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 11/22] worktree: simplify new branch (-b/-B) option checking","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:06Z","receivedAt":"2015-07-17T23:00:06Z","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 v2.\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 5f0e3c2..bf4db9f 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":"266313","messageId":"1437174017-81687-13-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 12/22] worktree: introduce options container","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:07Z","receivedAt":"2015-07-17T23:00:07Z","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 v2.\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 bf4db9f..7d70eb6 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":"266310","messageId":"1437174017-81687-14-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 13/22] worktree: make --detach mutually exclusive with -b/-B","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:08Z","receivedAt":"2015-07-17T23:00:08Z","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\nChanges since v2: add tests\n\n builtin/worktree.c      |  4 ++--\n t/t2025-worktree-add.sh | 12 ++++++++++++\n 2 files changed, 14 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 7d70eb6..813e016 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 \ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex 9e30690..249e454 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -167,4 +167,16 @@ test_expect_success '\"add\" auto-vivify does not clobber existing branch' '\n \ttest_path_is_missing precious\n '\n \n+test_expect_success '\"add\" -b/-B mutually exclusive' '\n+\ttest_must_fail git worktree add -b poodle -B poodle bamboo master\n+'\n+\n+test_expect_success '\"add\" -b/--detach mutually exclusive' '\n+\ttest_must_fail git worktree add -b poodle --detach bamboo master\n+'\n+\n+test_expect_success '\"add\" -B/--detach mutually exclusive' '\n+\ttest_must_fail git worktree add -B poodle --detach bamboo master\n+'\n+\n test_done\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266300","messageId":"1437174017-81687-15-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 14/22] worktree: add: suppress auto-vivication with --detach and no <branch>","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:09Z","receivedAt":"2015-07-17T23:00:09Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Fix oversight where branch auto-vivication incorrectly kicks in when\n--detach is specified and <branch> omitted. Instead, treat:\n\n    git worktree add --detach <path>\n\nas shorthand for:\n\n    git worktree add --detach <path> HEAD\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nNew in v3.\n\n builtin/worktree.c      |  2 +-\n t/t2025-worktree-add.sh | 14 ++++++++++++++\n 2 files changed, 15 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 813e016..83484ad 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -308,7 +308,7 @@ static int add(int ac, const char **av, const char *prefix)\n \tif (opts.force_new_branch)\n \t\topts.new_branch = new_branch_force;\n \n-\tif (ac < 2 && !opts.new_branch) {\n+\tif (ac < 2 && !opts.new_branch && !opts.detach) {\n \t\tint n;\n \t\tconst char *s = worktree_basename(path, &n);\n \t\topts.new_branch = xstrndup(s, n);\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex 249e454..8267411 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -153,6 +153,14 @@ test_expect_success '\"add -b\" with <branch> omitted' '\n \ttest_cmp_rev HEAD burble\n '\n \n+test_expect_success '\"add --detach\" with <branch> omitted' '\n+\tgit worktree add --detach fishhook &&\n+\tgit rev-parse HEAD >expected &&\n+\tgit -C fishhook rev-parse HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_must_fail git -C fishhook symbolic-ref HEAD\n+'\n+\n test_expect_success '\"add\" with <branch> omitted' '\n \tgit worktree add wiffle/bat &&\n \ttest_cmp_rev HEAD bat\n@@ -167,6 +175,12 @@ test_expect_success '\"add\" auto-vivify does not clobber existing branch' '\n \ttest_path_is_missing precious\n '\n \n+test_expect_success '\"add\" no auto-vivify with --detach and <branch> omitted' '\n+\tgit worktree add --detach mish/mash &&\n+\ttest_must_fail git rev-parse mash -- &&\n+\ttest_must_fail git -C mish/mash symbolic-ref HEAD\n+'\n+\n test_expect_success '\"add\" -b/-B mutually exclusive' '\n \ttest_must_fail git worktree add -b poodle -B poodle bamboo master\n '\n-- \n2.5.0.rc2.378.g0af52e8\n"},{"id":"266305","messageId":"1437174017-81687-16-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 15/22] worktree: make branch creation distinct from worktree population","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:10Z","receivedAt":"2015-07-17T23:00:10Z","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\nNo changes since v2.\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 83484ad..8225468 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":"266303","messageId":"1437174017-81687-17-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 16/22] worktree: elucidate environment variables intended for child processes","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:11Z","receivedAt":"2015-07-17T23:00:11Z","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\nNo changes since 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 8225468..7bd6f17 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, _(\"Preparing %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":"266307","messageId":"1437174017-81687-18-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 17/22] worktree: add_worktree: construct worktree-population command locally","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:12Z","receivedAt":"2015-07-17T23:00:12Z","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\nNo changes since v2.\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 7bd6f17..da76eb7 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":"266306","messageId":"1437174017-81687-19-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 18/22] worktree: detect branch-name/detached and error conditions locally","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:13Z","receivedAt":"2015-07-17T23:00:13Z","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\nNo changes since v2.\n\n builtin/worktree.c | 18 ++++++++++++++++++\n 1 file changed, 18 insertions(+)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex da76eb7..cf35b2a 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":"266299","messageId":"1437174017-81687-20-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 19/22] worktree: make setup of new HEAD distinct from worktree population","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:14Z","receivedAt":"2015-07-17T23:00:14Z","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\nNo changes since v2.\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 cf35b2a..79d088c 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":"266301","messageId":"1437174017-81687-21-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 20/22] worktree: avoid resolving HEAD unnecessarily","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:15Z","receivedAt":"2015-07-17T23:00:15Z","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 v2.\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 79d088c..4619308 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":"266304","messageId":"1437174017-81687-22-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 21/22] worktree: populate via \"git reset --hard\" rather than \"git checkout\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:16Z","receivedAt":"2015-07-17T23:00:16Z","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\nNo changes since v2.\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 4619308..5d9371c 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, _(\"Preparing %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":"266302","messageId":"1437174017-81687-23-git-send-email-sunshine@sunshineco.com","threadId":"39880","inReplyTo":"1437174017-81687-1-git-send-email-sunshine@sunshineco.com","subject":"[PATCH v3 22/22] checkout: drop intimate knowledge of newly created worktree","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:00:17Z","receivedAt":"2015-07-17T23:00:17Z","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\nNo changes since v2.\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":"266316","messageId":"20150717232040.GA85140@flurp.local","threadId":"39880","inReplyTo":"1437174017-81687-15-git-send-email-sunshine@sunshineco.com","subject":"Re: [PATCH v3 14/22] worktree: add: suppress auto-vivication with --detach and no <branch>","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-17T23:20:40Z","receivedAt":"2015-07-17T23:20:40Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Jul 17, 2015 at 07:00:09PM -0400, Eric Sunshine wrote:\n> Fix oversight where branch auto-vivication incorrectly kicks in when\n> --detach is specified and <branch> omitted. Instead, treat:\n> \n>     git worktree add --detach <path>\n> \n> as shorthand for:\n> \n>     git worktree add --detach <path> HEAD\n> \n> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>\n> ---\n>  builtin/worktree.c      |  2 +-\n>  t/t2025-worktree-add.sh | 14 ++++++++++++++\n>  2 files changed, 15 insertions(+), 1 deletion(-)\n\nI meant, but forgot, to include a documentation update with this patch.\nHere it is as a squash-in:\n\n---- 8< ----\nFrom: Eric Sunshine <sunshine@sunshineco.com>\nSubject: [PATCH v3 23/22] fixup! worktree: add: suppress auto-vivication with --detach and no <branch>\n\nSigned-off-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n Documentation/git-worktree.txt | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex da71f50..834a61c 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -51,9 +51,9 @@ Create `<path>` and checkout `<branch>` into it. The new working directory\n is linked to the current repository, sharing everything except working\n directory specific files such as HEAD, index, etc.\n +\n-If `<branch>` is omitted and neither `-b` nor `-B` is used, then, as a\n-convenience, a new branch based at HEAD is created automatically, as if\n-`-b $(basename <path>)` was specified.\n+If `<branch>` is omitted and neither `-b` nor `-B` nor `--detached` used,\n+then, as a convenience, a new branch based at HEAD is created automatically,\n+as if `-b $(basename <path>)` was specified.\n \n prune::\n \n-- \n2.5.0.rc2.378.g0af52e8\n"}]}