{"thread":{"id":"37194","subject":"[PATCH 0/5] nd/multiple-work-trees follow-ups","startedAt":"2014-07-23T11:43:10Z","lastAt":"2014-08-27T16:08:41Z","messageCount":36,"participants":["Nguyễn Thái Ngọc Duy","Michael J Gruber","Junio C Hamano","Duy Nguyen"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"246546","messageId":"1406115795-24082-1-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":null,"subject":"[PATCH 0/5] nd/multiple-work-trees follow-ups","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-23T11:43:10Z","receivedAt":"2014-07-23T11:43:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The series has entered 'next' so I can't replace patches any more.\nBesides the brown paper bag fixes, checkout now rejects if a branch is\nalready checked out elsewhere.\n\nNguyễn Thái Ngọc Duy (5):\n  gitrepository-layout.txt: s/ignored/ignored if/\n  prune --repos: fix uninitialized access\n  checkout --to: no auto-detach if the ref is already checked out\n  checkout --to: fix dangling pointers in remove_junk()\n  environment.c: fix incorrect git_graft_file initialization\n\n Documentation/config.txt               |  3 ++\n Documentation/gitrepository-layout.txt |  6 +--\n advice.c                               |  2 +\n advice.h                               |  1 +\n builtin/checkout.c                     | 88 ++++++++++++++++++++--------------\n builtin/prune.c                        | 16 +++----\n environment.c                          |  2 +-\n t/t0060-path-utils.sh                  |  1 +\n t/t2025-checkout-to.sh                 | 40 ++++++++++++----\n t/t2026-prune-linked-checkouts.sh      |  2 +-\n 10 files changed, 102 insertions(+), 59 deletions(-)\n\n-- \n1.9.1.346.ga2b5940\n"},{"id":"246547","messageId":"1406115795-24082-2-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406115795-24082-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/5] gitrepository-layout.txt: s/ignored/ignored if/","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-23T11:43:11Z","receivedAt":"2014-07-23T11:43:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitrepository-layout.txt | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex bed4f1a..6bd82af 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -139,7 +139,7 @@ is often called 'detached HEAD.'  See linkgit:git-checkout[1]\n for details.\n \n config::\n-\tRepository specific configuration file. This file is ignored\n+\tRepository specific configuration file. This file is ignored if\n \t$GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/config\" will be\n \tused instead.\n \n@@ -220,7 +220,7 @@ remotes::\n logs::\n \tRecords of changes made to refs are stored in this\n \tdirectory.  See linkgit:git-update-ref[1]\n-\tfor more information. This directory is ignored\n+\tfor more information. This directory is ignored if\n \t$GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/logs\" will be used\n \tinstead.\n \n@@ -252,7 +252,7 @@ modules::\n repos::\n \tContains worktree specific information of linked\n \tcheckouts. Each subdirectory contains the worktree-related\n-\tpart of a linked checkout. This directory is ignored\n+\tpart of a linked checkout. This directory is ignored if\n \t$GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/repos\" will be\n \tused instead.\n \n-- \n1.9.1.346.ga2b5940\n"},{"id":"246548","messageId":"1406115795-24082-3-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406115795-24082-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/5] prune --repos: fix uninitialized access","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-23T11:43:12Z","receivedAt":"2014-07-23T11:43:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"There's a code path in prune_repo_dir() that does not initialize 'st'\nbuffer, which is checked by the caller, prune_repos_dir(). Instead\nof leaking some prune logic out to prune_repos_dir(), move 'st' into\nprune_repo_dir().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/prune.c                   | 16 ++++++----------\n t/t2026-prune-linked-checkouts.sh |  2 +-\n 2 files changed, 7 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex 28b7adf..e72c391 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -112,8 +112,9 @@ static void prune_object_dir(const char *path)\n \t}\n }\n \n-static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason)\n+static int prune_repo_dir(const char *id, struct strbuf *reason)\n {\n+\tstruct stat st;\n \tchar *path;\n \tint fd, len;\n \n@@ -123,26 +124,23 @@ static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason\n \t}\n \tif (file_exists(git_path(\"repos/%s/locked\", id)))\n \t\treturn 0;\n-\tif (stat(git_path(\"repos/%s/gitdir\", id), st)) {\n-\t\tst->st_mtime = expire;\n+\tif (stat(git_path(\"repos/%s/gitdir\", id), &st)) {\n \t\tstrbuf_addf(reason, _(\"Removing repos/%s: gitdir file does not exist\"), id);\n \t\treturn 1;\n \t}\n \tfd = open(git_path(\"repos/%s/gitdir\", id), O_RDONLY);\n \tif (fd < 0) {\n-\t\tst->st_mtime = expire;\n \t\tstrbuf_addf(reason, _(\"Removing repos/%s: unable to read gitdir file (%s)\"),\n \t\t\t    id, strerror(errno));\n \t\treturn 1;\n \t}\n-\tlen = st->st_size;\n+\tlen = st.st_size;\n \tpath = xmalloc(len + 1);\n \tread_in_full(fd, path, len);\n \tclose(fd);\n \twhile (len && (path[len - 1] == '\\n' || path[len - 1] == '\\r'))\n \t\tlen--;\n \tif (!len) {\n-\t\tst->st_mtime = expire;\n \t\tstrbuf_addf(reason, _(\"Removing repos/%s: invalid gitdir file\"), id);\n \t\tfree(path);\n \t\treturn 1;\n@@ -162,7 +160,7 @@ static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason\n \t\treturn 1;\n \t}\n \tfree(path);\n-\treturn 0;\n+\treturn st.st_mtime <= expire;\n }\n \n static void prune_repos_dir(void)\n@@ -172,15 +170,13 @@ static void prune_repos_dir(void)\n \tDIR *dir = opendir(git_path(\"repos\"));\n \tstruct dirent *d;\n \tint ret;\n-\tstruct stat st;\n \tif (!dir)\n \t\treturn;\n \twhile ((d = readdir(dir)) != NULL) {\n \t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n \t\t\tcontinue;\n \t\tstrbuf_reset(&reason);\n-\t\tif (!prune_repo_dir(d->d_name, &st, &reason) ||\n-\t\t    st.st_mtime > expire)\n+\t\tif (!prune_repo_dir(d->d_name, &reason))\n \t\t\tcontinue;\n \t\tif (show_only || verbose)\n \t\t\tprintf(\"%s\\n\", reason.buf);\ndiff --git a/t/t2026-prune-linked-checkouts.sh b/t/t2026-prune-linked-checkouts.sh\nindex 4ccfa4e..79d84cb 100755\n--- a/t/t2026-prune-linked-checkouts.sh\n+++ b/t/t2026-prune-linked-checkouts.sh\n@@ -77,7 +77,7 @@ test_expect_success 'not prune recent checkouts' '\n \tmkdir zz &&\n \tmkdir -p .git/repos/jlm &&\n \techo \"$TRASH_DIRECTORY\"/zz >.git/repos/jlm/gitdir &&\n-\tgit prune --repos --verbose &&\n+\tgit prune --repos --verbose --expire=2.days.ago &&\n \ttest -d .git/repos/jlm\n '\n \n-- \n1.9.1.346.ga2b5940\n"},{"id":"246549","messageId":"1406115795-24082-4-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406115795-24082-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-23T11:43:13Z","receivedAt":"2014-07-23T11:43:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Give the user a choice in this case. If they want to detach, they can go\nwith '--detach --to ...', or they could switch branch of the checkout\nthat's holding the ref in question. Or they could just create a new\nbranch with '-b xxx --to yyy'\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt |  3 ++\n advice.c                 |  2 ++\n advice.h                 |  1 +\n builtin/checkout.c       | 73 ++++++++++++++++++++++++++++--------------------\n t/t2025-checkout-to.sh   | 16 +++++------\n 5 files changed, 56 insertions(+), 39 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 57999fa..4a41202 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -201,6 +201,9 @@ advice.*::\n \trmHints::\n \t\tIn case of failure in the output of linkgit:git-rm[1],\n \t\tshow directions on how to proceed from the current state.\n+\tcheckoutTo::\n+\t\tIn case of failure in the output of \"git checkout --to\",\n+\t\tshow directions on how to proceed from the current state.\n --\n \n core.fileMode::\ndiff --git a/advice.c b/advice.c\nindex 9b42033..b1fb524 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -15,6 +15,7 @@ int advice_detached_head = 1;\n int advice_set_upstream_failure = 1;\n int advice_object_name_warning = 1;\n int advice_rm_hints = 1;\n+int advice_checkout_to = 1;\n \n static struct {\n \tconst char *name;\n@@ -35,6 +36,7 @@ static struct {\n \t{ \"setupstreamfailure\", &advice_set_upstream_failure },\n \t{ \"objectnamewarning\", &advice_object_name_warning },\n \t{ \"rmhints\", &advice_rm_hints },\n+\t{ \"checkoutto\", &advice_checkout_to },\n \n \t/* make this an alias for backward compatibility */\n \t{ \"pushnonfastforward\", &advice_push_update_rejected }\ndiff --git a/advice.h b/advice.h\nindex 5ecc6c1..a288219 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -18,6 +18,7 @@ extern int advice_detached_head;\n extern int advice_set_upstream_failure;\n extern int advice_object_name_warning;\n extern int advice_rm_hints;\n+extern int advice_checkout_to;\n \n int git_default_advice_config(const char *var, const char *value);\n __attribute__((format (printf, 1, 2)))\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c83f476..d35245a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1006,31 +1006,52 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)\n \treturn NULL;\n }\n \n-static int check_linked_checkout(struct branch_info *new,\n-\t\t\t\t  const char *name, const char *path)\n+static void check_linked_checkout(struct branch_info *new, const char *id)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf gitdir = STRBUF_INIT;\n \tconst char *start, *end;\n-\tif (strbuf_read_file(&sb, path, 0) < 0 ||\n-\t    !skip_prefix(sb.buf, \"ref:\", &start)) {\n-\t\tstrbuf_release(&sb);\n-\t\treturn 0;\n-\t}\n \n+\tif (id)\n+\t\tstrbuf_addf(&path, \"%s/repos/%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+\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, new->path, end - start) &&\n-\t    new->path[end - start] == '\\0') {\n-\t\tstrbuf_release(&sb);\n-\t\tnew->path = NULL; /* detach */\n-\t\tnew->checkout = xstrdup(name); /* reason */\n-\t\treturn 1;\n-\t}\n+\tif (strncmp(start, new->path, end - start) ||\n+\t    new->path[end - start] != '\\0')\n+\t\tgoto done;\n+\tif (id) {\n+\t\tstrbuf_reset(&path);\n+\t\tstrbuf_addf(&path, \"%s/repos/%s/gitdir\",\n+\t\t\t    get_git_common_dir(), id);\n+\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n+\t\t\tgoto done;\n+\t\twhile (gitdir.len && (gitdir.buf[gitdir.len - 1] == '\\n' ||\n+\t\t\t\t      gitdir.buf[gitdir.len - 1] == '\\r'))\n+\t\t\tgitdir.buf[--gitdir.len] = '\\0';\n+\t} else\n+\t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n+\tif (advice_checkout_to)\n+\t\tdie(_(\"%s is already checked out at %s.\\n\"\n+\t\t      \"Either use --detach or -b together with --to \"\n+\t\t      \"or switch branch in the the other checkout.\"),\n+\t\t    new->path, gitdir.buf);\n+\telse\n+\t\tdie(_(\"%s is already checked out at %s.\"),\n+\t\t    new->path, gitdir.buf);\n+done:\n+\tstrbuf_release(&path);\n \tstrbuf_release(&sb);\n-\treturn 0;\n+\tstrbuf_release(&gitdir);\n }\n \n static void check_linked_checkouts(struct branch_info *new)\n@@ -1045,27 +1066,17 @@ static void check_linked_checkouts(struct branch_info *new)\n \t\treturn;\n \t}\n \n-\tstrbuf_reset(&path);\n-\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\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 (check_linked_checkout(new, \"\", path.buf)) {\n-\t\tstrbuf_release(&path);\n-\t\tclosedir(dir);\n-\t\treturn;\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-\t\tstrbuf_reset(&path);\n-\t\tstrbuf_addf(&path, \"%s/repos/%s/HEAD\",\n-\t\t\t    get_git_common_dir(), d->d_name);\n-\t\tif (check_linked_checkout(new, d->d_name, path.buf))\n-\t\t\tbreak;\n+\t\tcheck_linked_checkout(new, d->d_name);\n \t}\n \tstrbuf_release(&path);\n \tclosedir(dir);\n@@ -1076,7 +1087,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct branch_info *new,\n \t\t\t\tstruct tree **source_tree,\n \t\t\t\tunsigned char rev[20],\n-\t\t\t\tconst char **new_branch)\n+\t\t\t\tconst char **new_branch,\n+\t\t\t\tint force_detach)\n {\n \tint argcount = 0;\n \tunsigned char branch_rev[20];\n@@ -1198,7 +1210,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \telse\n \t\tnew->path = NULL; /* not an existing branch */\n \n-\tif (new->path) {\n+\tif (new->path && !force_detach && !*new_branch) {\n \t\tunsigned char sha1[20];\n \t\tint flag;\n \t\tchar *head_ref = resolve_refdup(\"HEAD\", sha1, 0, &flag);\n@@ -1416,7 +1428,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t!opts.new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n \t\t\t\t\t     &new, &opts.source_tree,\n-\t\t\t\t\t     rev, &opts.new_branch);\n+\t\t\t\t\t     rev, &opts.new_branch,\n+\t\t\t\t\t     opts.force_detach);\n \t\targv += n;\n \t\targc -= n;\n \t}\ndiff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\nindex b0d97a0..c6601a4 100755\n--- a/t/t2025-checkout-to.sh\n+++ b/t/t2025-checkout-to.sh\n@@ -14,7 +14,7 @@ test_expect_success 'checkout --to not updating paths' '\n \n test_expect_success 'checkout --to a new worktree' '\n \tgit rev-parse HEAD >expect &&\n-\tgit checkout --to here master &&\n+\tgit checkout --detach --to here master &&\n \t(\n \t\tcd here &&\n \t\ttest_cmp ../init.t init.t &&\n@@ -28,7 +28,7 @@ test_expect_success 'checkout --to a new worktree' '\n test_expect_success 'checkout --to from a linked checkout' '\n \t(\n \t\tcd here &&\n-\t\tgit checkout --to nested-here master\n+\t\tgit checkout --detach --to nested-here master\n \t\tcd nested-here &&\n \t\tgit fsck\n \t)\n@@ -46,19 +46,17 @@ test_expect_success 'checkout --to a new worktree creating new branch' '\n \t)\n '\n \n-test_expect_success 'detach if the same branch is already checked out' '\n+test_expect_success 'die the same branch is already checked out' '\n \t(\n \t\tcd here &&\n-\t\tgit checkout newmaster &&\n-\t\ttest_must_fail git symbolic-ref HEAD\n+\t\ttest_must_fail git checkout newmaster\n \t)\n '\n \n-test_expect_success 'not detach on re-checking out current branch' '\n+test_expect_success 'not die on re-checking out current branch' '\n \t(\n \t\tcd there &&\n-\t\tgit checkout newmaster &&\n-\t\tgit symbolic-ref HEAD\n+\t\tgit checkout newmaster\n \t)\n '\n \n@@ -66,7 +64,7 @@ test_expect_success 'checkout --to from a bare repo' '\n \t(\n \t\tgit clone --bare . bare &&\n \t\tcd bare &&\n-\t\tgit checkout --to ../there2 master\n+\t\tgit checkout --to ../there2 -b bare-master master\n \t)\n '\n \n-- \n1.9.1.346.ga2b5940\n"},{"id":"246550","messageId":"1406115795-24082-5-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406115795-24082-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/5] checkout --to: fix dangling pointers in remove_junk()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-23T11:43:14Z","receivedAt":"2014-07-23T11:43:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"junk_git_dir is set to sb_repo.buf. By the end of prepare_linked_checkout(),\nsb_repo is freed and so junk_git_dir points to nowhere. If the second\ncheckout command fails, is_junk remains non-zero, remove_junk() will\nbe called and try to clean junk_git_dir, which could be anything now\n(if it does not crash the program).\n\nThe new test may pass even without this patch. But it does fail under\nvalgrind (without this patch) with \"Invalid read of size 8\" at the\nright line.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c     | 15 ++++++++++-----\n t/t2025-checkout-to.sh |  6 ++++++\n 2 files changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex d35245a..e62c084 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -825,8 +825,8 @@ static int switch_branches(const struct checkout_opts *opts,\n \treturn ret || writeout_error;\n }\n \n-static const char *junk_work_tree;\n-static const char *junk_git_dir;\n+static char *junk_work_tree;\n+static char *junk_git_dir;\n static int is_junk;\n static pid_t junk_pid;\n \n@@ -895,7 +895,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,\n \n \tif (mkdir(sb_repo.buf, 0777))\n \t\tdie_errno(_(\"could not create directory of '%s'\"), sb_repo.buf);\n-\tjunk_git_dir = sb_repo.buf;\n+\tjunk_git_dir = xstrdup(sb_repo.buf);\n \tis_junk = 1;\n \n \t/*\n@@ -909,7 +909,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,\n \tif (safe_create_leading_directories_const(sb_git.buf))\n \t\tdie_errno(_(\"could not create leading directories of '%s'\"),\n \t\t\t  sb_git.buf);\n-\tjunk_work_tree = path;\n+\tjunk_work_tree = xstrdup(path);\n \n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/gitdir\", sb_repo.buf);\n@@ -939,8 +939,13 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,\n \tcp.git_cmd = 1;\n \tcp.argv = opts->saved_argv;\n \tret = run_command(&cp);\n-\tif (!ret)\n+\tif (!ret) {\n \t\tis_junk = 0;\n+\t\tfree(junk_work_tree);\n+\t\tfree(junk_git_dir);\n+\t\tjunk_work_tree = NULL;\n+\t\tjunk_git_dir = NULL;\n+\t}\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/locked\", sb_repo.buf);\n \tunlink_or_warn(sb.buf);\ndiff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\nindex c6601a4..8a00310 100755\n--- a/t/t2025-checkout-to.sh\n+++ b/t/t2025-checkout-to.sh\n@@ -12,6 +12,12 @@ test_expect_success 'checkout --to not updating paths' '\n \ttest_must_fail git checkout --to -- init.t\n '\n \n+test_expect_success 'checkout --to refuses to checkout locked branch' '\n+\ttest_must_fail git checkout --to zere master &&\n+\t! test -d zere &&\n+\t! test -d .git/repos/zere\n+'\n+\n test_expect_success 'checkout --to a new worktree' '\n \tgit rev-parse HEAD >expect &&\n \tgit checkout --detach --to here master &&\n-- \n1.9.1.346.ga2b5940\n"},{"id":"246551","messageId":"1406115795-24082-6-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406115795-24082-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/5] environment.c: fix incorrect git_graft_file initialization","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-23T11:43:15Z","receivedAt":"2014-07-23T11:43:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"info/grafts\" should be part of the \"common repository\" when accessed\nfrom a linked checkout (iow $GIT_COMMON_DIR/info/grafts, not\n$GIT_DIR/info/grafts).\n\ngit_path(\"info/grafts\") returns correctly, even without this fix,\nbecause it detects that $GIT_GRAFT_FILE is not set, so it goes with the\ncommon rule: anything except sparse-checkout in 'info' belongs to common\nrepo. But get_graft_file() would return a wrong value and that one is\nused for setting grafts up.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n environment.c          |  2 +-\n t/t0060-path-utils.sh  |  1 +\n t/t2025-checkout-to.sh | 18 ++++++++++++++++++\n 3 files changed, 20 insertions(+), 1 deletion(-)\n\ndiff --git a/environment.c b/environment.c\nindex 50ed40a..d5b0788 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -157,7 +157,7 @@ static void setup_git_env(void)\n \t\t\t\t\t   \"objects\", &git_db_env);\n \tgit_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,\n \t\t\t\t\t   \"index\", &git_index_env);\n-\tgit_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_dir,\n+\tgit_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_common_dir,\n \t\t\t\t\t   \"info/grafts\", &git_graft_env);\n \tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n \t\tcheck_replace_refs = 0;\ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex da82aab..93605f4 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -269,6 +269,7 @@ test_git_path GIT_COMMON_DIR=bar logs/HEAD                .git/logs/HEAD\n test_git_path GIT_COMMON_DIR=bar objects                  bar/objects\n test_git_path GIT_COMMON_DIR=bar objects/bar              bar/objects/bar\n test_git_path GIT_COMMON_DIR=bar info/exclude             bar/info/exclude\n+test_git_path GIT_COMMON_DIR=bar info/grafts              bar/info/grafts\n test_git_path GIT_COMMON_DIR=bar info/sparse-checkout     .git/info/sparse-checkout\n test_git_path GIT_COMMON_DIR=bar remotes/bar              bar/remotes/bar\n test_git_path GIT_COMMON_DIR=bar branches/bar             bar/branches/bar\ndiff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\nindex 8a00310..508993f 100755\n--- a/t/t2025-checkout-to.sh\n+++ b/t/t2025-checkout-to.sh\n@@ -81,4 +81,22 @@ test_expect_success 'checkout from a bare repo without --to' '\n \t)\n '\n \n+test_expect_success 'checkout with grafts' '\n+\ttest_when_finished rm .git/info/grafts &&\n+\ttest_commit abc &&\n+\tSHA1=`git rev-parse HEAD` &&\n+\ttest_commit def &&\n+\ttest_commit xyz &&\n+\techo \"`git rev-parse HEAD` $SHA1\" >.git/info/grafts &&\n+\tcat >expected <<-\\EOF &&\n+\txyz\n+\tabc\n+\tEOF\n+\tgit log --format=%s -2 >actual &&\n+\ttest_cmp expected actual &&\n+\tgit checkout --detach --to grafted master &&\n+\tgit --git-dir=grafted/.git log --format=%s -2 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.9.1.346.ga2b5940\n"},{"id":"246560","messageId":"53CFBD2A.9030803@drmicha.warpmail.net","threadId":"37194","inReplyTo":"1406115795-24082-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2014-07-23T13:48:26Z","receivedAt":"2014-07-23T13:48:26Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Nguyễn Thái Ngọc Duy venit, vidit, dixit 23.07.2014 13:43:\n> Give the user a choice in this case. If they want to detach, they can go\n> with '--detach --to ...', or they could switch branch of the checkout\n> that's holding the ref in question. Or they could just create a new\n> branch with '-b xxx --to yyy'\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/config.txt |  3 ++\n>  advice.c                 |  2 ++\n>  advice.h                 |  1 +\n>  builtin/checkout.c       | 73 ++++++++++++++++++++++++++++--------------------\n>  t/t2025-checkout-to.sh   | 16 +++++------\n>  5 files changed, 56 insertions(+), 39 deletions(-)\n> \n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 57999fa..4a41202 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -201,6 +201,9 @@ advice.*::\n>  \trmHints::\n>  \t\tIn case of failure in the output of linkgit:git-rm[1],\n>  \t\tshow directions on how to proceed from the current state.\n> +\tcheckoutTo::\n> +\t\tIn case of failure in the output of \"git checkout --to\",\n> +\t\tshow directions on how to proceed from the current state.\n>  --\n>  \n>  core.fileMode::\n> diff --git a/advice.c b/advice.c\n> index 9b42033..b1fb524 100644\n> --- a/advice.c\n> +++ b/advice.c\n> @@ -15,6 +15,7 @@ int advice_detached_head = 1;\n>  int advice_set_upstream_failure = 1;\n>  int advice_object_name_warning = 1;\n>  int advice_rm_hints = 1;\n> +int advice_checkout_to = 1;\n>  \n>  static struct {\n>  \tconst char *name;\n> @@ -35,6 +36,7 @@ static struct {\n>  \t{ \"setupstreamfailure\", &advice_set_upstream_failure },\n>  \t{ \"objectnamewarning\", &advice_object_name_warning },\n>  \t{ \"rmhints\", &advice_rm_hints },\n> +\t{ \"checkoutto\", &advice_checkout_to },\n>  \n>  \t/* make this an alias for backward compatibility */\n>  \t{ \"pushnonfastforward\", &advice_push_update_rejected }\n> diff --git a/advice.h b/advice.h\n> index 5ecc6c1..a288219 100644\n> --- a/advice.h\n> +++ b/advice.h\n> @@ -18,6 +18,7 @@ extern int advice_detached_head;\n>  extern int advice_set_upstream_failure;\n>  extern int advice_object_name_warning;\n>  extern int advice_rm_hints;\n> +extern int advice_checkout_to;\n>  \n>  int git_default_advice_config(const char *var, const char *value);\n>  __attribute__((format (printf, 1, 2)))\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index c83f476..d35245a 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -1006,31 +1006,52 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)\n>  \treturn NULL;\n>  }\n>  \n> -static int check_linked_checkout(struct branch_info *new,\n> -\t\t\t\t  const char *name, const char *path)\n> +static void check_linked_checkout(struct branch_info *new, const char *id)\n>  {\n>  \tstruct strbuf sb = STRBUF_INIT;\n> +\tstruct strbuf path = STRBUF_INIT;\n> +\tstruct strbuf gitdir = STRBUF_INIT;\n>  \tconst char *start, *end;\n> -\tif (strbuf_read_file(&sb, path, 0) < 0 ||\n> -\t    !skip_prefix(sb.buf, \"ref:\", &start)) {\n> -\t\tstrbuf_release(&sb);\n> -\t\treturn 0;\n> -\t}\n>  \n> +\tif (id)\n> +\t\tstrbuf_addf(&path, \"%s/repos/%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> +\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, new->path, end - start) &&\n> -\t    new->path[end - start] == '\\0') {\n> -\t\tstrbuf_release(&sb);\n> -\t\tnew->path = NULL; /* detach */\n> -\t\tnew->checkout = xstrdup(name); /* reason */\n> -\t\treturn 1;\n> -\t}\n> +\tif (strncmp(start, new->path, end - start) ||\n> +\t    new->path[end - start] != '\\0')\n> +\t\tgoto done;\n> +\tif (id) {\n> +\t\tstrbuf_reset(&path);\n> +\t\tstrbuf_addf(&path, \"%s/repos/%s/gitdir\",\n> +\t\t\t    get_git_common_dir(), id);\n> +\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n> +\t\t\tgoto done;\n> +\t\twhile (gitdir.len && (gitdir.buf[gitdir.len - 1] == '\\n' ||\n> +\t\t\t\t      gitdir.buf[gitdir.len - 1] == '\\r'))\n> +\t\t\tgitdir.buf[--gitdir.len] = '\\0';\n> +\t} else\n> +\t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n> +\tif (advice_checkout_to)\n> +\t\tdie(_(\"%s is already checked out at %s.\\n\"\n> +\t\t      \"Either use --detach or -b together with --to \"\n> +\t\t      \"or switch branch in the the other checkout.\"),\n\n\"or switch to a different branch in the other checkout\". But maybe we\ncan be even more helpful, like:\n\n\"%s is already checked out at %s.\\n\"\n\"Either checkout the detached head of branch %s using\\n\"\n\"    git checkout --detach --to %s %s\\n\"\n\"or checkout a new branch based off %s using\\n\"\n\"    git checkout --to %s -b %s newbranch %s\\n\"\n\"or switch to a different branch in the other checkout.\"\n\nif we can figure out the appropriate arguments at this point in the code.\n\n> +\t\t    new->path, gitdir.buf);\n> +\telse\n> +\t\tdie(_(\"%s is already checked out at %s.\"),\n> +\t\t    new->path, gitdir.buf);\n> +done:\n> +\tstrbuf_release(&path);\n>  \tstrbuf_release(&sb);\n> -\treturn 0;\n> +\tstrbuf_release(&gitdir);\n>  }\n>  \n>  static void check_linked_checkouts(struct branch_info *new)\n> @@ -1045,27 +1066,17 @@ static void check_linked_checkouts(struct branch_info *new)\n>  \t\treturn;\n>  \t}\n>  \n> -\tstrbuf_reset(&path);\n> -\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\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 (check_linked_checkout(new, \"\", path.buf)) {\n> -\t\tstrbuf_release(&path);\n> -\t\tclosedir(dir);\n> -\t\treturn;\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> -\t\tstrbuf_reset(&path);\n> -\t\tstrbuf_addf(&path, \"%s/repos/%s/HEAD\",\n> -\t\t\t    get_git_common_dir(), d->d_name);\n> -\t\tif (check_linked_checkout(new, d->d_name, path.buf))\n> -\t\t\tbreak;\n> +\t\tcheck_linked_checkout(new, d->d_name);\n>  \t}\n>  \tstrbuf_release(&path);\n>  \tclosedir(dir);\n> @@ -1076,7 +1087,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n>  \t\t\t\tstruct branch_info *new,\n>  \t\t\t\tstruct tree **source_tree,\n>  \t\t\t\tunsigned char rev[20],\n> -\t\t\t\tconst char **new_branch)\n> +\t\t\t\tconst char **new_branch,\n> +\t\t\t\tint force_detach)\n>  {\n>  \tint argcount = 0;\n>  \tunsigned char branch_rev[20];\n> @@ -1198,7 +1210,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n>  \telse\n>  \t\tnew->path = NULL; /* not an existing branch */\n>  \n> -\tif (new->path) {\n> +\tif (new->path && !force_detach && !*new_branch) {\n>  \t\tunsigned char sha1[20];\n>  \t\tint flag;\n>  \t\tchar *head_ref = resolve_refdup(\"HEAD\", sha1, 0, &flag);\n> @@ -1416,7 +1428,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t\t\t!opts.new_branch;\n>  \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n>  \t\t\t\t\t     &new, &opts.source_tree,\n> -\t\t\t\t\t     rev, &opts.new_branch);\n> +\t\t\t\t\t     rev, &opts.new_branch,\n> +\t\t\t\t\t     opts.force_detach);\n>  \t\targv += n;\n>  \t\targc -= n;\n>  \t}\n> diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\n> index b0d97a0..c6601a4 100755\n> --- a/t/t2025-checkout-to.sh\n> +++ b/t/t2025-checkout-to.sh\n> @@ -14,7 +14,7 @@ test_expect_success 'checkout --to not updating paths' '\n>  \n>  test_expect_success 'checkout --to a new worktree' '\n>  \tgit rev-parse HEAD >expect &&\n> -\tgit checkout --to here master &&\n> +\tgit checkout --detach --to here master &&\n>  \t(\n>  \t\tcd here &&\n>  \t\ttest_cmp ../init.t init.t &&\n> @@ -28,7 +28,7 @@ test_expect_success 'checkout --to a new worktree' '\n>  test_expect_success 'checkout --to from a linked checkout' '\n>  \t(\n>  \t\tcd here &&\n> -\t\tgit checkout --to nested-here master\n> +\t\tgit checkout --detach --to nested-here master\n>  \t\tcd nested-here &&\n>  \t\tgit fsck\n>  \t)\n> @@ -46,19 +46,17 @@ test_expect_success 'checkout --to a new worktree creating new branch' '\n>  \t)\n>  '\n>  \n> -test_expect_success 'detach if the same branch is already checked out' '\n> +test_expect_success 'die the same branch is already checked out' '\n>  \t(\n>  \t\tcd here &&\n> -\t\tgit checkout newmaster &&\n> -\t\ttest_must_fail git symbolic-ref HEAD\n> +\t\ttest_must_fail git checkout newmaster\n>  \t)\n>  '\n>  \n> -test_expect_success 'not detach on re-checking out current branch' '\n> +test_expect_success 'not die on re-checking out current branch' '\n>  \t(\n>  \t\tcd there &&\n> -\t\tgit checkout newmaster &&\n> -\t\tgit symbolic-ref HEAD\n> +\t\tgit checkout newmaster\n>  \t)\n>  '\n>  \n> @@ -66,7 +64,7 @@ test_expect_success 'checkout --to from a bare repo' '\n>  \t(\n>  \t\tgit clone --bare . bare &&\n>  \t\tcd bare &&\n> -\t\tgit checkout --to ../there2 master\n> +\t\tgit checkout --to ../there2 -b bare-master master\n>  \t)\n>  '\n>  \n> \n"},{"id":"246579","messageId":"xmqqmwc0dl46.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"53CFBD2A.9030803@drmicha.warpmail.net","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-23T17:46:01Z","receivedAt":"2014-07-23T17:46:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael J Gruber <git@drmicha.warpmail.net> writes:\n\n>> +\tif (advice_checkout_to)\n>> +\t\tdie(_(\"%s is already checked out at %s.\\n\"\n>> +\t\t      \"Either use --detach or -b together with --to \"\n>> +\t\t      \"or switch branch in the the other checkout.\"),\n>\n> \"or switch to a different branch in the other checkout\". But maybe we\n> can be even more helpful, like:\n>\n> \"%s is already checked out at %s.\\n\"\n> \"Either checkout the detached head of branch %s using\\n\"\n> \"    git checkout --detach --to %s %s\\n\"\n> \"or checkout a new branch based off %s using\\n\"\n> \"    git checkout --to %s -b %s newbranch %s\\n\"\n> \"or switch to a different branch in the other checkout.\"\n>\n> if we can figure out the appropriate arguments at this point in the code.\n\nAnother possible alternative is \"go and work there instead of\ncreating yet another checkout\", but is that too obvious without\nsaying?\n"},{"id":"246601","messageId":"xmqq61inetib.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"1406115795-24082-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 2/5] prune --repos: fix uninitialized access","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-23T19:59:24Z","receivedAt":"2014-07-23T19:59:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> There's a code path in prune_repo_dir() that does not initialize 'st'\n> buffer, which is checked by the caller, prune_repos_dir(). Instead\n> of leaking some prune logic out to prune_repos_dir(), move 'st' into\n> prune_repo_dir().\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThe return value from the function used to be \"should .git/repos/$x\nbe removed if it is stale enough?\" and now it is just \"should it be\nremoved?\", so the change makes sense.\n\nIt does not explain what the change to the test is about, though.\n\nThanks.\n\n>  builtin/prune.c                   | 16 ++++++----------\n>  t/t2026-prune-linked-checkouts.sh |  2 +-\n>  2 files changed, 7 insertions(+), 11 deletions(-)\n>\n> diff --git a/builtin/prune.c b/builtin/prune.c\n> index 28b7adf..e72c391 100644\n> --- a/builtin/prune.c\n> +++ b/builtin/prune.c\n> @@ -112,8 +112,9 @@ static void prune_object_dir(const char *path)\n>  \t}\n>  }\n>  \n> -static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason)\n> +static int prune_repo_dir(const char *id, struct strbuf *reason)\n>  {\n> +\tstruct stat st;\n>  \tchar *path;\n>  \tint fd, len;\n>  \n> @@ -123,26 +124,23 @@ static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason\n>  \t}\n>  \tif (file_exists(git_path(\"repos/%s/locked\", id)))\n>  \t\treturn 0;\n> -\tif (stat(git_path(\"repos/%s/gitdir\", id), st)) {\n> -\t\tst->st_mtime = expire;\n> +\tif (stat(git_path(\"repos/%s/gitdir\", id), &st)) {\n>  \t\tstrbuf_addf(reason, _(\"Removing repos/%s: gitdir file does not exist\"), id);\n>  \t\treturn 1;\n>  \t}\n>  \tfd = open(git_path(\"repos/%s/gitdir\", id), O_RDONLY);\n>  \tif (fd < 0) {\n> -\t\tst->st_mtime = expire;\n>  \t\tstrbuf_addf(reason, _(\"Removing repos/%s: unable to read gitdir file (%s)\"),\n>  \t\t\t    id, strerror(errno));\n>  \t\treturn 1;\n>  \t}\n> -\tlen = st->st_size;\n> +\tlen = st.st_size;\n>  \tpath = xmalloc(len + 1);\n>  \tread_in_full(fd, path, len);\n>  \tclose(fd);\n>  \twhile (len && (path[len - 1] == '\\n' || path[len - 1] == '\\r'))\n>  \t\tlen--;\n>  \tif (!len) {\n> -\t\tst->st_mtime = expire;\n>  \t\tstrbuf_addf(reason, _(\"Removing repos/%s: invalid gitdir file\"), id);\n>  \t\tfree(path);\n>  \t\treturn 1;\n> @@ -162,7 +160,7 @@ static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason\n>  \t\treturn 1;\n>  \t}\n>  \tfree(path);\n> -\treturn 0;\n> +\treturn st.st_mtime <= expire;\n>  }\n>  \n>  static void prune_repos_dir(void)\n> @@ -172,15 +170,13 @@ static void prune_repos_dir(void)\n>  \tDIR *dir = opendir(git_path(\"repos\"));\n>  \tstruct dirent *d;\n>  \tint ret;\n> -\tstruct stat st;\n>  \tif (!dir)\n>  \t\treturn;\n>  \twhile ((d = readdir(dir)) != NULL) {\n>  \t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n>  \t\t\tcontinue;\n>  \t\tstrbuf_reset(&reason);\n> -\t\tif (!prune_repo_dir(d->d_name, &st, &reason) ||\n> -\t\t    st.st_mtime > expire)\n> +\t\tif (!prune_repo_dir(d->d_name, &reason))\n>  \t\t\tcontinue;\n>  \t\tif (show_only || verbose)\n>  \t\t\tprintf(\"%s\\n\", reason.buf);\n> diff --git a/t/t2026-prune-linked-checkouts.sh b/t/t2026-prune-linked-checkouts.sh\n> index 4ccfa4e..79d84cb 100755\n> --- a/t/t2026-prune-linked-checkouts.sh\n> +++ b/t/t2026-prune-linked-checkouts.sh\n> @@ -77,7 +77,7 @@ test_expect_success 'not prune recent checkouts' '\n>  \tmkdir zz &&\n>  \tmkdir -p .git/repos/jlm &&\n>  \techo \"$TRASH_DIRECTORY\"/zz >.git/repos/jlm/gitdir &&\n> -\tgit prune --repos --verbose &&\n> +\tgit prune --repos --verbose --expire=2.days.ago &&\n>  \ttest -d .git/repos/jlm\n>  '\n"},{"id":"246605","messageId":"xmqqzjfzdbds.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"1406115795-24082-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-23T21:16:15Z","receivedAt":"2014-07-23T21:16:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index c83f476..d35245a 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -1006,31 +1006,52 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)\n>  \treturn NULL;\n>  }\n>  \n> -static int check_linked_checkout(struct branch_info *new,\n> -\t\t\t\t  const char *name, const char *path)\n> +static void check_linked_checkout(struct branch_info *new, const char *id)\n>  {\n>  \tstruct strbuf sb = STRBUF_INIT;\n> +\tstruct strbuf path = STRBUF_INIT;\n> +\tstruct strbuf gitdir = STRBUF_INIT;\n>  \tconst char *start, *end;\n> -\tif (strbuf_read_file(&sb, path, 0) < 0 ||\n> -\t    !skip_prefix(sb.buf, \"ref:\", &start)) {\n> -\t\tstrbuf_release(&sb);\n> -\t\treturn 0;\n> -\t}\n>  \n> +\tif (id)\n> +\t\tstrbuf_addf(&path, \"%s/repos/%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> +\t\tgoto done;\n>  \twhile (isspace(*start))\n>  \t\tstart++;\n>  \tend = start;\n>  \twhile (*end && !isspace(*end))\n>  \t\tend++;\n\nNot new in this round of update, and may not even be an issue, but:\n\n - Earlier, the code returned early on a negative return value from\n   read-file (i.e., an error), but this round it also does so for\n   zero.  Intended?\n\n - The above duplicates the logic in resolve_ref_unsafe() and\n   resolve_gitlink_ref_recursive(); three places now knows what a\n   textual symref looks like (i.e. begins with \"ref:\", zero or more\n   whitespaces, the target ref and then zero or more trailing\n   whitespaces).  Perhaps we need to consolidate the code further,\n   so that this knowledge does not leak out of refs.c?\n\n> +\tif (strncmp(start, new->path, end - start) ||\n> +\t    new->path[end - start] != '\\0')\n> +\t\tgoto done;\n> +\tif (id) {\n> +\t\tstrbuf_reset(&path);\n> +\t\tstrbuf_addf(&path, \"%s/repos/%s/gitdir\",\n> +\t\t\t    get_git_common_dir(), id);\n> +\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n> +\t\t\tgoto done;\n> +\t\twhile (gitdir.len && (gitdir.buf[gitdir.len - 1] == '\\n' ||\n> +\t\t\t\t      gitdir.buf[gitdir.len - 1] == '\\r'))\n> +\t\t\tgitdir.buf[--gitdir.len] = '\\0';\n\nAccepting arbitrary numbers of '\\r' and '\\n' sounds as if the code\nis allowing it, but text editors would not end their files with a\nnonsense sequence like \"\\r\\r\\n\\r\" unless the end-user works to do\nso, and if you are prepared to be lenient to noisy human input, not\ntrimming trailing whitespaces does not look it is going far enough\nto help them.\n\nI do not see a good reason to allow random text editors to edit this\nfile in the first place, so my preference is:\n\n\tif (strbuf_read_file(...) < 0 ||\n\t    gitdir.len == 0 ||\n            gitdir.buf[gitdir.len - 1] != '\\n')\n            goto error_return;\n\tgitdir.buf[--gitdir.len] = '\\0';\n\nAlternatively, if you are trying to be lenient to human input, I\nwould understand:\n\n\tif (strbuf_read_file(...) < 0)\n        \tgoto error_return;\n\tstrbuf_rtrim(&gitdir);\n\nThe code in the patch, which is something in between, does not make\nmuch sense to me.\n"},{"id":"246607","messageId":"xmqqvbqndb2z.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"1406115795-24082-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 5/5] environment.c: fix incorrect git_graft_file initialization","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-23T21:22:44Z","receivedAt":"2014-07-23T21:22:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> \"info/grafts\" should be part of the \"common repository\" when accessed\n> from a linked checkout (iow $GIT_COMMON_DIR/info/grafts, not\n> $GIT_DIR/info/grafts).\n>\n> git_path(\"info/grafts\") returns correctly, even without this fix,\n> because it detects that $GIT_GRAFT_FILE is not set, so it goes with the\n> common rule: anything except sparse-checkout in 'info' belongs to common\n> repo. But get_graft_file() would return a wrong value and that one is\n> used for setting grafts up.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThanks Eric for sharp eyes and Duy for a quick fix.\n\nWill queue all five.\n\n>  environment.c          |  2 +-\n>  t/t0060-path-utils.sh  |  1 +\n>  t/t2025-checkout-to.sh | 18 ++++++++++++++++++\n>  3 files changed, 20 insertions(+), 1 deletion(-)\n>\n> diff --git a/environment.c b/environment.c\n> index 50ed40a..d5b0788 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -157,7 +157,7 @@ static void setup_git_env(void)\n>  \t\t\t\t\t   \"objects\", &git_db_env);\n>  \tgit_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,\n>  \t\t\t\t\t   \"index\", &git_index_env);\n> -\tgit_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_dir,\n> +\tgit_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_common_dir,\n>  \t\t\t\t\t   \"info/grafts\", &git_graft_env);\n>  \tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n>  \t\tcheck_replace_refs = 0;\n> diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\n> index da82aab..93605f4 100755\n> --- a/t/t0060-path-utils.sh\n> +++ b/t/t0060-path-utils.sh\n> @@ -269,6 +269,7 @@ test_git_path GIT_COMMON_DIR=bar logs/HEAD                .git/logs/HEAD\n>  test_git_path GIT_COMMON_DIR=bar objects                  bar/objects\n>  test_git_path GIT_COMMON_DIR=bar objects/bar              bar/objects/bar\n>  test_git_path GIT_COMMON_DIR=bar info/exclude             bar/info/exclude\n> +test_git_path GIT_COMMON_DIR=bar info/grafts              bar/info/grafts\n>  test_git_path GIT_COMMON_DIR=bar info/sparse-checkout     .git/info/sparse-checkout\n>  test_git_path GIT_COMMON_DIR=bar remotes/bar              bar/remotes/bar\n>  test_git_path GIT_COMMON_DIR=bar branches/bar             bar/branches/bar\n> diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\n> index 8a00310..508993f 100755\n> --- a/t/t2025-checkout-to.sh\n> +++ b/t/t2025-checkout-to.sh\n> @@ -81,4 +81,22 @@ test_expect_success 'checkout from a bare repo without --to' '\n>  \t)\n>  '\n>  \n> +test_expect_success 'checkout with grafts' '\n> +\ttest_when_finished rm .git/info/grafts &&\n> +\ttest_commit abc &&\n> +\tSHA1=`git rev-parse HEAD` &&\n> +\ttest_commit def &&\n> +\ttest_commit xyz &&\n> +\techo \"`git rev-parse HEAD` $SHA1\" >.git/info/grafts &&\n> +\tcat >expected <<-\\EOF &&\n> +\txyz\n> +\tabc\n> +\tEOF\n> +\tgit log --format=%s -2 >actual &&\n> +\ttest_cmp expected actual &&\n> +\tgit checkout --detach --to grafted master &&\n> +\tgit --git-dir=grafted/.git log --format=%s -2 >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n>  test_done\n"},{"id":"246634","messageId":"CACsJy8DwPM68j-=LDDbq1H_bT1gD=aLQ8x6C1qiwF79=ai14dg@mail.gmail.com","threadId":"37194","inReplyTo":"53CFBD2A.9030803@drmicha.warpmail.net","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-24T09:58:31Z","receivedAt":"2014-07-24T09:58:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Jul 23, 2014 at 8:48 PM, Michael J Gruber\n<git@drmicha.warpmail.net> wrote:\n> Nguyễn Thái Ngọc Duy venit, vidit, dixit 23.07.2014 13:43:\n>> +     if (advice_checkout_to)\n>> +             die(_(\"%s is already checked out at %s.\\n\"\n>> +                   \"Either use --detach or -b together with --to \"\n>> +                   \"or switch branch in the the other checkout.\"),\n>\n> \"or switch to a different branch in the other checkout\". But maybe we\n> can be even more helpful, like:\n>\n> \"%s is already checked out at %s.\\n\"\n> \"Either checkout the detached head of branch %s using\\n\"\n> \"    git checkout --detach --to %s %s\\n\"\n> \"or checkout a new branch based off %s using\\n\"\n> \"    git checkout --to %s -b %s newbranch %s\\n\"\n> \"or switch to a different branch in the other checkout.\"\n>\n> if we can figure out the appropriate arguments at this point in the code.\n\nWe would not be able to construct the exact command that the user has\nentered, so perhaps\n\n  git checkout --detach <more options> %s\n  git checkout -b <new branch> <more options> %s\n\n?\n\nNote that this does not only occur when --to is given. If you have two\ncheckouts associated to the same repo, \"git checkout foo\" on one\ncheckout when \"foo\" is held by another checkout would cause the same\nerror. I'll need to think of a better name than advice.checkoutTo.\n-- \nDuy\n"},{"id":"246635","messageId":"CACsJy8CTYyjqmwGgkKZQFX4YC9-X=TGx9Ex5QfNWRNKFjgpKag@mail.gmail.com","threadId":"37194","inReplyTo":"xmqqzjfzdbds.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-24T10:09:28Z","receivedAt":"2014-07-24T10:09:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jul 24, 2014 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> +     if (strbuf_read_file(&sb, path.buf, 0) <= 0 ||\n>> +         !skip_prefix(sb.buf, \"ref:\", &start))\n>> +             goto done;\n>>       while (isspace(*start))\n>>               start++;\n>>       end = start;\n>>       while (*end && !isspace(*end))\n>>               end++;\n>\n> Not new in this round of update, and may not even be an issue, but:\n>\n>  - Earlier, the code returned early on a negative return value from\n>    read-file (i.e., an error), but this round it also does so for\n>    zero.  Intended?\n\nYes. But it does not make any difference. strbuf_read_file returns\nsb.len, if it's empty, the next skip_prefix would fail anyway.\n\n>  - The above duplicates the logic in resolve_ref_unsafe() and\n>    resolve_gitlink_ref_recursive(); three places now knows what a\n>    textual symref looks like (i.e. begins with \"ref:\", zero or more\n>    whitespaces, the target ref and then zero or more trailing\n>    whitespaces).  Perhaps we need to consolidate the code further,\n>    so that this knowledge does not leak out of refs.c?\n\nOK. Will do (unless Mike has a different opinion about this).\n\n>\n>> +     if (strncmp(start, new->path, end - start) ||\n>> +         new->path[end - start] != '\\0')\n>> +             goto done;\n>> +     if (id) {\n>> +             strbuf_reset(&path);\n>> +             strbuf_addf(&path, \"%s/repos/%s/gitdir\",\n>> +                         get_git_common_dir(), id);\n>> +             if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n>> +                     goto done;\n>> +             while (gitdir.len && (gitdir.buf[gitdir.len - 1] == '\\n' ||\n>> +                                   gitdir.buf[gitdir.len - 1] == '\\r'))\n>> +                     gitdir.buf[--gitdir.len] = '\\0';\n>\n> Accepting arbitrary numbers of '\\r' and '\\n' sounds as if the code\n> is allowing it, but text editors would not end their files with a\n> nonsense sequence like \"\\r\\r\\n\\r\" unless the end-user works to do\n> so, and if you are prepared to be lenient to noisy human input, not\n> trimming trailing whitespaces does not look it is going far enough\n> to help them.\n>\n> I do not see a good reason to allow random text editors to edit this\n> file in the first place, so my preference is:\n>\n>         if (strbuf_read_file(...) < 0 ||\n>             gitdir.len == 0 ||\n>             gitdir.buf[gitdir.len - 1] != '\\n')\n>             goto error_return;\n>         gitdir.buf[--gitdir.len] = '\\0';\n>\n> Alternatively, if you are trying to be lenient to human input, I\n> would understand:\n>\n>         if (strbuf_read_file(...) < 0)\n>                 goto error_return;\n>         strbuf_rtrim(&gitdir);\n>\n> The code in the patch, which is something in between, does not make\n> much sense to me.\n\nI think more about \"echo abc > $this_file\" where the echo command may\noutput '\\r\\n' on Windows (wild guess though, I don't use Windows\nmuch). I think I'm going with _rtrim.\n-- \nDuy\n"},{"id":"246636","messageId":"CACsJy8BHnE83qddHaAJjKEv_J=VvvyMPeDbTmiFXatJeLwwGHw@mail.gmail.com","threadId":"37194","inReplyTo":"xmqq61inetib.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 2/5] prune --repos: fix uninitialized access","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-24T10:14:15Z","receivedAt":"2014-07-24T10:14:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jul 24, 2014 at 2:59 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> It does not explain what the change to the test is about, though.\n\nThe old test and code are wrong. We want to verify repos are not\npruned if they're new (like, just created). But \"git prune\" without\n--expire sets expire time to 0xffffffff (everything will be pruned) so\ntoday's repos are pruned as well. But the old code is wrong. It\nreturns 0 at the end of prune_repo_dir instead of1, so st.st_mtime is\nnever checked, the repos are not pruned. The end result is an\nincorrect passed test :(\n-- \nDuy\n"},{"id":"246649","messageId":"xmqqr41aaey0.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"CACsJy8CTYyjqmwGgkKZQFX4YC9-X=TGx9Ex5QfNWRNKFjgpKag@mail.gmail.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-24T16:39:51Z","receivedAt":"2014-07-24T16:39:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Thu, Jul 24, 2014 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> +     if (strbuf_read_file(&sb, path.buf, 0) <= 0 ||\n>>> +         !skip_prefix(sb.buf, \"ref:\", &start))\n>>> +             goto done;\n>>>       while (isspace(*start))\n>>>               start++;\n>>>       end = start;\n>>>       while (*end && !isspace(*end))\n>>>               end++;\n>>\n>> Not new in this round of update, and may not even be an issue, but:\n>>\n>>  - Earlier, the code returned early on a negative return value from\n>>    read-file (i.e., an error), but this round it also does so for\n>>    zero.  Intended?\n>\n> Yes. But it does not make any difference. strbuf_read_file returns\n> sb.len, if it's empty, the next skip_prefix would fail anyway.\n\nYes but changing < 0 to <= 0 is inconsistent with that; I would\nunderstand if you changed it to <= 4, which would be consistent with\nthe reasoning, though.\n\n>> The code in the patch, which is something in between, does not make\n>> much sense to me.\n>\n> I think more about \"echo abc > $this_file\" where the echo command may\n> output '\\r\\n' on Windows (wild guess though, I don't use Windows\n> much). I think I'm going with _rtrim.\n\nTo expect 'echo' into the file is to expect and encourage that\npeople muck with the internal implementation details by hand, which\nwe do not generally do for things inside .git [*1*].\n\nIf we consider the contents of $this_file not an implementation\ndetail but a part of the published API (i.e. \"writing this string\ninto the file is a valid way to make Git do this\"), rtrim would at\nleast be consistent with how the existing code deals with symrefs,\nso I wouldn't say \"does not make much sense\" if you are going in\nthat direction ;-)\n\n\n[Footnote]\n\n*1* ... except for .git/config, to which we say \"It's a simple text\nfile; don't be afraied to edit it away!\".\n"},{"id":"246654","messageId":"xmqq61imaamd.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"xmqqr41aaey0.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-24T18:13:14Z","receivedAt":"2014-07-24T18:13:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n>> On Thu, Jul 24, 2014 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>> +     if (strbuf_read_file(&sb, path.buf, 0) <= 0 ||\n>>>> +         !skip_prefix(sb.buf, \"ref:\", &start))\n>>>> +             goto done;\n>>>>       while (isspace(*start))\n>>>>               start++;\n>>>>       end = start;\n>>>>       while (*end && !isspace(*end))\n>>>>               end++;\n>>>\n>>> Not new in this round of update, and may not even be an issue, but:\n>>>\n>>>  - Earlier, the code returned early on a negative return value from\n>>>    read-file (i.e., an error), but this round it also does so for\n>>>    zero.  Intended?\n>>\n>> Yes. But it does not make any difference. strbuf_read_file returns\n>> sb.len, if it's empty, the next skip_prefix would fail anyway.\n>\n> Yes but changing < 0 to <= 0 is inconsistent with that; I would\n> understand if you changed it to <= 4, which would be consistent with\n> the reasoning, though.\n\nJust to make sure.  I am not saying changing < 0  to <= 4 is a good\nidea.  I think checking for strbuf_read_file() failure with < 0 and\nthen checking for malformatted input (or detached head perhaps?)\nwith !skip_prefix(), i.e. testing two logically separate things with\ntwo separate conditions concatenated together with ||, would be the\nmost natural way to express it.\n"},{"id":"246669","messageId":"xmqq8uni8mx4.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"CACsJy8DwPM68j-=LDDbq1H_bT1gD=aLQ8x6C1qiwF79=ai14dg@mail.gmail.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-24T21:30:31Z","receivedAt":"2014-07-24T21:30:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Wed, Jul 23, 2014 at 8:48 PM, Michael J Gruber\n> <git@drmicha.warpmail.net> wrote:\n>> Nguyễn Thái Ngọc Duy venit, vidit, dixit 23.07.2014 13:43:\n>>> +     if (advice_checkout_to)\n>>> +             die(_(\"%s is already checked out at %s.\\n\"\n>>> +                   \"Either use --detach or -b together with --to \"\n>>> +                   \"or switch branch in the the other checkout.\"),\n>>\n>> \"or switch to a different branch in the other checkout\". But maybe we\n>> can be even more helpful, like:\n>>\n>> \"%s is already checked out at %s.\\n\"\n>> \"Either checkout the detached head of branch %s using\\n\"\n>> \"    git checkout --detach --to %s %s\\n\"\n>> \"or checkout a new branch based off %s using\\n\"\n>> \"    git checkout --to %s -b %s newbranch %s\\n\"\n>> \"or switch to a different branch in the other checkout.\"\n>>\n>> if we can figure out the appropriate arguments at this point in the code.\n>\n> We would not be able to construct the exact command that the user has\n> entered, so perhaps\n>\n>   git checkout --detach <more options> %s\n>   git checkout -b <new branch> <more options> %s\n>\n> ?\n>\n> Note that this does not only occur when --to is given. If you have two\n> checkouts associated to the same repo, \"git checkout foo\" on one\n> checkout when \"foo\" is held by another checkout would cause the same\n> error. I'll need to think of a better name than advice.checkoutTo.\n\nI am not sure if we need to say anything more than the first line of\nthe message you have in your patch.  To fork a new branch at the\ncommit the user is interested in to check it out, or not bothering\nwith the branch and detach, are both very normal parts of user's Git\ntoolchest, nothing particularly special.  As long as the most\nimportant point, i.e. \"in the new world order, unlike the\ncontrib/workdir hack, you cannot check out the same branch at two\ndifferent places\", is clearly conveyed and understood, everything\nelse should follow naturally, no?\n"},{"id":"246681","messageId":"53D1FE76.5080708@drmicha.warpmail.net","threadId":"37194","inReplyTo":"xmqq8uni8mx4.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2014-07-25T06:51:34Z","receivedAt":"2014-07-25T06:51:34Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Junio C Hamano venit, vidit, dixit 24.07.2014 23:30:\n> Duy Nguyen <pclouds@gmail.com> writes:\n> \n>> On Wed, Jul 23, 2014 at 8:48 PM, Michael J Gruber\n>> <git@drmicha.warpmail.net> wrote:\n>>> Nguyễn Thái Ngọc Duy venit, vidit, dixit 23.07.2014 13:43:\n>>>> +     if (advice_checkout_to)\n>>>> +             die(_(\"%s is already checked out at %s.\\n\"\n>>>> +                   \"Either use --detach or -b together with --to \"\n>>>> +                   \"or switch branch in the the other checkout.\"),\n>>>\n>>> \"or switch to a different branch in the other checkout\". But maybe we\n>>> can be even more helpful, like:\n>>>\n>>> \"%s is already checked out at %s.\\n\"\n>>> \"Either checkout the detached head of branch %s using\\n\"\n>>> \"    git checkout --detach --to %s %s\\n\"\n>>> \"or checkout a new branch based off %s using\\n\"\n>>> \"    git checkout --to %s -b %s newbranch %s\\n\"\n>>> \"or switch to a different branch in the other checkout.\"\n>>>\n>>> if we can figure out the appropriate arguments at this point in the code.\n>>\n>> We would not be able to construct the exact command that the user has\n>> entered, so perhaps\n>>\n>>   git checkout --detach <more options> %s\n>>   git checkout -b <new branch> <more options> %s\n>>\n>> ?\n>>\n>> Note that this does not only occur when --to is given. If you have two\n>> checkouts associated to the same repo, \"git checkout foo\" on one\n>> checkout when \"foo\" is held by another checkout would cause the same\n>> error. I'll need to think of a better name than advice.checkoutTo.\n> \n> I am not sure if we need to say anything more than the first line of\n> the message you have in your patch.  To fork a new branch at the\n> commit the user is interested in to check it out, or not bothering\n> with the branch and detach, are both very normal parts of user's Git\n> toolchest, nothing particularly special.  As long as the most\n> important point, i.e. \"in the new world order, unlike the\n> contrib/workdir hack, you cannot check out the same branch at two\n> different places\", is clearly conveyed and understood, everything\n> else should follow naturally, no?\n\nAs an error message that is completely sufficient.\n\nThe advice messages are meant to teach the user about the normal parts\nof the toolchest to use in a situation of \"conflict\", aren't they? Maybe\nwe should ask someone who hasn't turned them off...\n\nMichael\n"},{"id":"246910","messageId":"1406641831-2390-1-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406115795-24082-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 0/8] nd/multiple-work-trees follow-ups","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:23Z","receivedAt":"2014-07-29T13:50:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This fixes all comments from v1. I still keep the long advice when\n'git checkout' fails because another branch is locked, because it's\neasy(*) to disable the advice. Two more fixes found today. The test\nbreakages on Windows have to wait until somebody on Windows jumps in.\n\n(*) although I start to want an advice.nothing config that disables\nall advice..\n\nNguyễn Thái Ngọc Duy (8):\n  gitrepository-layout.txt: s/ignored/ignored if/\n  checkout: no need to call check_linked_checkouts if head_ref is NULL\n  prune --repos: fix uninitialized access\n  checkout: no auto-detach if the ref is already checked out\n  checkout --to: fix dangling pointers in remove_junk()\n  environment.c: fix incorrect git_graft_file initialization\n  checkout: prefix --to argument properly when cwd is moved\n  checkout --to: do not touch existing target directory\n\n Documentation/config.txt               |  4 ++\n Documentation/gitrepository-layout.txt |  6 +--\n advice.c                               |  2 +\n advice.h                               |  1 +\n builtin/checkout.c                     | 98 +++++++++++++++++++---------------\n builtin/prune.c                        | 16 +++---\n environment.c                          |  2 +-\n t/t0060-path-utils.sh                  |  1 +\n t/t2025-checkout-to.sh                 | 40 ++++++++++----\n t/t2026-prune-linked-checkouts.sh      |  2 +-\n 10 files changed, 106 insertions(+), 66 deletions(-)\n\n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246911","messageId":"1406641831-2390-2-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 1/8] gitrepository-layout.txt: s/ignored/ignored if/","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:24Z","receivedAt":"2014-07-29T13:50:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitrepository-layout.txt | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex bed4f1a..6bd82af 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -139,7 +139,7 @@ is often called 'detached HEAD.'  See linkgit:git-checkout[1]\n for details.\n \n config::\n-\tRepository specific configuration file. This file is ignored\n+\tRepository specific configuration file. This file is ignored if\n \t$GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/config\" will be\n \tused instead.\n \n@@ -220,7 +220,7 @@ remotes::\n logs::\n \tRecords of changes made to refs are stored in this\n \tdirectory.  See linkgit:git-update-ref[1]\n-\tfor more information. This directory is ignored\n+\tfor more information. This directory is ignored if\n \t$GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/logs\" will be used\n \tinstead.\n \n@@ -252,7 +252,7 @@ modules::\n repos::\n \tContains worktree specific information of linked\n \tcheckouts. Each subdirectory contains the worktree-related\n-\tpart of a linked checkout. This directory is ignored\n+\tpart of a linked checkout. This directory is ignored if\n \t$GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/repos\" will be\n \tused instead.\n \n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246912","messageId":"1406641831-2390-3-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 2/8] checkout: no need to call check_linked_checkouts if head_ref is NULL","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:25Z","receivedAt":"2014-07-29T13:50:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c83f476..6ac89eb 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1202,7 +1202,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\tunsigned char sha1[20];\n \t\tint flag;\n \t\tchar *head_ref = resolve_refdup(\"HEAD\", sha1, 0, &flag);\n-\t\tif (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))\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\tfree(head_ref);\n \t}\n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246913","messageId":"1406641831-2390-4-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 3/8] prune --repos: fix uninitialized access","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:26Z","receivedAt":"2014-07-29T13:50:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"There's a code path in prune_repo_dir() that does not initialize 'st'\nbuffer, which is checked by the caller, prune_repos_dir(). Instead\nof leaking some prune logic out to prune_repos_dir(), move 'st' into\nprune_repo_dir().\n\nAnother bug that is fixed while at there is the \"return 0\" at the end\nof prune_repo_dir() instead of '1', meaning \"keep the checkout\" while\nwe want \"keep the checkout _unless_ its last update is older than\nexpire limit\". Set correct expire limit in the test as well.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/prune.c                   | 16 ++++++----------\n t/t2026-prune-linked-checkouts.sh |  2 +-\n 2 files changed, 7 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex 28b7adf..e72c391 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -112,8 +112,9 @@ static void prune_object_dir(const char *path)\n \t}\n }\n \n-static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason)\n+static int prune_repo_dir(const char *id, struct strbuf *reason)\n {\n+\tstruct stat st;\n \tchar *path;\n \tint fd, len;\n \n@@ -123,26 +124,23 @@ static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason\n \t}\n \tif (file_exists(git_path(\"repos/%s/locked\", id)))\n \t\treturn 0;\n-\tif (stat(git_path(\"repos/%s/gitdir\", id), st)) {\n-\t\tst->st_mtime = expire;\n+\tif (stat(git_path(\"repos/%s/gitdir\", id), &st)) {\n \t\tstrbuf_addf(reason, _(\"Removing repos/%s: gitdir file does not exist\"), id);\n \t\treturn 1;\n \t}\n \tfd = open(git_path(\"repos/%s/gitdir\", id), O_RDONLY);\n \tif (fd < 0) {\n-\t\tst->st_mtime = expire;\n \t\tstrbuf_addf(reason, _(\"Removing repos/%s: unable to read gitdir file (%s)\"),\n \t\t\t    id, strerror(errno));\n \t\treturn 1;\n \t}\n-\tlen = st->st_size;\n+\tlen = st.st_size;\n \tpath = xmalloc(len + 1);\n \tread_in_full(fd, path, len);\n \tclose(fd);\n \twhile (len && (path[len - 1] == '\\n' || path[len - 1] == '\\r'))\n \t\tlen--;\n \tif (!len) {\n-\t\tst->st_mtime = expire;\n \t\tstrbuf_addf(reason, _(\"Removing repos/%s: invalid gitdir file\"), id);\n \t\tfree(path);\n \t\treturn 1;\n@@ -162,7 +160,7 @@ static int prune_repo_dir(const char *id, struct stat *st, struct strbuf *reason\n \t\treturn 1;\n \t}\n \tfree(path);\n-\treturn 0;\n+\treturn st.st_mtime <= expire;\n }\n \n static void prune_repos_dir(void)\n@@ -172,15 +170,13 @@ static void prune_repos_dir(void)\n \tDIR *dir = opendir(git_path(\"repos\"));\n \tstruct dirent *d;\n \tint ret;\n-\tstruct stat st;\n \tif (!dir)\n \t\treturn;\n \twhile ((d = readdir(dir)) != NULL) {\n \t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n \t\t\tcontinue;\n \t\tstrbuf_reset(&reason);\n-\t\tif (!prune_repo_dir(d->d_name, &st, &reason) ||\n-\t\t    st.st_mtime > expire)\n+\t\tif (!prune_repo_dir(d->d_name, &reason))\n \t\t\tcontinue;\n \t\tif (show_only || verbose)\n \t\t\tprintf(\"%s\\n\", reason.buf);\ndiff --git a/t/t2026-prune-linked-checkouts.sh b/t/t2026-prune-linked-checkouts.sh\nindex 4ccfa4e..79d84cb 100755\n--- a/t/t2026-prune-linked-checkouts.sh\n+++ b/t/t2026-prune-linked-checkouts.sh\n@@ -77,7 +77,7 @@ test_expect_success 'not prune recent checkouts' '\n \tmkdir zz &&\n \tmkdir -p .git/repos/jlm &&\n \techo \"$TRASH_DIRECTORY\"/zz >.git/repos/jlm/gitdir &&\n-\tgit prune --repos --verbose &&\n+\tgit prune --repos --verbose --expire=2.days.ago &&\n \ttest -d .git/repos/jlm\n '\n \n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246918","messageId":"1406641831-2390-5-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 4/8] checkout: no auto-detach if the ref is already checked out","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:27Z","receivedAt":"2014-07-29T13:50:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Give the user a choice in this case: detach, make a new branch,\nrelease current branch in the other checkout or simply 'cd' there and\ncontinue to work.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt |  4 +++\n advice.c                 |  2 ++\n advice.h                 |  1 +\n builtin/checkout.c       | 76 ++++++++++++++++++++++++++----------------------\n t/t2025-checkout-to.sh   | 16 +++++-----\n 5 files changed, 55 insertions(+), 44 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 57999fa..b2c3388 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -201,6 +201,10 @@ advice.*::\n \trmHints::\n \t\tIn case of failure in the output of linkgit:git-rm[1],\n \t\tshow directions on how to proceed from the current state.\n+\tcheckoutLocked::\n+\t\tIn multiple checkout setup, attempting to checkout a\n+\t\tbranch already checked out elsewhere will fail. Show\n+\t\tsome useful options to proceed.\n --\n \n core.fileMode::\ndiff --git a/advice.c b/advice.c\nindex 9b42033..cf3b0f7 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -15,6 +15,7 @@ int advice_detached_head = 1;\n int advice_set_upstream_failure = 1;\n int advice_object_name_warning = 1;\n int advice_rm_hints = 1;\n+int advice_checkout_locked = 1;\n \n static struct {\n \tconst char *name;\n@@ -35,6 +36,7 @@ static struct {\n \t{ \"setupstreamfailure\", &advice_set_upstream_failure },\n \t{ \"objectnamewarning\", &advice_object_name_warning },\n \t{ \"rmhints\", &advice_rm_hints },\n+\t{ \"checkoutlocked\", &advice_checkout_locked },\n \n \t/* make this an alias for backward compatibility */\n \t{ \"pushnonfastforward\", &advice_push_update_rejected }\ndiff --git a/advice.h b/advice.h\nindex 5ecc6c1..935631d 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -18,6 +18,7 @@ extern int advice_detached_head;\n extern int advice_set_upstream_failure;\n extern int advice_object_name_warning;\n extern int advice_rm_hints;\n+extern int advice_checkout_locked;\n \n int git_default_advice_config(const char *var, const char *value);\n __attribute__((format (printf, 1, 2)))\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 6ac89eb..0714856 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -645,11 +645,6 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\t\tif (old->path && advice_detached_head)\n \t\t\t\tdetach_advice(new->name);\n \t\t\tdescribe_detached_head(_(\"HEAD is now at\"), new->commit);\n-\t\t\tif (new->checkout && !*new->checkout)\n-\t\t\t\tfprintf(stderr, _(\"hint: the main checkout is holding this branch\\n\"));\n-\t\t\telse if (new->checkout)\n-\t\t\t\tfprintf(stderr, _(\"hint: the linked checkout %s is holding this branch\\n\"),\n-\t\t\t\t\tnew->checkout);\n \t\t}\n \t} else if (new->path) {\t/* Switch branches. */\n \t\tcreate_symref(\"HEAD\", new->path, msg.buf);\n@@ -1006,31 +1001,50 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)\n \treturn NULL;\n }\n \n-static int check_linked_checkout(struct branch_info *new,\n-\t\t\t\t  const char *name, const char *path)\n+static void check_linked_checkout(struct branch_info *new, const char *id)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf gitdir = STRBUF_INIT;\n \tconst char *start, *end;\n-\tif (strbuf_read_file(&sb, path, 0) < 0 ||\n-\t    !skip_prefix(sb.buf, \"ref:\", &start)) {\n-\t\tstrbuf_release(&sb);\n-\t\treturn 0;\n-\t}\n \n+\tif (id)\n+\t\tstrbuf_addf(&path, \"%s/repos/%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+\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, new->path, end - start) &&\n-\t    new->path[end - start] == '\\0') {\n-\t\tstrbuf_release(&sb);\n-\t\tnew->path = NULL; /* detach */\n-\t\tnew->checkout = xstrdup(name); /* reason */\n-\t\treturn 1;\n-\t}\n+\tif (strncmp(start, new->path, end - start) || new->path[end - start] != '\\0')\n+\t\tgoto done;\n+\tif (id) {\n+\t\tstrbuf_reset(&path);\n+\t\tstrbuf_addf(&path, \"%s/repos/%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+\tif (advice_checkout_locked)\n+\t\tdie(_(\"'%s' is already checked out at %s\\n\"\n+\t\t      \"Either go there and continue working, or detach HEAD using\\n\"\n+\t\t      \"    git checkout --detach [more options] %s\\n\"\n+\t\t      \"or create a new branch based off '%s' using\\n\"\n+\t\t      \"    git checkout -b <branch name> [more options] %s\\n\"\n+\t\t      \"or switch to another branch at the other checkout and retry.\"),\n+\t\t    new->name, gitdir.buf, new->name, new->name, new->name);\n+\telse\n+\t\tdie(_(\"'%s' is already checked out at %s\"), new->name, gitdir.buf);\n+done:\n+\tstrbuf_release(&path);\n \tstrbuf_release(&sb);\n-\treturn 0;\n+\tstrbuf_release(&gitdir);\n }\n \n static void check_linked_checkouts(struct branch_info *new)\n@@ -1045,27 +1059,17 @@ static void check_linked_checkouts(struct branch_info *new)\n \t\treturn;\n \t}\n \n-\tstrbuf_reset(&path);\n-\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\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 (check_linked_checkout(new, \"\", path.buf)) {\n-\t\tstrbuf_release(&path);\n-\t\tclosedir(dir);\n-\t\treturn;\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-\t\tstrbuf_reset(&path);\n-\t\tstrbuf_addf(&path, \"%s/repos/%s/HEAD\",\n-\t\t\t    get_git_common_dir(), d->d_name);\n-\t\tif (check_linked_checkout(new, d->d_name, path.buf))\n-\t\t\tbreak;\n+\t\tcheck_linked_checkout(new, d->d_name);\n \t}\n \tstrbuf_release(&path);\n \tclosedir(dir);\n@@ -1076,7 +1080,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct branch_info *new,\n \t\t\t\tstruct tree **source_tree,\n \t\t\t\tunsigned char rev[20],\n-\t\t\t\tconst char **new_branch)\n+\t\t\t\tconst char **new_branch,\n+\t\t\t\tint force_detach)\n {\n \tint argcount = 0;\n \tunsigned char branch_rev[20];\n@@ -1198,7 +1203,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \telse\n \t\tnew->path = NULL; /* not an existing branch */\n \n-\tif (new->path) {\n+\tif (new->path && !force_detach && !*new_branch) {\n \t\tunsigned char sha1[20];\n \t\tint flag;\n \t\tchar *head_ref = resolve_refdup(\"HEAD\", sha1, 0, &flag);\n@@ -1417,7 +1422,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t!opts.new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n \t\t\t\t\t     &new, &opts.source_tree,\n-\t\t\t\t\t     rev, &opts.new_branch);\n+\t\t\t\t\t     rev, &opts.new_branch,\n+\t\t\t\t\t     opts.force_detach);\n \t\targv += n;\n \t\targc -= n;\n \t}\ndiff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\nindex b0d97a0..c6601a4 100755\n--- a/t/t2025-checkout-to.sh\n+++ b/t/t2025-checkout-to.sh\n@@ -14,7 +14,7 @@ test_expect_success 'checkout --to not updating paths' '\n \n test_expect_success 'checkout --to a new worktree' '\n \tgit rev-parse HEAD >expect &&\n-\tgit checkout --to here master &&\n+\tgit checkout --detach --to here master &&\n \t(\n \t\tcd here &&\n \t\ttest_cmp ../init.t init.t &&\n@@ -28,7 +28,7 @@ test_expect_success 'checkout --to a new worktree' '\n test_expect_success 'checkout --to from a linked checkout' '\n \t(\n \t\tcd here &&\n-\t\tgit checkout --to nested-here master\n+\t\tgit checkout --detach --to nested-here master\n \t\tcd nested-here &&\n \t\tgit fsck\n \t)\n@@ -46,19 +46,17 @@ test_expect_success 'checkout --to a new worktree creating new branch' '\n \t)\n '\n \n-test_expect_success 'detach if the same branch is already checked out' '\n+test_expect_success 'die the same branch is already checked out' '\n \t(\n \t\tcd here &&\n-\t\tgit checkout newmaster &&\n-\t\ttest_must_fail git symbolic-ref HEAD\n+\t\ttest_must_fail git checkout newmaster\n \t)\n '\n \n-test_expect_success 'not detach on re-checking out current branch' '\n+test_expect_success 'not die on re-checking out current branch' '\n \t(\n \t\tcd there &&\n-\t\tgit checkout newmaster &&\n-\t\tgit symbolic-ref HEAD\n+\t\tgit checkout newmaster\n \t)\n '\n \n@@ -66,7 +64,7 @@ test_expect_success 'checkout --to from a bare repo' '\n \t(\n \t\tgit clone --bare . bare &&\n \t\tcd bare &&\n-\t\tgit checkout --to ../there2 master\n+\t\tgit checkout --to ../there2 -b bare-master master\n \t)\n '\n \n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246914","messageId":"1406641831-2390-6-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 5/8] checkout --to: fix dangling pointers in remove_junk()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:28Z","receivedAt":"2014-07-29T13:50:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"junk_git_dir is set to sb_repo.buf. By the end of prepare_linked_checkout(),\nsb_repo is freed and so junk_git_dir points to nowhere. If the second\ncheckout command fails, is_junk remains non-zero, remove_junk() will\nbe called and try to clean junk_git_dir, which could be anything now\n(if it does not crash the program).\n\nThe new test may pass even without this patch. But it does fail under\nvalgrind (without this patch) with \"Invalid read of size 8\" at the\nright line.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c     | 15 ++++++++++-----\n t/t2025-checkout-to.sh |  6 ++++++\n 2 files changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0714856..173aab1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -820,8 +820,8 @@ static int switch_branches(const struct checkout_opts *opts,\n \treturn ret || writeout_error;\n }\n \n-static const char *junk_work_tree;\n-static const char *junk_git_dir;\n+static char *junk_work_tree;\n+static char *junk_git_dir;\n static int is_junk;\n static pid_t junk_pid;\n \n@@ -890,7 +890,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,\n \n \tif (mkdir(sb_repo.buf, 0777))\n \t\tdie_errno(_(\"could not create directory of '%s'\"), sb_repo.buf);\n-\tjunk_git_dir = sb_repo.buf;\n+\tjunk_git_dir = xstrdup(sb_repo.buf);\n \tis_junk = 1;\n \n \t/*\n@@ -904,7 +904,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,\n \tif (safe_create_leading_directories_const(sb_git.buf))\n \t\tdie_errno(_(\"could not create leading directories of '%s'\"),\n \t\t\t  sb_git.buf);\n-\tjunk_work_tree = path;\n+\tjunk_work_tree = xstrdup(path);\n \n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/gitdir\", sb_repo.buf);\n@@ -934,8 +934,13 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,\n \tcp.git_cmd = 1;\n \tcp.argv = opts->saved_argv;\n \tret = run_command(&cp);\n-\tif (!ret)\n+\tif (!ret) {\n \t\tis_junk = 0;\n+\t\tfree(junk_work_tree);\n+\t\tfree(junk_git_dir);\n+\t\tjunk_work_tree = NULL;\n+\t\tjunk_git_dir = NULL;\n+\t}\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/locked\", sb_repo.buf);\n \tunlink_or_warn(sb.buf);\ndiff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\nindex c6601a4..8a00310 100755\n--- a/t/t2025-checkout-to.sh\n+++ b/t/t2025-checkout-to.sh\n@@ -12,6 +12,12 @@ test_expect_success 'checkout --to not updating paths' '\n \ttest_must_fail git checkout --to -- init.t\n '\n \n+test_expect_success 'checkout --to refuses to checkout locked branch' '\n+\ttest_must_fail git checkout --to zere master &&\n+\t! test -d zere &&\n+\t! test -d .git/repos/zere\n+'\n+\n test_expect_success 'checkout --to a new worktree' '\n \tgit rev-parse HEAD >expect &&\n \tgit checkout --detach --to here master &&\n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246915","messageId":"1406641831-2390-7-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 6/8] environment.c: fix incorrect git_graft_file initialization","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:29Z","receivedAt":"2014-07-29T13:50:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"info/grafts\" should be part of the \"common repository\" when accessed\nfrom a linked checkout (iow $GIT_COMMON_DIR/info/grafts, not\n$GIT_DIR/info/grafts).\n\ngit_path(\"info/grafts\") returns correctly, even without this fix,\nbecause it detects that $GIT_GRAFT_FILE is not set, so it goes with the\ncommon rule: anything except sparse-checkout in 'info' belongs to common\nrepo. But get_graft_file() would return a wrong value and that one is\nused for setting grafts up.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n environment.c          |  2 +-\n t/t0060-path-utils.sh  |  1 +\n t/t2025-checkout-to.sh | 18 ++++++++++++++++++\n 3 files changed, 20 insertions(+), 1 deletion(-)\n\ndiff --git a/environment.c b/environment.c\nindex 50ed40a..d5b0788 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -157,7 +157,7 @@ static void setup_git_env(void)\n \t\t\t\t\t   \"objects\", &git_db_env);\n \tgit_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,\n \t\t\t\t\t   \"index\", &git_index_env);\n-\tgit_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_dir,\n+\tgit_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_common_dir,\n \t\t\t\t\t   \"info/grafts\", &git_graft_env);\n \tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n \t\tcheck_replace_refs = 0;\ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex da82aab..93605f4 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -269,6 +269,7 @@ test_git_path GIT_COMMON_DIR=bar logs/HEAD                .git/logs/HEAD\n test_git_path GIT_COMMON_DIR=bar objects                  bar/objects\n test_git_path GIT_COMMON_DIR=bar objects/bar              bar/objects/bar\n test_git_path GIT_COMMON_DIR=bar info/exclude             bar/info/exclude\n+test_git_path GIT_COMMON_DIR=bar info/grafts              bar/info/grafts\n test_git_path GIT_COMMON_DIR=bar info/sparse-checkout     .git/info/sparse-checkout\n test_git_path GIT_COMMON_DIR=bar remotes/bar              bar/remotes/bar\n test_git_path GIT_COMMON_DIR=bar branches/bar             bar/branches/bar\ndiff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh\nindex 8a00310..508993f 100755\n--- a/t/t2025-checkout-to.sh\n+++ b/t/t2025-checkout-to.sh\n@@ -81,4 +81,22 @@ test_expect_success 'checkout from a bare repo without --to' '\n \t)\n '\n \n+test_expect_success 'checkout with grafts' '\n+\ttest_when_finished rm .git/info/grafts &&\n+\ttest_commit abc &&\n+\tSHA1=`git rev-parse HEAD` &&\n+\ttest_commit def &&\n+\ttest_commit xyz &&\n+\techo \"`git rev-parse HEAD` $SHA1\" >.git/info/grafts &&\n+\tcat >expected <<-\\EOF &&\n+\txyz\n+\tabc\n+\tEOF\n+\tgit log --format=%s -2 >actual &&\n+\ttest_cmp expected actual &&\n+\tgit checkout --detach --to grafted master &&\n+\tgit --git-dir=grafted/.git log --format=%s -2 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246916","messageId":"1406641831-2390-8-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 7/8] checkout: prefix --to argument properly when cwd is moved","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:30Z","receivedAt":"2014-07-29T13:50:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 173aab1..4fbb9c1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1342,7 +1342,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n \t\t\t\tN_(\"second guess 'git checkout no-such-branch'\")),\n-\t\tOPT_STRING(0, \"to\", &opts.new_worktree, N_(\"path\"),\n+\t\tOPT_FILENAME(0, \"to\", &opts.new_worktree,\n \t\t\t   N_(\"check a branch out in a separate working directory\")),\n \t\tOPT_END(),\n \t};\n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246917","messageId":"1406641831-2390-9-git-send-email-pclouds@gmail.com","threadId":"37194","inReplyTo":"1406641831-2390-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 8/8] checkout --to: do not touch existing target directory","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-29T13:50:31Z","receivedAt":"2014-07-29T13:50:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4fbb9c1..3dc416c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -861,6 +861,8 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,\n \n \tif (!new->commit)\n \t\tdie(_(\"no branch specified\"));\n+\tif (file_exists(path))\n+\t\tdie(_(\"%s already exists\"), path);\n \n \tlen = strlen(path);\n \twhile (len && is_dir_sep(path[len - 1]))\n-- \n2.1.0.rc0.78.gc0d8480\n"},{"id":"246945","messageId":"xmqq8unb3n3m.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"1406641831-2390-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 7/8] checkout: prefix --to argument properly when cwd is moved","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-29T20:51:25Z","receivedAt":"2014-07-29T20:51:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/checkout.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 173aab1..4fbb9c1 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -1342,7 +1342,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n>  \t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n>  \t\t\t\tN_(\"second guess 'git checkout no-such-branch'\")),\n> -\t\tOPT_STRING(0, \"to\", &opts.new_worktree, N_(\"path\"),\n> +\t\tOPT_FILENAME(0, \"to\", &opts.new_worktree,\n>  \t\t\t   N_(\"check a branch out in a separate working directory\")),\n>  \t\tOPT_END(),\n>  \t};\n\nGood thinking.  Otherwise this would not work from within a\nsubdirectory.  Perhaps you would want a test for it?\n\nAn unrelated tangent, but would we want to further enhance\nOPT_FILENAME() to understand ~/path and ~user/path perhaps?\nIt is easy to rely on users' shells to do so, so it is not a big\ndeal and it certainly is unrelated to this particular topic.\n"},{"id":"246960","messageId":"CACsJy8DmufHRy_LcmPmLozGRNGa8pYSprdhWM5qq8xydPuw=UQ@mail.gmail.com","threadId":"37194","inReplyTo":"xmqq8unb3n3m.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 7/8] checkout: prefix --to argument properly when cwd is moved","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-30T10:32:57Z","receivedAt":"2014-07-30T10:32:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Jul 30, 2014 at 3:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  builtin/checkout.c | 2 +-\n>>  1 file changed, 1 insertion(+), 1 deletion(-)\n>>\n>> diff --git a/builtin/checkout.c b/builtin/checkout.c\n>> index 173aab1..4fbb9c1 100644\n>> --- a/builtin/checkout.c\n>> +++ b/builtin/checkout.c\n>> @@ -1342,7 +1342,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>>                        N_(\"do not limit pathspecs to sparse entries only\")),\n>>               OPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n>>                               N_(\"second guess 'git checkout no-such-branch'\")),\n>> -             OPT_STRING(0, \"to\", &opts.new_worktree, N_(\"path\"),\n>> +             OPT_FILENAME(0, \"to\", &opts.new_worktree,\n>>                          N_(\"check a branch out in a separate working directory\")),\n>>               OPT_END(),\n>>       };\n>\n> Good thinking.  Otherwise this would not work from within a\n> subdirectory.  Perhaps you would want a test for it?\n\nWill do at the next round.\n\n> An unrelated tangent, but would we want to further enhance\n> OPT_FILENAME() to understand ~/path and ~user/path perhaps?\n> It is easy to rely on users' shells to do so, so it is not a big\n> deal and it certainly is unrelated to this particular topic.\n\nYeah, the only user group that probably wants this is cmd.exe users.\nWe already have expand_user_path for this, all needed is some glue.\nI'll let Windows users do this if they really want to.\n-- \nDuy\n"},{"id":"246979","messageId":"xmqqr412zqdo.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"1406115795-24082-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 0/5] nd/multiple-work-trees follow-ups","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-30T17:51:47Z","receivedAt":"2014-07-30T17:51:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> The series has entered 'next' so I can't replace patches any more.\n> Besides the brown paper bag fixes, checkout now rejects if a branch is\n> already checked out elsewhere.\n\nI do not think we would want to rush the entire series to 'master'\nbefore the upcoming release, so we could squash these fixes into the\noriginal when we rewind 'next' post release, if you wanted to.\n\nThe fix-ups are easier to review than wholesale replacements; keep\nthem coming as needed.\n\nThanks.\n"},{"id":"246980","messageId":"xmqqmwbqzptt.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"53D1FE76.5080708@drmicha.warpmail.net","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-30T18:03:42Z","receivedAt":"2014-07-30T18:03:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael J Gruber <git@drmicha.warpmail.net> writes:\n\n> As an error message that is completely sufficient.\n>\n> The advice messages are meant to teach the user about the normal parts\n> of the toolchest to use in a situation of \"conflict\", aren't they?\n\nNot really.  They are to remind (to those who learned but forgot)\nand to hint (to those who haven't realized they have things to learn\nin this area).  Wall of text that tries to do more than that, like\n\"teaching\", risks not getting read by anybody.\n\n> Maybe\n> we should ask someone who hasn't turned them off...\n\nActually, I run without most of the 'advice.*' turned off.\n"},{"id":"246984","messageId":"xmqq8unaznl8.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"xmqqmwbqzptt.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-30T18:52:03Z","receivedAt":"2014-07-30T18:52:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Michael J Gruber <git@drmicha.warpmail.net> writes:\n>\n>> As an error message that is completely sufficient.\n>>\n>> The advice messages are meant to teach the user about the normal parts\n>> of the toolchest to use in a situation of \"conflict\", aren't they?\n>\n> Not really.  They are to remind (to those who learned but forgot)\n> and to hint (to those who haven't realized they have things to learn\n> in this area).  Wall of text that tries to do more than that, like\n> \"teaching\", risks not getting read by anybody.\n>\n>> Maybe\n>> we should ask someone who hasn't turned them off...\n>\n> Actually, I run without most of the 'advice.*' turned off.\n\nEhh, what I meant was that I do not have \"advice.foo = false\" for\nmost of 'foo', i.e. I run with most of them still active.\n"},{"id":"247025","messageId":"CACsJy8AambnVfbchGrvV0rgN1JyzCXBGgJxbV8JD1BF1DeDdHA@mail.gmail.com","threadId":"37194","inReplyTo":"xmqqr412zqdo.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 0/5] nd/multiple-work-trees follow-ups","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-07-31T10:13:01Z","receivedAt":"2014-07-31T10:13:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jul 31, 2014 at 12:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> The series has entered 'next' so I can't replace patches any more.\n>> Besides the brown paper bag fixes, checkout now rejects if a branch is\n>> already checked out elsewhere.\n>\n> I do not think we would want to rush the entire series to 'master'\n> before the upcoming release, so we could squash these fixes into the\n> original when we rewind 'next' post release, if you wanted to.\n\nGreat. Please remove it from next at the next rewind.\n\n> The fix-ups are easier to review than wholesale replacements; keep\n> them coming as needed.\n\nWill do.\n-- \nDuy\n"},{"id":"247067","messageId":"xmqq7g2txy3b.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"CACsJy8AambnVfbchGrvV0rgN1JyzCXBGgJxbV8JD1BF1DeDdHA@mail.gmail.com","subject":"Re: [PATCH 0/5] nd/multiple-work-trees follow-ups","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-31T17:00:24Z","receivedAt":"2014-07-31T17:00:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Thu, Jul 31, 2014 at 12:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>>\n>>> The series has entered 'next' so I can't replace patches any more.\n>>> Besides the brown paper bag fixes, checkout now rejects if a branch is\n>>> already checked out elsewhere.\n>>\n>> I do not think we would want to rush the entire series to 'master'\n>> before the upcoming release, so we could squash these fixes into the\n>> original when we rewind 'next' post release, if you wanted to.\n>\n> Great. Please remove it from next at the next rewind.\n\nI'd likely forget so please remind me when the time comes ;-)  In\nthe meantime, I made a note in the \"What's cooking\" report to hold\nthe topic in 'next' and not advance it to 'master'.\n"},{"id":"248393","messageId":"CACsJy8A3+9Rzz7ZEC3jgz0aQngT06Xi85gf6u2KS8VQUqkChvg@mail.gmail.com","threadId":"37194","inReplyTo":"xmqqmwbqzptt.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-08-27T11:58:35Z","receivedAt":"2014-08-27T11:58:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Jul 31, 2014 at 1:03 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Michael J Gruber <git@drmicha.warpmail.net> writes:\n>\n>> As an error message that is completely sufficient.\n>>\n>> The advice messages are meant to teach the user about the normal parts\n>> of the toolchest to use in a situation of \"conflict\", aren't they?\n>\n> Not really.  They are to remind (to those who learned but forgot)\n> and to hint (to those who haven't realized they have things to learn\n> in this area).  Wall of text that tries to do more than that, like\n> \"teaching\", risks not getting read by anybody.\n\nLast call to all. Keep this 'advice' or drop it?\n-- \nDuy\n"},{"id":"248407","messageId":"xmqq4mwxgbjq.fsf@gitster.dls.corp.google.com","threadId":"37194","inReplyTo":"CACsJy8A3+9Rzz7ZEC3jgz0aQngT06Xi85gf6u2KS8VQUqkChvg@mail.gmail.com","subject":"Re: [PATCH 3/5] checkout --to: no auto-detach if the ref is already checked out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-08-27T16:08:41Z","receivedAt":"2014-08-27T16:08:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Thu, Jul 31, 2014 at 1:03 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Michael J Gruber <git@drmicha.warpmail.net> writes:\n>>\n>>> As an error message that is completely sufficient.\n>>>\n>>> The advice messages are meant to teach the user about the normal parts\n>>> of the toolchest to use in a situation of \"conflict\", aren't they?\n>>\n>> Not really.  They are to remind (to those who learned but forgot)\n>> and to hint (to those who haven't realized they have things to learn\n>> in this area).  Wall of text that tries to do more than that, like\n>> \"teaching\", risks not getting read by anybody.\n>\n> Last call to all. Keep this 'advice' or drop it?\n\nI think you quoted what I needed to say already so I won't repeat\nmyself.  Earlier you said:\n\n    If you have two checkouts associated to the same repo, \"git\n    checkout foo\" on one checkout when \"foo\" is held by another\n    checkout would cause the same error. I'll need to think of a\n    better name than advice.checkoutTo.\n\nand I agree; the patch needs rerolling in any case.\n"}]}