{"thread":{"id":"40519","subject":"[PATCH v10 2/5] worktree: refactor find_linked_symref function","startedAt":"2015-10-08T17:01:01Z","lastAt":"2015-10-08T17:01:05Z","messageCount":5,"participants":["Michael Rappazzo"],"isPatch":true,"patchVersion":10,"patchTotal":5},"messages":[{"id":"271322","messageId":"1444323665-64590-1-git-send-email-rappazzo@gmail.com","threadId":"40519","inReplyTo":null,"subject":"[PATCH v10 1/5] worktree: add top-level worktree.c","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-10-08T17:01:01Z","receivedAt":"2015-10-08T17:01:01Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"worktree.c contains functions to work with and get information from\nworktrees.  This introduction moves functions related to worktrees\nfrom branch.c into worktree.c\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n Makefile        |  1 +\n branch.c        | 79 +-----------------------------------------------------\n branch.h        |  8 ------\n builtin/notes.c |  2 +-\n worktree.c      | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n worktree.h      | 12 +++++++++\n 6 files changed, 97 insertions(+), 87 deletions(-)\n create mode 100644 worktree.c\n create mode 100644 worktree.h\n\ndiff --git a/Makefile b/Makefile\nindex 36bc54c..9cc010c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -810,6 +810,7 @@ LIB_OBJS += version.o\n LIB_OBJS += versioncmp.o\n LIB_OBJS += walker.o\n LIB_OBJS += wildmatch.o\n+LIB_OBJS += worktree.o\n LIB_OBJS += wrapper.o\n LIB_OBJS += write_or_die.o\n LIB_OBJS += ws.o\ndiff --git a/branch.c b/branch.c\nindex d013374..77d7f2a 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -4,6 +4,7 @@\n #include \"refs.h\"\n #include \"remote.h\"\n #include \"commit.h\"\n+#include \"worktree.h\"\n \n struct tracking {\n \tstruct refspec spec;\n@@ -311,84 +312,6 @@ void remove_branch_state(void)\n \tunlink(git_path_squash_msg());\n }\n \n-static char *find_linked_symref(const char *symref, const char *branch,\n-\t\t\t\tconst char *id)\n-{\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tstruct strbuf path = STRBUF_INIT;\n-\tstruct strbuf gitdir = STRBUF_INIT;\n-\tchar *existing = NULL;\n-\n-\t/*\n-\t * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside\n-\t * $GIT_DIR so resolve_ref_unsafe() won't work (it uses\n-\t * git_path). Parse the ref ourselves.\n-\t */\n-\tif (id)\n-\t\tstrbuf_addf(&path, \"%s/worktrees/%s/%s\", get_git_common_dir(), id, symref);\n-\telse\n-\t\tstrbuf_addf(&path, \"%s/%s\", get_git_common_dir(), symref);\n-\n-\tif (!strbuf_readlink(&sb, path.buf, 0)) {\n-\t\tif (!starts_with(sb.buf, \"refs/\") ||\n-\t\t    check_refname_format(sb.buf, 0))\n-\t\t\tgoto done;\n-\t} else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n-\t    starts_with(sb.buf, \"ref:\")) {\n-\t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n-\t\tstrbuf_trim(&sb);\n-\t} else\n-\t\tgoto done;\n-\tif (strcmp(sb.buf, branch))\n-\t\tgoto done;\n-\tif (id) {\n-\t\tstrbuf_reset(&path);\n-\t\tstrbuf_addf(&path, \"%s/worktrees/%s/gitdir\", get_git_common_dir(), id);\n-\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n-\t\t\tgoto done;\n-\t\tstrbuf_rtrim(&gitdir);\n-\t} else\n-\t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n-\tstrbuf_strip_suffix(&gitdir, \".git\");\n-\n-\texisting = strbuf_detach(&gitdir, NULL);\n-done:\n-\tstrbuf_release(&path);\n-\tstrbuf_release(&sb);\n-\tstrbuf_release(&gitdir);\n-\n-\treturn existing;\n-}\n-\n-char *find_shared_symref(const char *symref, const char *target)\n-{\n-\tstruct strbuf path = STRBUF_INIT;\n-\tDIR *dir;\n-\tstruct dirent *d;\n-\tchar *existing;\n-\n-\tif ((existing = find_linked_symref(symref, target, NULL)))\n-\t\treturn existing;\n-\n-\tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n-\tdir = opendir(path.buf);\n-\tstrbuf_release(&path);\n-\tif (!dir)\n-\t\treturn 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\texisting = find_linked_symref(symref, target, d->d_name);\n-\t\tif (existing)\n-\t\t\tgoto done;\n-\t}\n-done:\n-\tclosedir(dir);\n-\n-\treturn existing;\n-}\n-\n void die_if_checked_out(const char *branch)\n {\n \tchar *existing;\ndiff --git a/branch.h b/branch.h\nindex d3446ed..58aa45f 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -59,12 +59,4 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);\n  */\n extern void die_if_checked_out(const char *branch);\n \n-/*\n- * Check if a per-worktree symref points to a ref in the main worktree\n- * or any linked worktree, and return the path to the exising worktree\n- * if it is.  Returns NULL if there is no existing ref.  The caller is\n- * responsible for freeing the returned path.\n- */\n-extern char *find_shared_symref(const char *symref, const char *target);\n-\n #endif\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 3608c64..13c0af9 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -19,7 +19,7 @@\n #include \"string-list.h\"\n #include \"notes-merge.h\"\n #include \"notes-utils.h\"\n-#include \"branch.h\"\n+#include \"worktree.h\"\n \n static const char * const git_notes_usage[] = {\n \tN_(\"git notes [--ref <notes-ref>] [list [<object>]]\"),\ndiff --git a/worktree.c b/worktree.c\nnew file mode 100644\nindex 0000000..10e1496\n--- /dev/null\n+++ b/worktree.c\n@@ -0,0 +1,82 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"strbuf.h\"\n+#include \"worktree.h\"\n+\n+static char *find_linked_symref(const char *symref, const char *branch,\n+\t\t\t\tconst char *id)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf gitdir = STRBUF_INIT;\n+\tchar *existing = NULL;\n+\n+\t/*\n+\t * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside\n+\t * $GIT_DIR so resolve_ref_unsafe() won't work (it uses\n+\t * git_path). Parse the ref ourselves.\n+\t */\n+\tif (id)\n+\t\tstrbuf_addf(&path, \"%s/worktrees/%s/%s\", get_git_common_dir(), id, symref);\n+\telse\n+\t\tstrbuf_addf(&path, \"%s/%s\", get_git_common_dir(), symref);\n+\n+\tif (!strbuf_readlink(&sb, path.buf, 0)) {\n+\t\tif (!starts_with(sb.buf, \"refs/\") ||\n+\t\t    check_refname_format(sb.buf, 0))\n+\t\t\tgoto done;\n+\t} else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n+\t    starts_with(sb.buf, \"ref:\")) {\n+\t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n+\t\tstrbuf_trim(&sb);\n+\t} else\n+\t\tgoto done;\n+\tif (strcmp(sb.buf, branch))\n+\t\tgoto done;\n+\tif (id) {\n+\t\tstrbuf_reset(&path);\n+\t\tstrbuf_addf(&path, \"%s/worktrees/%s/gitdir\", get_git_common_dir(), id);\n+\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n+\t\t\tgoto done;\n+\t\tstrbuf_rtrim(&gitdir);\n+\t} else\n+\t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n+\tstrbuf_strip_suffix(&gitdir, \".git\");\n+\n+\texisting = strbuf_detach(&gitdir, NULL);\n+done:\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&sb);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn existing;\n+}\n+\n+char *find_shared_symref(const char *symref, const char *target)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tDIR *dir;\n+\tstruct dirent *d;\n+\tchar *existing;\n+\n+\tif ((existing = find_linked_symref(symref, target, NULL)))\n+\t\treturn existing;\n+\n+\tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n+\tdir = opendir(path.buf);\n+\tstrbuf_release(&path);\n+\tif (!dir)\n+\t\treturn 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\texisting = find_linked_symref(symref, target, d->d_name);\n+\t\tif (existing)\n+\t\t\tgoto done;\n+\t}\n+done:\n+\tclosedir(dir);\n+\n+\treturn existing;\n+}\ndiff --git a/worktree.h b/worktree.h\nnew file mode 100644\nindex 0000000..71b1409\n--- /dev/null\n+++ b/worktree.h\n@@ -0,0 +1,12 @@\n+#ifndef WORKTREE_H\n+#define WORKTREE_H\n+\n+/*\n+ * Check if a per-worktree symref points to a ref in the main worktree\n+ * or any linked worktree, and return the path to the exising worktree\n+ * if it is.  Returns NULL if there is no existing ref.  The caller is\n+ * responsible for freeing the returned path.\n+ */\n+extern char *find_shared_symref(const char *symref, const char *target);\n+\n+#endif\n-- \n2.6.0\n"},{"id":"271321","messageId":"1444323665-64590-2-git-send-email-rappazzo@gmail.com","threadId":"40519","inReplyTo":"1444323665-64590-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH v10 2/5] worktree: refactor find_linked_symref function","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-10-08T17:01:02Z","receivedAt":"2015-10-08T17:01:02Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"Refactoring will help transition this code to provide additional useful\nworktree functions.\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n worktree.c | 96 ++++++++++++++++++++++++++++++++++++++++++++------------------\n 1 file changed, 69 insertions(+), 27 deletions(-)\n\ndiff --git a/worktree.c b/worktree.c\nindex 10e1496..3c2498a 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -3,6 +3,64 @@\n #include \"strbuf.h\"\n #include \"worktree.h\"\n \n+/*\n+ * read 'path_to_ref' into 'ref'.  Also if is_detached is not NULL,\n+ * set is_detached to 1 (0) if the ref is detatched (is not detached).\n+ *\n+ * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so\n+ * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses\n+ * git_path). Parse the ref ourselves.\n+ *\n+ * return -1 if the ref is not a proper ref, 0 otherwise (success)\n+ */\n+static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)\n+{\n+\tif (is_detached)\n+\t\t*is_detached = 0;\n+\tif (!strbuf_readlink(ref, path_to_ref, 0)) {\n+\t\t/* HEAD is symbolic link */\n+\t\tif (!starts_with(ref->buf, \"refs/\") ||\n+\t\t\t\tcheck_refname_format(ref->buf, 0))\n+\t\t\treturn -1;\n+\t} else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {\n+\t\t/* textual symref or detached */\n+\t\tif (!starts_with(ref->buf, \"ref:\")) {\n+\t\t\tif (is_detached)\n+\t\t\t\t*is_detached = 1;\n+\t\t} else {\n+\t\t\tstrbuf_remove(ref, 0, strlen(\"ref:\"));\n+\t\t\tstrbuf_trim(ref);\n+\t\t\tif (check_refname_format(ref->buf, 0))\n+\t\t\t\treturn -1;\n+\t\t}\n+\t} else\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static char *find_main_symref(const char *symref, const char *branch)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf gitdir = STRBUF_INIT;\n+\tchar *existing = NULL;\n+\n+\tstrbuf_addf(&path, \"%s/%s\", get_git_common_dir(), symref);\n+\tif (parse_ref(path.buf, &sb, NULL) < 0)\n+\t\tgoto done;\n+\tif (strcmp(sb.buf, branch))\n+\t\tgoto done;\n+\tstrbuf_addstr(&gitdir, get_git_common_dir());\n+\tstrbuf_strip_suffix(&gitdir, \".git\");\n+\texisting = strbuf_detach(&gitdir, NULL);\n+done:\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&sb);\n+\tstrbuf_release(&gitdir);\n+\n+\treturn existing;\n+}\n+\n static char *find_linked_symref(const char *symref, const char *branch,\n \t\t\t\tconst char *id)\n {\n@@ -11,36 +69,20 @@ static char *find_linked_symref(const char *symref, const char *branch,\n \tstruct strbuf gitdir = STRBUF_INIT;\n \tchar *existing = NULL;\n \n-\t/*\n-\t * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside\n-\t * $GIT_DIR so resolve_ref_unsafe() won't work (it uses\n-\t * git_path). Parse the ref ourselves.\n-\t */\n-\tif (id)\n-\t\tstrbuf_addf(&path, \"%s/worktrees/%s/%s\", get_git_common_dir(), id, symref);\n-\telse\n-\t\tstrbuf_addf(&path, \"%s/%s\", get_git_common_dir(), symref);\n+\tif (!id)\n+\t\tdie(\"Missing linked worktree name\");\n \n-\tif (!strbuf_readlink(&sb, path.buf, 0)) {\n-\t\tif (!starts_with(sb.buf, \"refs/\") ||\n-\t\t    check_refname_format(sb.buf, 0))\n-\t\t\tgoto done;\n-\t} else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&\n-\t    starts_with(sb.buf, \"ref:\")) {\n-\t\tstrbuf_remove(&sb, 0, strlen(\"ref:\"));\n-\t\tstrbuf_trim(&sb);\n-\t} else\n+\tstrbuf_addf(&path, \"%s/worktrees/%s/%s\", get_git_common_dir(), id, symref);\n+\n+\tif (parse_ref(path.buf, &sb, NULL) < 0)\n \t\tgoto done;\n \tif (strcmp(sb.buf, branch))\n \t\tgoto done;\n-\tif (id) {\n-\t\tstrbuf_reset(&path);\n-\t\tstrbuf_addf(&path, \"%s/worktrees/%s/gitdir\", get_git_common_dir(), id);\n-\t\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n-\t\t\tgoto done;\n-\t\tstrbuf_rtrim(&gitdir);\n-\t} else\n-\t\tstrbuf_addstr(&gitdir, get_git_common_dir());\n+\tstrbuf_reset(&path);\n+\tstrbuf_addf(&path, \"%s/worktrees/%s/gitdir\", get_git_common_dir(), id);\n+\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n+\t\tgoto done;\n+\tstrbuf_rtrim(&gitdir);\n \tstrbuf_strip_suffix(&gitdir, \".git\");\n \n \texisting = strbuf_detach(&gitdir, NULL);\n@@ -59,7 +101,7 @@ char *find_shared_symref(const char *symref, const char *target)\n \tstruct dirent *d;\n \tchar *existing;\n \n-\tif ((existing = find_linked_symref(symref, target, NULL)))\n+\tif ((existing = find_main_symref(symref, target)))\n \t\treturn existing;\n \n \tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n-- \n2.6.0\n"},{"id":"271323","messageId":"1444323665-64590-3-git-send-email-rappazzo@gmail.com","threadId":"40519","inReplyTo":"1444323665-64590-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH v10 3/5] worktree: add a function to get worktree details","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-10-08T17:01:03Z","receivedAt":"2015-10-08T17:01:03Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"The worktree structure provided for an individual worktree includes the\nabsolute path of the worktree.  The fuction to get the worktree details\nis a refactor of the find main/linked symref functions.\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n worktree.c | 154 +++++++++++++++++++++++++++++++++++++++++++------------------\n worktree.h |  22 +++++++++\n 2 files changed, 130 insertions(+), 46 deletions(-)\n\ndiff --git a/worktree.c b/worktree.c\nindex 3c2498a..c2e6db0 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -3,6 +3,17 @@\n #include \"strbuf.h\"\n #include \"worktree.h\"\n \n+void free_worktrees(struct worktree **worktrees)\n+{\n+\tint i = 0;\n+\n+\tfor (i = 0; worktrees[i]; i++) {\n+\t\tfree(worktrees[i]->path);\n+\t\tfree(worktrees[i]);\n+\t}\n+\tfree (worktrees);\n+}\n+\n /*\n  * read 'path_to_ref' into 'ref'.  Also if is_detached is not NULL,\n  * set is_detached to 1 (0) if the ref is detatched (is not detached).\n@@ -38,87 +49,138 @@ static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)\n \treturn 0;\n }\n \n-static char *find_main_symref(const char *symref, const char *branch)\n+/**\n+ * get the main worktree\n+ */\n+static struct worktree *get_main_worktree(void)\n {\n-\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct worktree *worktree = NULL;\n \tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf worktree_path = STRBUF_INIT;\n \tstruct strbuf gitdir = STRBUF_INIT;\n-\tchar *existing = NULL;\n+\tstruct strbuf head_ref = STRBUF_INIT;\n \n-\tstrbuf_addf(&path, \"%s/%s\", get_git_common_dir(), symref);\n-\tif (parse_ref(path.buf, &sb, NULL) < 0)\n-\t\tgoto done;\n-\tif (strcmp(sb.buf, branch))\n-\t\tgoto done;\n-\tstrbuf_addstr(&gitdir, get_git_common_dir());\n-\tstrbuf_strip_suffix(&gitdir, \".git\");\n-\texisting = strbuf_detach(&gitdir, NULL);\n-done:\n+\tstrbuf_addf(&gitdir, \"%s\", absolute_path(get_git_common_dir()));\n+\tstrbuf_addbuf(&worktree_path, &gitdir);\n+\tif (!strbuf_strip_suffix(&worktree_path, \"/.git\"))\n+\t\tstrbuf_strip_suffix(&worktree_path, \"/.\");\n+\n+\tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\n+\n+\tif (parse_ref(path.buf, &head_ref, NULL) >= 0) {\n+\t\tworktree = xmalloc(sizeof(struct worktree));\n+\t\tworktree->path = strbuf_detach(&worktree_path, NULL);\n+\t\tworktree->git_dir = strbuf_detach(&gitdir, NULL);\n+\t}\n \tstrbuf_release(&path);\n-\tstrbuf_release(&sb);\n \tstrbuf_release(&gitdir);\n-\n-\treturn existing;\n+\tstrbuf_release(&worktree_path);\n+\tstrbuf_release(&head_ref);\n+\treturn worktree;\n }\n \n-static char *find_linked_symref(const char *symref, const char *branch,\n-\t\t\t\tconst char *id)\n+static struct worktree *get_linked_worktree(const char *id)\n {\n-\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct worktree *worktree = NULL;\n \tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf worktree_path = STRBUF_INIT;\n \tstruct strbuf gitdir = STRBUF_INIT;\n-\tchar *existing = NULL;\n+\tstruct strbuf head_ref = STRBUF_INIT;\n \n \tif (!id)\n \t\tdie(\"Missing linked worktree name\");\n \n-\tstrbuf_addf(&path, \"%s/worktrees/%s/%s\", get_git_common_dir(), id, symref);\n-\n-\tif (parse_ref(path.buf, &sb, NULL) < 0)\n-\t\tgoto done;\n-\tif (strcmp(sb.buf, branch))\n+\tstrbuf_addf(&gitdir, \"%s/worktrees/%s\",\n+\t\t\tabsolute_path(get_git_common_dir()), id);\n+\tstrbuf_addf(&path, \"%s/gitdir\", gitdir.buf);\n+\tif (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)\n+\t\t/* invalid gitdir file */\n \t\tgoto done;\n+\n+\tstrbuf_rtrim(&worktree_path);\n+\tif (!strbuf_strip_suffix(&worktree_path, \"/.git\")) {\n+\t\tstrbuf_reset(&worktree_path);\n+\t\tstrbuf_addstr(&worktree_path, absolute_path(\".\"));\n+\t\tstrbuf_strip_suffix(&worktree_path, \"/.\");\n+\t}\n+\n \tstrbuf_reset(&path);\n-\tstrbuf_addf(&path, \"%s/worktrees/%s/gitdir\", get_git_common_dir(), id);\n-\tif (strbuf_read_file(&gitdir, path.buf, 0) <= 0)\n-\t\tgoto done;\n-\tstrbuf_rtrim(&gitdir);\n-\tstrbuf_strip_suffix(&gitdir, \".git\");\n+\tstrbuf_addf(&path, \"%s/worktrees/%s/HEAD\", get_git_common_dir(), id);\n+\n+\tif (parse_ref(path.buf, &head_ref, NULL) >= 0) {\n+\t\tworktree = xmalloc(sizeof(struct worktree));\n+\t\tworktree->path = strbuf_detach(&worktree_path, NULL);\n+\t\tworktree->git_dir = strbuf_detach(&gitdir, NULL);\n+\t}\n \n-\texisting = strbuf_detach(&gitdir, NULL);\n done:\n \tstrbuf_release(&path);\n-\tstrbuf_release(&sb);\n \tstrbuf_release(&gitdir);\n-\n-\treturn existing;\n+\tstrbuf_release(&worktree_path);\n+\tstrbuf_release(&head_ref);\n+\treturn worktree;\n }\n \n-char *find_shared_symref(const char *symref, const char *target)\n+struct worktree **get_worktrees(void)\n {\n+\tstruct worktree **list = NULL;\n \tstruct strbuf path = STRBUF_INIT;\n \tDIR *dir;\n \tstruct dirent *d;\n-\tchar *existing;\n+\tint counter = 0, alloc = 2;\n+\n+\tlist = xmalloc(alloc * sizeof(struct worktree *));\n \n-\tif ((existing = find_main_symref(symref, target)))\n-\t\treturn existing;\n+\tif ((list[counter] = get_main_worktree()))\n+\t\tcounter++;\n \n \tstrbuf_addf(&path, \"%s/worktrees\", get_git_common_dir());\n \tdir = opendir(path.buf);\n \tstrbuf_release(&path);\n-\tif (!dir)\n-\t\treturn NULL;\n+\tif (dir) {\n+\t\twhile ((d = readdir(dir)) != NULL) {\n+\t\t\tstruct worktree *linked = NULL;\n+\t\t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n+\t\t\t\tcontinue;\n \n-\twhile ((d = readdir(dir)) != NULL) {\n-\t\tif (!strcmp(d->d_name, \".\") || !strcmp(d->d_name, \"..\"))\n+\t\t\t\tif ((linked = get_linked_worktree(d->d_name))) {\n+\t\t\t\t\tALLOC_GROW(list, counter + 1, alloc);\n+\t\t\t\t\tlist[counter++] = linked;\n+\t\t\t\t}\n+\t\t}\n+\t\tclosedir(dir);\n+\t}\n+\tALLOC_GROW(list, counter + 1, alloc);\n+\tlist[counter] = NULL;\n+\treturn list;\n+}\n+\n+char *find_shared_symref(const char *symref, const char *target)\n+{\n+\tchar *existing = NULL;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct worktree **worktrees = get_worktrees();\n+\tint i = 0;\n+\n+\tfor (i = 0; worktrees[i]; i++) {\n+\t\tstrbuf_reset(&path);\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_addf(&path, \"%s/%s\", worktrees[i]->git_dir, symref);\n+\n+\t\tif (parse_ref(path.buf, &sb, NULL)) {\n \t\t\tcontinue;\n-\t\texisting = find_linked_symref(symref, target, d->d_name);\n-\t\tif (existing)\n-\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif (!strcmp(sb.buf, target)) {\n+\t\t\texisting = xstrdup(worktrees[i]->path);\n+\t\t\tbreak;\n+\t\t}\n \t}\n-done:\n-\tclosedir(dir);\n+\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&sb);\n+\tfree_worktrees(worktrees);\n \n \treturn existing;\n }\ndiff --git a/worktree.h b/worktree.h\nindex 71b1409..7022029 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -1,6 +1,28 @@\n #ifndef WORKTREE_H\n #define WORKTREE_H\n \n+struct worktree {\n+\tchar *path;\n+\tchar *git_dir;\n+};\n+\n+/* Functions for acting on the information about worktrees. */\n+\n+/*\n+ * Get the worktrees.  The primary worktree will always be the first returned,\n+ * and linked worktrees will be pointed to by 'next' in each subsequent\n+ * worktree.  No specific ordering is done on the linked worktrees.\n+ *\n+ * The caller is responsible for freeing the memory from the returned\n+ * worktree(s).\n+ */\n+extern struct worktree **get_worktrees(void);\n+\n+/*\n+ * Free up the memory for worktree(s)\n+ */\n+extern void free_worktrees(struct worktree **);\n+\n /*\n  * Check if a per-worktree symref points to a ref in the main worktree\n  * or any linked worktree, and return the path to the exising worktree\n-- \n2.6.0\n"},{"id":"271324","messageId":"1444323665-64590-4-git-send-email-rappazzo@gmail.com","threadId":"40519","inReplyTo":"1444323665-64590-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH v10 4/5] worktree: add details to the worktree struct","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-10-08T17:01:04Z","receivedAt":"2015-10-08T17:01:04Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"In addition to the absolute path in the worktree struct, add the location\nof the git dir, the head ref (if not detached), the head revision sha1,\nwhether or not head is detached, and whether or not the worktree is a\nbare repo.\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n worktree.c | 55 ++++++++++++++++++++++++++++++++++++++++++++-----------\n worktree.h |  4 ++++\n 2 files changed, 48 insertions(+), 11 deletions(-)\n\ndiff --git a/worktree.c b/worktree.c\nindex c2e6db0..981f810 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -9,6 +9,8 @@ void free_worktrees(struct worktree **worktrees)\n \n \tfor (i = 0; worktrees[i]; i++) {\n \t\tfree(worktrees[i]->path);\n+\t\tfree(worktrees[i]->git_dir);\n+\t\tfree(worktrees[i]->head_ref);\n \t\tfree(worktrees[i]);\n \t}\n \tfree (worktrees);\n@@ -50,6 +52,21 @@ static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)\n }\n \n /**\n+ * Add the head_sha1 and head_ref (if not detached) to the given worktree\n+ */\n+static void add_head_info(struct strbuf *head_ref, struct worktree *worktree)\n+{\n+\tif (head_ref->len) {\n+\t\tif (worktree->is_detached) {\n+\t\t\tget_sha1_hex(head_ref->buf, worktree->head_sha1);\n+\t\t} else {\n+\t\t\tresolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);\n+\t\t\tworktree->head_ref = strbuf_detach(head_ref, NULL);\n+\t\t}\n+\t}\n+}\n+\n+/**\n  * get the main worktree\n  */\n static struct worktree *get_main_worktree(void)\n@@ -59,19 +76,29 @@ static struct worktree *get_main_worktree(void)\n \tstruct strbuf worktree_path = STRBUF_INIT;\n \tstruct strbuf gitdir = STRBUF_INIT;\n \tstruct strbuf head_ref = STRBUF_INIT;\n+\tint is_bare = 0;\n+\tint is_detached = 0;\n \n \tstrbuf_addf(&gitdir, \"%s\", absolute_path(get_git_common_dir()));\n \tstrbuf_addbuf(&worktree_path, &gitdir);\n-\tif (!strbuf_strip_suffix(&worktree_path, \"/.git\"))\n+\tis_bare = !strbuf_strip_suffix(&worktree_path, \"/.git\");\n+\tif (is_bare)\n \t\tstrbuf_strip_suffix(&worktree_path, \"/.\");\n \n \tstrbuf_addf(&path, \"%s/HEAD\", get_git_common_dir());\n \n-\tif (parse_ref(path.buf, &head_ref, NULL) >= 0) {\n-\t\tworktree = xmalloc(sizeof(struct worktree));\n-\t\tworktree->path = strbuf_detach(&worktree_path, NULL);\n-\t\tworktree->git_dir = strbuf_detach(&gitdir, NULL);\n-\t}\n+\tif (parse_ref(path.buf, &head_ref, &is_detached) < 0)\n+\t\tgoto done;\n+\n+\tworktree = xmalloc(sizeof(struct worktree));\n+\tworktree->path = strbuf_detach(&worktree_path, NULL);\n+\tworktree->git_dir = strbuf_detach(&gitdir, NULL);\n+\tworktree->is_bare = is_bare;\n+\tworktree->head_ref = NULL;\n+\tworktree->is_detached = is_detached;\n+\tadd_head_info(&head_ref, worktree);\n+\n+done:\n \tstrbuf_release(&path);\n \tstrbuf_release(&gitdir);\n \tstrbuf_release(&worktree_path);\n@@ -86,6 +113,7 @@ static struct worktree *get_linked_worktree(const char *id)\n \tstruct strbuf worktree_path = STRBUF_INIT;\n \tstruct strbuf gitdir = STRBUF_INIT;\n \tstruct strbuf head_ref = STRBUF_INIT;\n+\tint is_detached = 0;\n \n \tif (!id)\n \t\tdie(\"Missing linked worktree name\");\n@@ -107,11 +135,16 @@ static struct worktree *get_linked_worktree(const char *id)\n \tstrbuf_reset(&path);\n \tstrbuf_addf(&path, \"%s/worktrees/%s/HEAD\", get_git_common_dir(), id);\n \n-\tif (parse_ref(path.buf, &head_ref, NULL) >= 0) {\n-\t\tworktree = xmalloc(sizeof(struct worktree));\n-\t\tworktree->path = strbuf_detach(&worktree_path, NULL);\n-\t\tworktree->git_dir = strbuf_detach(&gitdir, NULL);\n-\t}\n+\tif (parse_ref(path.buf, &head_ref, &is_detached) < 0)\n+\t\tgoto done;\n+\n+\tworktree = xmalloc(sizeof(struct worktree));\n+\tworktree->path = strbuf_detach(&worktree_path, NULL);\n+\tworktree->git_dir = strbuf_detach(&gitdir, NULL);\n+\tworktree->is_bare = 0;\n+\tworktree->head_ref = NULL;\n+\tworktree->is_detached = is_detached;\n+\tadd_head_info(&head_ref, worktree);\n \n done:\n \tstrbuf_release(&path);\ndiff --git a/worktree.h b/worktree.h\nindex 7022029..b4b3dda 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -4,6 +4,10 @@\n struct worktree {\n \tchar *path;\n \tchar *git_dir;\n+\tchar *head_ref;\n+\tunsigned char head_sha1[20];\n+\tint is_detached;\n+\tint is_bare;\n };\n \n /* Functions for acting on the information about worktrees. */\n-- \n2.6.0\n"},{"id":"271325","messageId":"1444323665-64590-5-git-send-email-rappazzo@gmail.com","threadId":"40519","inReplyTo":"1444323665-64590-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH v10 5/5] worktree: add 'list' command","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-10-08T17:01:05Z","receivedAt":"2015-10-08T17:01:05Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"'git worktree list' iterates through the worktree list, and outputs\ndetails of the worktree including the path to the worktree, the currently\nchecked out revision and branch, and if the work tree is bare.  There is\nalso porcelain format option available.\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n Documentation/git-worktree.txt | 49 ++++++++++++++++++++++-\n builtin/worktree.c             | 88 +++++++++++++++++++++++++++++++++++++++++\n t/t2027-worktree-list.sh       | 89 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 225 insertions(+), 1 deletion(-)\n create mode 100755 t/t2027-worktree-list.sh\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex fb68156..5b9ad04 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n [verse]\n 'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>]\n 'git worktree prune' [-n] [-v] [--expire <expire>]\n+'git worktree list' [--porcelain]\n \n DESCRIPTION\n -----------\n@@ -59,6 +60,13 @@ prune::\n \n Prune working tree information in $GIT_DIR/worktrees.\n \n+list::\n+\n+List details of each worktree.  The main worktree is listed first, followed by\n+each of the linked worktrees.  The output details include if the worktree is\n+bare, the revision currently checked out, and the branch currently checked out\n+(or 'detached HEAD' if none).\n+\n OPTIONS\n -------\n \n@@ -86,6 +94,11 @@ OPTIONS\n \tWith `prune`, do not remove anything; just report what it would\n \tremove.\n \n+--porcelain::\n+\tWith `list`, output in an easy-to-parse format for scripts.\n+\tThis format will remain stable across Git versions and regardless of user\n+\tconfiguration.  See below for details.\n+\n -v::\n --verbose::\n \tWith `prune`, report all removals.\n@@ -134,6 +147,41 @@ to `/path/main/.git/worktrees/test-next` then a file named\n `test-next` entry from being pruned.  See\n linkgit:gitrepository-layout[5] for details.\n \n+LIST OUTPUT FORMAT\n+------------------\n+The worktree list command has two output formats.  The default format shows the\n+details on a single line with columns.  For example:\n+\n+------------\n+S git worktree list\n+/path/to/bare-source            (bare)\n+/path/to/linked-worktree        abcd1234 [master]\n+/path/to/other-linked-worktree  1234abc  (detached HEAD)\n+------------\n+\n+Porcelain Format\n+~~~~~~~~~~~~~~~~\n+The porcelain format has a line per attribute.  Attributes are listed with a\n+label and value separated by a single space.  Boolean attributes (like 'bare'\n+and 'detached') are listed as a label only, and are only present if and only\n+if the value is true.  An empty line indicates the end of a worktree.  For\n+example:\n+\n+------------\n+S git worktree list --porcelain\n+worktree /path/to/bare-source\n+bare\n+\n+worktree /path/to/linked-worktree\n+HEAD abcd1234abcd1234abcd1234abcd1234abcd1234\n+branch refs/heads/master\n+\n+worktree /path/to/other-linked-worktree\n+HEAD 1234abc1234abc1234abc1234abc1234abc1234a\n+detached\n+\n+------------\n+\n EXAMPLES\n --------\n You are in the middle of a refactoring session and your boss comes in and\n@@ -167,7 +215,6 @@ performed manually, such as:\n - `remove` to remove a linked working tree and its administrative files (and\n   warn if the working tree is dirty)\n - `mv` to move or rename a working tree and update its administrative files\n-- `list` to list linked working trees\n - `lock` to prevent automatic pruning of administrative files (for instance,\n   for a working tree on a portable device)\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 71bb770..78d2690 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -8,10 +8,13 @@\n #include \"run-command.h\"\n #include \"sigchain.h\"\n #include \"refs.h\"\n+#include \"utf8.h\"\n+#include \"worktree.h\"\n \n static const char * const worktree_usage[] = {\n \tN_(\"git worktree add [<options>] <path> <branch>\"),\n \tN_(\"git worktree prune [<options>]\"),\n+\tN_(\"git worktree list [<options>]\"),\n \tNULL\n };\n \n@@ -359,6 +362,89 @@ static int add(int ac, const char **av, const char *prefix)\n \treturn add_worktree(path, branch, &opts);\n }\n \n+static void show_worktree_porcelain(struct worktree *wt)\n+{\n+\tprintf(\"worktree %s\\n\", wt->path);\n+\tif (wt->is_bare)\n+\t\tprintf(\"bare\\n\");\n+\telse {\n+\t\tprintf(\"HEAD %s\\n\", sha1_to_hex(wt->head_sha1));\n+\t\tif (wt->is_detached)\n+\t\t\tprintf(\"detached\\n\");\n+\t\telse\n+\t\t\tprintf(\"branch %s\\n\", wt->head_ref);\n+\t}\n+\tprintf(\"\\n\");\n+}\n+\n+static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint cur_path_len = strlen(wt->path);\n+\tint path_adj = cur_path_len - utf8_strwidth(wt->path);\n+\n+\tstrbuf_addf(&sb, \"%-*s \", 1 + path_maxlen + path_adj, wt->path);\n+\tif (wt->is_bare)\n+\t\tstrbuf_addstr(&sb, \"(bare)\");\n+\telse {\n+\t\tstrbuf_addf(&sb, \"%-*s \", abbrev_len,\n+\t\t\t\tfind_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));\n+\t\tif (!wt->is_detached)\n+\t\t\tstrbuf_addf(&sb, \"[%s]\", shorten_unambiguous_ref(wt->head_ref, 0));\n+\t\telse\n+\t\t\tstrbuf_addstr(&sb, \"(detached HEAD)\");\n+\t}\n+\tprintf(\"%s\\n\", sb.buf);\n+\n+\tstrbuf_release(&sb);\n+}\n+\n+static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)\n+{\n+\tint i;\n+\n+\tfor (i = 0; wt[i]; i++) {\n+\t\tint sha1_len;\n+\t\tint path_len = strlen(wt[i]->path);\n+\n+\t\tif (path_len > *maxlen)\n+\t\t\t*maxlen = path_len;\n+\t\tsha1_len = strlen(find_unique_abbrev(wt[i]->head_sha1, *abbrev));\n+\t\tif (sha1_len > *abbrev)\n+\t\t\t*abbrev = sha1_len;\n+\t}\n+}\n+\n+static int list(int ac, const char **av, const char *prefix)\n+{\n+\tint porcelain = 0;\n+\n+\tstruct option options[] = {\n+\t\tOPT_BOOL(0, \"porcelain\", &porcelain, N_(\"machine-readable output\")),\n+\t\tOPT_END()\n+\t};\n+\n+\tac = parse_options(ac, av, prefix, options, worktree_usage, 0);\n+\tif (ac)\n+\t\tusage_with_options(worktree_usage, options);\n+\telse {\n+\t\tstruct worktree **worktrees = get_worktrees();\n+\t\tint path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;\n+\n+\t\tif (!porcelain)\n+\t\t\tmeasure_widths(worktrees, &abbrev, &path_maxlen);\n+\n+\t\tfor (i = 0; worktrees[i]; i++) {\n+\t\t\tif (porcelain)\n+\t\t\t\tshow_worktree_porcelain(worktrees[i]);\n+\t\t\telse\n+\t\t\t\tshow_worktree(worktrees[i], path_maxlen, abbrev);\n+\t\t}\n+\t\tfree_worktrees(worktrees);\n+\t}\n+\treturn 0;\n+}\n+\n int cmd_worktree(int ac, const char **av, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -371,5 +457,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)\n \t\treturn add(ac - 1, av + 1, prefix);\n \tif (!strcmp(av[1], \"prune\"))\n \t\treturn prune(ac - 1, av + 1, prefix);\n+\tif (!strcmp(av[1], \"list\"))\n+\t\treturn list(ac - 1, av + 1, prefix);\n \tusage_with_options(worktree_usage, options);\n }\ndiff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh\nnew file mode 100755\nindex 0000000..75ebb1b\n--- /dev/null\n+++ b/t/t2027-worktree-list.sh\n@@ -0,0 +1,89 @@\n+#!/bin/sh\n+\n+test_description='test git worktree list'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit init\n+'\n+\n+test_expect_success '\"list\" all worktrees from main' '\n+\techo \"$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]\" >expect &&\n+\ttest_when_finished \"rm -rf here && git worktree prune\" &&\n+\tgit worktree add --detach here master &&\n+\techo \"$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)\" >>expect &&\n+\tgit worktree list | sed \"s/  */ /g\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '\"list\" all worktrees from linked' '\n+\techo \"$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]\" >expect &&\n+\ttest_when_finished \"rm -rf here && git worktree prune\" &&\n+\tgit worktree add --detach here master &&\n+\techo \"$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)\" >>expect &&\n+\tgit -C here worktree list | sed \"s/  */ /g\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '\"list\" all worktrees --porcelain' '\n+\techo \"worktree $(git rev-parse --show-toplevel)\" >expect &&\n+\techo \"HEAD $(git rev-parse HEAD)\" >>expect &&\n+\techo \"branch $(git symbolic-ref HEAD)\" >>expect &&\n+\techo >>expect &&\n+\ttest_when_finished \"rm -rf here && git worktree prune\" &&\n+\tgit worktree add --detach here master &&\n+\techo \"worktree $(git -C here rev-parse --show-toplevel)\" >>expect &&\n+\techo \"HEAD $(git rev-parse HEAD)\" >>expect &&\n+\techo \"detached\" >>expect &&\n+\techo >>expect &&\n+\tgit worktree list --porcelain >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'bare repo setup' '\n+\tgit init --bare bare1 &&\n+\techo \"data\" >file1 &&\n+\tgit add file1 &&\n+\tgit commit -m\"File1: add data\" &&\n+\tgit push bare1 master &&\n+\tgit reset --hard HEAD^\n+'\n+\n+test_expect_success '\"list\" all worktrees from bare main' '\n+\ttest_when_finished \"rm -rf there && git -C bare1 worktree prune\" &&\n+\tgit -C bare1 worktree add --detach ../there master &&\n+\techo \"$(pwd)/bare1 (bare)\" >expect &&\n+\techo \"$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)\" >>expect &&\n+\tgit -C bare1 worktree list | sed \"s/  */ /g\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '\"list\" all worktrees --porcelain from bare main' '\n+\ttest_when_finished \"rm -rf there && git -C bare1 worktree prune\" &&\n+\tgit -C bare1 worktree add --detach ../there master &&\n+\techo \"worktree $(pwd)/bare1\" >expect &&\n+\techo \"bare\" >>expect &&\n+\techo >>expect &&\n+\techo \"worktree $(git -C there rev-parse --show-toplevel)\" >>expect &&\n+\techo \"HEAD $(git -C there rev-parse HEAD)\" >>expect &&\n+\techo \"detached\" >>expect &&\n+\techo >>expect &&\n+\tgit -C bare1 worktree list --porcelain >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '\"list\" all worktrees from linked with a bare main' '\n+\ttest_when_finished \"rm -rf there && git -C bare1 worktree prune\" &&\n+\tgit -C bare1 worktree add --detach ../there master &&\n+\techo \"$(pwd)/bare1 (bare)\" >expect &&\n+\techo \"$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)\" >>expect &&\n+\tgit -C there worktree list | sed \"s/  */ /g\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'bare repo cleanup' '\n+\trm -rf bare1\n+'\n+\n+test_done\n-- \n2.6.0\n"}]}