Subject: Re: Bug? git worktree fails with master on bare repo

From: Dennis Kaarsemaker <[email-masked]>
Date: Sun, 09 Oct 2016 09:51:53 +0200

On Sat, 2016-10-08 at 19:30 -0500, Michael Tutty wrote:
> Hey all,
> I'm working on some server-side software to do a merge. By using git
> worktree it's possible to check out a given branch for a bare repo and
> merge another branch into it. It's very fast, even with large
> repositories.
> 
> The only exception seems to be merging to master. When I do git
> worktree add /tmp/path/to/worktree master I get an error:
> 
> [fatal: 'master' is already checked out at '/path/to/bare/repo']
> 
> But this is clearly not true, git worktree list gives:
> 
> [/path/to/bare/repo (bare)]
> 
> ...and of course, there is no work tree at that path, just the bare
> repo files you'd expect.

The worktree code treats the base repo as a worktree, even if it's
bare. For the purpose of being able to do a checkout of the main branch
of a bare repo, this patch should do:

diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 4bcc335..b618d6b 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -138,6 +138,14 @@ test_expect_success 'checkout from a bare repo without "add"' '
        )
 '
 
+test_expect_success '"add" default branch of a bare repo' '
+       (
+               git clone --bare . bare2 &&
+               cd bare2 &&
+               git worktree add ../there3 master
+       )
+'
+
 test_expect_success 'checkout with grafts' '
        test_when_finished rm .git/info/grafts &&
        test_commit abc &&
diff --git a/worktree.c b/worktree.c
index 5acfe4c..35e95b7 100644
--- a/worktree.c
+++ b/worktree.c
@@ -345,6 +345,8 @@ const struct worktree *find_shared_symref(const char *symref,
 
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
+               if(wt->is_bare)
+                       continue;
 
                if (wt->is_detached && !strcmp(symref, "HEAD")) {
                        if (is_worktree_being_rebased(wt, target)) {

But I'm wondering why the worktree code does this. A bare repo isn't a
worktree and I think it shouldn't treat it as one. A patch that rips
out this feature and updates the tests to match would look like this:

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 5c4854d..3600530 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -382,15 +382,11 @@ static int add(int ac, const char **av, const char *prefix)
 static void show_worktree_porcelain(struct worktree *wt)
 {
 	printf("worktree %s\n", wt->path);
-	if (wt->is_bare)
-		printf("bare\n");
-	else {
-		printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
-		if (wt->is_detached)
-			printf("detached\n");
-		else
-			printf("branch %s\n", wt->head_ref);
-	}
+	printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
+	if (wt->is_detached)
+		printf("detached\n");
+	else
+		printf("branch %s\n", wt->head_ref);
 	printf("\n");
 }
 
@@ -401,16 +397,12 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
 	int path_adj = cur_path_len - utf8_strwidth(wt->path);
 
 	strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
-	if (wt->is_bare)
-		strbuf_addstr(&sb, "(bare)");
-	else {
-		strbuf_addf(&sb, "%-*s ", abbrev_len,
-				find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
-		if (!wt->is_detached)
-			strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0));
-		else
-			strbuf_addstr(&sb, "(detached HEAD)");
-	}
+	strbuf_addf(&sb, "%-*s ", abbrev_len,
+			find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
+	if (!wt->is_detached)
+		strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0));
+	else
+		strbuf_addstr(&sb, "(detached HEAD)");
 	printf("%s\n", sb.buf);
 
 	strbuf_release(&sb);
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 4bcc335..b618d6b 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -138,6 +138,14 @@ test_expect_success 'checkout from a bare repo without "add"' '
 	)
 '
 
+test_expect_success '"add" default branch of a bare repo' '
+	(
+		git clone --bare . bare2 &&
+		cd bare2 &&
+		git worktree add ../there3 master
+	)
+'
+
 test_expect_success 'checkout with grafts' '
 	test_when_finished rm .git/info/grafts &&
 	test_commit abc &&
diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh
index 1b1b65a..842e9d9 100755
--- a/t/t2027-worktree-list.sh
+++ b/t/t2027-worktree-list.sh
@@ -62,9 +62,8 @@ test_expect_success 'bare repo setup' '
 
 test_expect_success '"list" all worktrees from bare main' '
 	test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
-	git -C bare1 worktree add --detach ../there master &&
-	echo "$(pwd)/bare1 (bare)" >expect &&
-	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
+	git -C bare1 worktree add ../there master &&
+	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) [master]" >expect &&
 	git -C bare1 worktree list | sed "s/  */ /g" >actual &&
 	test_cmp expect actual
 '
@@ -72,10 +71,7 @@ test_expect_success '"list" all worktrees from bare main' '
 test_expect_success '"list" all worktrees --porcelain from bare main' '
 	test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
 	git -C bare1 worktree add --detach ../there master &&
-	echo "worktree $(pwd)/bare1" >expect &&
-	echo "bare" >>expect &&
-	echo >>expect &&
-	echo "worktree $(git -C there rev-parse --show-toplevel)" >>expect &&
+	echo "worktree $(git -C there rev-parse --show-toplevel)" >expect &&
 	echo "HEAD $(git -C there rev-parse HEAD)" >>expect &&
 	echo "detached" >>expect &&
 	echo >>expect &&
@@ -85,9 +81,8 @@ test_expect_success '"list" all worktrees --porcelain from bare main' '
 
 test_expect_success '"list" all worktrees from linked with a bare main' '
 	test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
-	git -C bare1 worktree add --detach ../there master &&
-	echo "$(pwd)/bare1 (bare)" >expect &&
-	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
+	git -C bare1 worktree add ../there master &&
+	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) [master]" >expect &&
 	git -C there worktree list | sed "s/  */ /g" >actual &&
 	test_cmp expect actual
 '
diff --git a/worktree.c b/worktree.c
index 5acfe4c..d4dbaab 100644
--- a/worktree.c
+++ b/worktree.c
@@ -84,7 +84,7 @@ static struct worktree *get_main_worktree(void)
 	strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
 	is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
 	if (is_bare)
-		strbuf_strip_suffix(&worktree_path, "/.");
+		goto done;
 
 	strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
 
@@ -94,7 +94,6 @@ static struct worktree *get_main_worktree(void)
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
 	worktree->id = NULL;
-	worktree->is_bare = is_bare;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
@@ -141,7 +140,6 @@ static struct worktree *get_linked_worktree(const char *id)
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
 	worktree->id = xstrdup(id);
-	worktree->is_bare = 0;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
diff --git a/worktree.h b/worktree.h
index 90e1311..04a75e8 100644
--- a/worktree.h
+++ b/worktree.h
@@ -8,7 +8,6 @@ struct worktree {
 	char *lock_reason;	/* internal use */
 	unsigned char head_sha1[20];
 	int is_detached;
-	int is_bare;
 	int is_current;
 	int lock_reason_valid;
 };