All of lore.kernel.org
 help / color / mirror / Atom feed
* possible to checkout same branch in different worktree
@ 2016-04-15 13:53 Reto Hablützel
  2016-04-15 15:36 ` Junio C Hamano
  0 siblings, 1 reply; 162+ messages in thread
From: Reto Hablützel @ 2016-04-15 13:53 UTC (permalink / raw)
  To: git

Hi,

the checkout command prevents me from checking out a branch in the
current worktree if it is already checked out in another worktree.

However, if I rebase the branch in the current worktree onto the
branch in the other worktree, I end up in a situation where the same
branch is checked out twice in the two worktrees.

- Reto

--

PGP
Key ID = 0x67D30264
Fingerprint = E235 F740 4419 5B4B 29EC 3437 CBF2 2CB7 67D3 0264

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: possible to checkout same branch in different worktree
  2016-04-15 13:53 possible to checkout same branch in different worktree Reto Hablützel
@ 2016-04-15 15:36 ` Junio C Hamano
  2016-04-16  0:51   ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-04-15 15:36 UTC (permalink / raw)
  To: Reto Hablützel
  Cc: git, Nguyễn Thái Ngọc Duy, Michael Rappazzo

Reto Hablützel <rethab.ch@gmail.com> writes:

> the checkout command prevents me from checking out a branch in the
> current worktree if it is already checked out in another worktree.
>
> However, if I rebase the branch in the current worktree onto the
> branch in the other worktree, I end up in a situation where the same
> branch is checked out twice in the two worktrees.

I agree that any end-user facing subcommand like "git rebase", even
if it is not "git checkout", should refuse to work on and update a
branch that is checked out elsewhere.  Otherwise it will end up
causing confusion.

Thanks for a report.  The "multiple worktree" feature is still
highly experimental and those who are working on it would appreciate
your reporting issues with it.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: possible to checkout same branch in different worktree
  2016-04-15 15:36 ` Junio C Hamano
@ 2016-04-16  0:51   ` Duy Nguyen
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-04-16  0:51 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Reto Hablützel, Git Mailing List, Michael Rappazzo

On Fri, Apr 15, 2016 at 10:36 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Reto Hablützel <rethab.ch@gmail.com> writes:
>
>> the checkout command prevents me from checking out a branch in the
>> current worktree if it is already checked out in another worktree.
>>
>> However, if I rebase the branch in the current worktree onto the
>> branch in the other worktree, I end up in a situation where the same
>> branch is checked out twice in the two worktrees.
>
> I agree that any end-user facing subcommand like "git rebase", even
> if it is not "git checkout", should refuse to work on and update a
> branch that is checked out elsewhere.  Otherwise it will end up
> causing confusion.

I agree. I suppose we need same treatment for git-push? A push can be
rejected if the pushed ref is being checked out. Suppose HEAD is in
the middle of a rebase (or am), it fails to detect ref name (and thus
checkout state) the same way here and we definitely do not want "git
rebase" to simply overwrite whatever is updated by the push request.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH 0/7] fix checking out a being-rebased branch
  2016-04-16  0:51   ` Duy Nguyen
@ 2016-04-17 12:59     ` Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 1/7] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
                         ` (8 more replies)
  0 siblings, 9 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

First cut. I need opinions on 05/07, which converts
wt_status_get_state() to support selecting any worktree. I'm not super
happy with leaving "TODO: not supported yet" comments, even though
strictly speaking this series does not need it.

The reason it's left unsupported is, in order to add
worktree_get_sha1() or similar, resolve_ref() will be touched. But
that function is going under some changes for quite some time now, for
lmdb backend support, and I don't want to create more code conflicts
there, potentially delaying lmdb backend further.

Another option is leave wt_status_get_state() alone, factor out the
rebase-detection code and use that for worktree/checkout. We save a
few syscalls this way too.

Comments?

  [01/07] path.c: add git_common_path() and strbuf_git_common_path()
  [02/07] worktree.c: store "id" instead of "git_dir"
  [03/07] path.c: refactor and add worktree_git_path()
  [04/07] worktree.c: add worktree_git_path_..._head()
  [05/07] wt-status.c: make wt_status_get_state() support worktree
  [06/07] worktree.c: avoid referencing to worktrees[i] multiple times
  [07/07] checkout: prevent checking out a branch being rebased in another worktree

Total 6 files changed, 167 insertions(+), 38 deletions(-)

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH 1/7] path.c: add git_common_path() and strbuf_git_common_path()
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
@ 2016-04-17 12:59       ` Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 2/7] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
                         ` (7 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

These are mostly convenient functions to reduce code duplication. Most
of the time, we should be able to get by with git_path() which handles
$GIT_COMMON_DIR internally. However there are a few cases where we need
to construct paths manually, for example some paths from a specific
worktree. These functions will enable that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 cache.h |  3 +++
 path.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/cache.h b/cache.h
index 2711048..c04a17f 100644
--- a/cache.h
+++ b/cache.h
@@ -799,11 +799,14 @@ extern void check_repository_format(void);
  */
 extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
 extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
diff --git a/path.c b/path.c
index bbaea5a..2ebb23d 100644
--- a/path.c
+++ b/path.c
@@ -503,6 +503,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 	va_end(args);
 }
 
+static void do_git_common_path(struct strbuf *buf,
+			       const char *fmt,
+			       va_list args)
+{
+	strbuf_addstr(buf, get_git_common_dir());
+	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+		strbuf_addch(buf, '/');
+	strbuf_vaddf(buf, fmt, args);
+	strbuf_cleanup_path(buf);
+}
+
+const char *git_common_path(const char *fmt, ...)
+{
+	struct strbuf *pathname = get_pathname();
+	va_list args;
+	va_start(args, fmt);
+	do_git_common_path(pathname, fmt, args);
+	va_end(args);
+	return pathname->buf;
+}
+
+void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	do_git_common_path(sb, fmt, args);
+	va_end(args);
+}
+
 int validate_headref(const char *path)
 {
 	struct stat st;
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 2/7] worktree.c: store "id" instead of "git_dir"
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 1/7] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
@ 2016-04-17 12:59       ` Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 3/7] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
                         ` (6 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

We can reconstruct git_dir from id quite easily. It's a bit hackier to
do the reverse.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 31 ++++++++++++++++++-------------
 worktree.h |  8 +++++++-
 2 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/worktree.c b/worktree.c
index 6181a66..5ae54f0 100644
--- a/worktree.c
+++ b/worktree.c
@@ -9,7 +9,7 @@ void free_worktrees(struct worktree **worktrees)
 
 	for (i = 0; worktrees[i]; i++) {
 		free(worktrees[i]->path);
-		free(worktrees[i]->git_dir);
+		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
 		free(worktrees[i]);
 	}
@@ -74,13 +74,11 @@ static struct worktree *get_main_worktree(void)
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf worktree_path = STRBUF_INIT;
-	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf head_ref = STRBUF_INIT;
 	int is_bare = 0;
 	int is_detached = 0;
 
-	strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
-	strbuf_addbuf(&worktree_path, &gitdir);
+	strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
 	is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
 	if (is_bare)
 		strbuf_strip_suffix(&worktree_path, "/.");
@@ -92,7 +90,7 @@ static struct worktree *get_main_worktree(void)
 
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
-	worktree->git_dir = strbuf_detach(&gitdir, NULL);
+	worktree->id = NULL;
 	worktree->is_bare = is_bare;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
@@ -100,7 +98,6 @@ static struct worktree *get_main_worktree(void)
 
 done:
 	strbuf_release(&path);
-	strbuf_release(&gitdir);
 	strbuf_release(&worktree_path);
 	strbuf_release(&head_ref);
 	return worktree;
@@ -111,16 +108,13 @@ static struct worktree *get_linked_worktree(const char *id)
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf worktree_path = STRBUF_INIT;
-	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf head_ref = STRBUF_INIT;
 	int is_detached = 0;
 
 	if (!id)
 		die("Missing linked worktree name");
 
-	strbuf_addf(&gitdir, "%s/worktrees/%s",
-			absolute_path(get_git_common_dir()), id);
-	strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+	strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
 	if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
 		/* invalid gitdir file */
 		goto done;
@@ -140,7 +134,7 @@ static struct worktree *get_linked_worktree(const char *id)
 
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
-	worktree->git_dir = strbuf_detach(&gitdir, NULL);
+	worktree->id = xstrdup(id);
 	worktree->is_bare = 0;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
@@ -148,7 +142,6 @@ static struct worktree *get_linked_worktree(const char *id)
 
 done:
 	strbuf_release(&path);
-	strbuf_release(&gitdir);
 	strbuf_release(&worktree_path);
 	strbuf_release(&head_ref);
 	return worktree;
@@ -188,6 +181,16 @@ struct worktree **get_worktrees(void)
 	return list;
 }
 
+const char *get_worktree_git_dir(const struct worktree *wt)
+{
+	if (!wt)
+		return get_git_dir();
+	else if (!wt->id)
+		return get_git_common_dir();
+	else
+		return git_common_path("worktrees/%s", wt->id);
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
 	char *existing = NULL;
@@ -199,7 +202,9 @@ char *find_shared_symref(const char *symref, const char *target)
 	for (i = 0; worktrees[i]; i++) {
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
-		strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+		strbuf_addf(&path, "%s/%s",
+			    get_worktree_git_dir(worktrees[i]),
+			    symref);
 
 		if (parse_ref(path.buf, &sb, NULL)) {
 			continue;
diff --git a/worktree.h b/worktree.h
index b4b3dda..3198c8d 100644
--- a/worktree.h
+++ b/worktree.h
@@ -3,7 +3,7 @@
 
 struct worktree {
 	char *path;
-	char *git_dir;
+	char *id;
 	char *head_ref;
 	unsigned char head_sha1[20];
 	int is_detached;
@@ -23,6 +23,12 @@ struct worktree {
 extern struct worktree **get_worktrees(void);
 
 /*
+ * Return git dir of the worktree. Note that the path may be relative.
+ * If wt is NULL, git dir of current worktree is returned.
+ */
+extern const char *get_worktree_git_dir(const struct worktree *wt);
+
+/*
  * Free up the memory for worktree(s)
  */
 extern void free_worktrees(struct worktree **);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 3/7] path.c: refactor and add worktree_git_path()
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 1/7] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 2/7] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
@ 2016-04-17 12:59       ` Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 4/7] worktree.c: add worktree_git_path_..._head() Nguyễn Thái Ngọc Duy
                         ` (5 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

do_git_path(), which is the common code for all git_path* functions, is
modified to take a worktree struct and can produce paths for any
worktree.

worktree_git_path() is the first function that makes use of this. It can
be used to write code that can examine any worktree. For example,
wt_status_get_state() will be converted using this to take
am/rebase/... state of any worktree.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 path.c     | 34 ++++++++++++++++++++++++++++------
 worktree.h | 11 +++++++++++
 2 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/path.c b/path.c
index 2ebb23d..c421d37 100644
--- a/path.c
+++ b/path.c
@@ -5,6 +5,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
+#include "worktree.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -383,10 +384,11 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
 		update_common_dir(buf, git_dir_len, NULL);
 }
 
-static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+			const char *fmt, va_list args)
 {
 	int gitdir_len;
-	strbuf_addstr(buf, get_git_dir());
+	strbuf_addstr(buf, get_worktree_git_dir(wt));
 	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
 		strbuf_addch(buf, '/');
 	gitdir_len = buf->len;
@@ -400,7 +402,7 @@ char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	va_list args;
 	strbuf_reset(buf);
 	va_start(args, fmt);
-	do_git_path(buf, fmt, args);
+	do_git_path(NULL, buf, fmt, args);
 	va_end(args);
 	return buf->buf;
 }
@@ -409,7 +411,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 {
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(sb, fmt, args);
+	do_git_path(NULL, sb, fmt, args);
 	va_end(args);
 }
 
@@ -418,7 +420,7 @@ const char *git_path(const char *fmt, ...)
 	struct strbuf *pathname = get_pathname();
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(pathname, fmt, args);
+	do_git_path(NULL, pathname, fmt, args);
 	va_end(args);
 	return pathname->buf;
 }
@@ -428,7 +430,7 @@ char *git_pathdup(const char *fmt, ...)
 	struct strbuf path = STRBUF_INIT;
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(&path, fmt, args);
+	do_git_path(NULL, &path, fmt, args);
 	va_end(args);
 	return strbuf_detach(&path, NULL);
 }
@@ -454,6 +456,26 @@ const char *mkpath(const char *fmt, ...)
 	return cleanup_path(pathname->buf);
 }
 
+const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
+{
+	struct strbuf *pathname = get_pathname();
+	va_list args;
+	va_start(args, fmt);
+	do_git_path(wt, pathname, fmt, args);
+	va_end(args);
+	return pathname->buf;
+}
+
+char *worktree_git_pathdup(const struct worktree *wt, const char *fmt, ...)
+{
+	struct strbuf path = STRBUF_INIT;
+	va_list args;
+	va_start(args, fmt);
+	do_git_path(wt, &path, fmt, args);
+	va_end(args);
+	return strbuf_detach(&path, NULL);
+}
+
 static void do_submodule_path(struct strbuf *buf, const char *path,
 			      const char *fmt, va_list args)
 {
diff --git a/worktree.h b/worktree.h
index 3198c8d..9ae2028 100644
--- a/worktree.h
+++ b/worktree.h
@@ -41,4 +41,15 @@ extern void free_worktrees(struct worktree **);
  */
 extern char *find_shared_symref(const char *symref, const char *target);
 
+/*
+ * Similar to git_path() and git_pathdup() but can produce paths for a
+ * specified worktree instead of current one
+ */
+extern const char *worktree_git_path(const struct worktree *wt,
+				     const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+extern char *worktree_git_pathdup(const struct worktree *wt,
+				  const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+
 #endif
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 4/7] worktree.c: add worktree_git_path_..._head()
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
                         ` (2 preceding siblings ...)
  2016-04-17 12:59       ` [PATCH 3/7] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
@ 2016-04-17 12:59       ` Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 5/7] wt-status.c: make wt_status_get_state() support worktree Nguyễn Thái Ngọc Duy
                         ` (4 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

These are a variant of git_path_..._head(), defined with GIT_PATH_FUNC
macro but they takes worktree into account. To be used when
wt_status_get_state() is converted to be worktree-aware.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c |  4 ++++
 worktree.h | 13 +++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/worktree.c b/worktree.c
index 5ae54f0..c5bf583 100644
--- a/worktree.c
+++ b/worktree.c
@@ -222,3 +222,7 @@ char *find_shared_symref(const char *symref, const char *target)
 
 	return existing;
 }
+
+WORKTREE_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
+WORKTREE_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD")
+WORKTREE_GIT_PATH_FUNC(revert_head, "REVERT_HEAD")
diff --git a/worktree.h b/worktree.h
index 9ae2028..b7c5da0 100644
--- a/worktree.h
+++ b/worktree.h
@@ -52,4 +52,17 @@ extern char *worktree_git_pathdup(const struct worktree *wt,
 				  const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
+#define WORKTREE_GIT_PATH_FUNC(func, filename) \
+	const char *worktree_git_path_ ## func(const struct worktree *wt) \
+	{ \
+		static char *ret; \
+		if (!ret) \
+			ret = worktree_git_pathdup(wt, filename); \
+		return ret; \
+	}
+
+const char *worktree_git_path_merge_head(const struct worktree *);
+const char *worktree_git_path_cherry_pick_head(const struct worktree *);
+const char *worktree_git_path_revert_head(const struct worktree *);
+
 #endif
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 5/7] wt-status.c: make wt_status_get_state() support worktree
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
                         ` (3 preceding siblings ...)
  2016-04-17 12:59       ` [PATCH 4/7] worktree.c: add worktree_git_path_..._head() Nguyễn Thái Ngọc Duy
@ 2016-04-17 12:59       ` Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 6/7] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
                         ` (3 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

The function is to be used to detect if a ref is checked out in another
worktree. In some cases, parsing "HEAD" alone is not enough because the
worktree may be under rebase or bisect.

Note that because get_sha1() cannot take a worktree (and of course will
not, we need something like worktree_get_sha1), cherry-pick and revert
detection will not work when the examined worktree is not the current
one. The same for get_detached_from.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wt-status.c | 42 +++++++++++++++++++++++++-----------------
 wt-status.h |  3 +++
 2 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index 1ea2ebe..6cca3a6 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -15,6 +15,7 @@
 #include "column.h"
 #include "strbuf.h"
 #include "utf8.h"
+#include "worktree.h"
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -1262,13 +1263,13 @@ static void show_bisect_in_progress(struct wt_status *s,
 /*
  * Extract branch information from rebase/bisect
  */
-static char *read_and_strip_branch(const char *path)
+static char *read_and_strip_branch(const struct worktree *wt, const char *path)
 {
 	struct strbuf sb = STRBUF_INIT;
 	unsigned char sha1[20];
 	const char *branch_name;
 
-	if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+	if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
 		goto got_nothing;
 
 	while (sb.len && sb.buf[sb.len - 1] == '\n')
@@ -1363,45 +1364,52 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 void wt_status_get_state(struct wt_status_state *state,
 			 int get_detached_from)
 {
+	const struct worktree *wt = state->wt;
 	struct stat st;
 	unsigned char sha1[20];
 
-	if (!stat(git_path_merge_head(), &st)) {
+	if (!stat(worktree_git_path_merge_head(wt), &st)) {
 		state->merge_in_progress = 1;
-	} else if (!stat(git_path("rebase-apply"), &st)) {
-		if (!stat(git_path("rebase-apply/applying"), &st)) {
+	} else if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
+		if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
 			state->am_in_progress = 1;
-			if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+			if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
 				state->am_empty_patch = 1;
 		} else {
 			state->rebase_in_progress = 1;
-			state->branch = read_and_strip_branch("rebase-apply/head-name");
-			state->onto = read_and_strip_branch("rebase-apply/onto");
+			state->branch = read_and_strip_branch(wt, "rebase-apply/head-name");
+			state->onto = read_and_strip_branch(wt, "rebase-apply/onto");
 		}
-	} else if (!stat(git_path("rebase-merge"), &st)) {
-		if (!stat(git_path("rebase-merge/interactive"), &st))
+	} else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
+		if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
 			state->rebase_interactive_in_progress = 1;
 		else
 			state->rebase_in_progress = 1;
-		state->branch = read_and_strip_branch("rebase-merge/head-name");
-		state->onto = read_and_strip_branch("rebase-merge/onto");
-	} else if (!stat(git_path_cherry_pick_head(), &st) &&
+		state->branch = read_and_strip_branch(wt, "rebase-merge/head-name");
+		state->onto = read_and_strip_branch(wt, "rebase-merge/onto");
+	} else if (!wt && /* TODO: get_sha1() cannot select worktree yet */
+		   !stat(worktree_git_path_cherry_pick_head(wt), &st) &&
 			!get_sha1("CHERRY_PICK_HEAD", sha1)) {
 		state->cherry_pick_in_progress = 1;
 		hashcpy(state->cherry_pick_head_sha1, sha1);
 	}
-	if (!stat(git_path("BISECT_LOG"), &st)) {
+	if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
 		state->bisect_in_progress = 1;
-		state->branch = read_and_strip_branch("BISECT_START");
+		state->branch = read_and_strip_branch(wt, "BISECT_START");
 	}
-	if (!stat(git_path_revert_head(), &st) &&
+	if (!wt &&  /* TODO: get_sha1() cannot select worktree yet */
+	    !stat(worktree_git_path_revert_head(wt), &st) &&
 	    !get_sha1("REVERT_HEAD", sha1)) {
 		state->revert_in_progress = 1;
 		hashcpy(state->revert_head_sha1, sha1);
 	}
 
-	if (get_detached_from)
+	if (get_detached_from) {
+		if (wt)
+			die("BUG: %s:%d: non-zero get_detached_from is not supported",
+			    __FILE__, __LINE__);
 		wt_status_get_detached_from(state);
+	}
 }
 
 static void wt_status_print_state(struct wt_status *s,
diff --git a/wt-status.h b/wt-status.h
index c9b3b74..d31db38 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -6,6 +6,8 @@
 #include "color.h"
 #include "pathspec.h"
 
+struct worktree;
+
 enum color_wt_status {
 	WT_STATUS_HEADER = 0,
 	WT_STATUS_UPDATED,
@@ -77,6 +79,7 @@ struct wt_status {
 };
 
 struct wt_status_state {
+	struct worktree *wt;
 	int merge_in_progress;
 	int am_in_progress;
 	int am_empty_patch;
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 6/7] worktree.c: avoid referencing to worktrees[i] multiple times
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
                         ` (4 preceding siblings ...)
  2016-04-17 12:59       ` [PATCH 5/7] wt-status.c: make wt_status_get_state() support worktree Nguyễn Thái Ngọc Duy
@ 2016-04-17 12:59       ` Nguyễn Thái Ngọc Duy
  2016-04-17 12:59       ` [PATCH 7/7] checkout: prevent checking out a branch being rebased in another worktree Nguyễn Thái Ngọc Duy
                         ` (2 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/worktree.c b/worktree.c
index c5bf583..3b7c6f2 100644
--- a/worktree.c
+++ b/worktree.c
@@ -200,10 +200,12 @@ char *find_shared_symref(const char *symref, const char *target)
 	int i = 0;
 
 	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
 		strbuf_addf(&path, "%s/%s",
-			    get_worktree_git_dir(worktrees[i]),
+			    get_worktree_git_dir(wt),
 			    symref);
 
 		if (parse_ref(path.buf, &sb, NULL)) {
@@ -211,7 +213,7 @@ char *find_shared_symref(const char *symref, const char *target)
 		}
 
 		if (!strcmp(sb.buf, target)) {
-			existing = xstrdup(worktrees[i]->path);
+			existing = xstrdup(wt->path);
 			break;
 		}
 	}
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 7/7] checkout: prevent checking out a branch being rebased in another worktree
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
                         ` (5 preceding siblings ...)
  2016-04-17 12:59       ` [PATCH 6/7] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
@ 2016-04-17 12:59       ` Nguyễn Thái Ngọc Duy
  2016-04-18 17:42       ` [PATCH 0/7] fix checking out a being-rebased branch Junio C Hamano
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-17 12:59 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

We failed to detect this case because the rebase-in-progress worktree
has HEAD in detached state. Some more poking around is needed to
determine if a rebase is in progress.

Note that even though bisect can also put a worktree in detached state
and hide the real branch, we allow to checkout anyway. This is because
unlike rebase, bisect does not update the branch. Yes it's still a bit
confusing when the user doing the bisecting realizes that the branch is
updated elsewhere, but at least we do not lose any valuable ref update.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/worktree.c b/worktree.c
index 3b7c6f2..6dc40c2 100644
--- a/worktree.c
+++ b/worktree.c
@@ -2,6 +2,7 @@
 #include "refs.h"
 #include "strbuf.h"
 #include "worktree.h"
+#include "wt-status.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -202,6 +203,28 @@ char *find_shared_symref(const char *symref, const char *target)
 	for (i = 0; worktrees[i]; i++) {
 		struct worktree *wt = worktrees[i];
 
+		if (wt->is_detached) {
+			struct wt_status_state wss;
+			int found;
+
+			memset(&wss, 0, sizeof(wss));
+			wss.wt = wt;
+			wt_status_get_state(&wss, 0);
+			found = (wss.rebase_in_progress ||
+				 wss.rebase_interactive_in_progress) &&
+				wss.branch &&
+				starts_with(target, "refs/heads/") &&
+				!strcmp(wss.branch,
+					target + strlen("refs/heads/"));
+			free(wss.branch);
+			free(wss.onto);
+			free(wss.detached_from);
+			if (found) {
+				existing = xstrdup(wt->path);
+				break;
+			}
+		}
+
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
 		strbuf_addf(&path, "%s/%s",
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH 0/7] fix checking out a being-rebased branch
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
                         ` (6 preceding siblings ...)
  2016-04-17 12:59       ` [PATCH 7/7] checkout: prevent checking out a branch being rebased in another worktree Nguyễn Thái Ngọc Duy
@ 2016-04-18 17:42       ` Junio C Hamano
  2016-04-19  1:04         ` Duy Nguyen
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
  8 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-04-18 17:42 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> First cut. I need opinions on 05/07, which converts
> wt_status_get_state() to support selecting any worktree. I'm not super
> happy with leaving "TODO: not supported yet" comments, even though
> strictly speaking this series does not need it.

It is a reasonable idea to hook this to wt_status_get_state(), I
would say.

> Another option is leave wt_status_get_state() alone, factor out the
> rebase-detection code and use that for worktree/checkout. We save a
> few syscalls this way too.
>
> Comments?
>
>   [01/07] path.c: add git_common_path() and strbuf_git_common_path()
>   [02/07] worktree.c: store "id" instead of "git_dir"
>   [03/07] path.c: refactor and add worktree_git_path()
>   [04/07] worktree.c: add worktree_git_path_..._head()

I actually wondered how ky/branch-[dm]-worktree topics to avoid
"branch -d" and "branch -m" from removing or renaming a branch that
is checked out in other worktrees from screwing them up.  There may
be a missed opportunity to clean them up with using these?

>   [05/07] wt-status.c: make wt_status_get_state() support worktree
>   [06/07] worktree.c: avoid referencing to worktrees[i] multiple times
>   [07/07] checkout: prevent checking out a branch being rebased in another worktree
>
> Total 6 files changed, 167 insertions(+), 38 deletions(-)

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 0/7] fix checking out a being-rebased branch
  2016-04-18 17:42       ` [PATCH 0/7] fix checking out a being-rebased branch Junio C Hamano
@ 2016-04-19  1:04         ` Duy Nguyen
  2016-04-19 20:43           ` Junio C Hamano
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-04-19  1:04 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, rethab.ch, Mike Rappazzo

On Tue, Apr 19, 2016 at 12:42 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Another option is leave wt_status_get_state() alone, factor out the
>> rebase-detection code and use that for worktree/checkout. We save a
>> few syscalls this way too.
>>
>> Comments?
>>
>>   [01/07] path.c: add git_common_path() and strbuf_git_common_path()
>>   [02/07] worktree.c: store "id" instead of "git_dir"
>>   [03/07] path.c: refactor and add worktree_git_path()
>>   [04/07] worktree.c: add worktree_git_path_..._head()
>
> I actually wondered how ky/branch-[dm]-worktree topics to avoid
> "branch -d" and "branch -m" from removing or renaming a branch that
> is checked out in other worktrees from screwing them up.  There may
> be a missed opportunity to clean them up with using these?

branch-m-worktree uses info populated at get_worktrees() phase.
branch-d-worktree uses find_shared_symref() which is modified in this
series in order to detect rebase branch. So both topics have the same
problem when a branch is being rebased and if I do it right, I'll fix
them both without extra code. Actually I may need to check
branch-m-worktree again, renaming a branch under rebase might cause
problems, I need to have a closer look at git-rebase*.sh.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 0/7] fix checking out a being-rebased branch
  2016-04-19  1:04         ` Duy Nguyen
@ 2016-04-19 20:43           ` Junio C Hamano
  0 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-04-19 20:43 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, rethab.ch, Mike Rappazzo

Duy Nguyen <pclouds@gmail.com> writes:

> On Tue, Apr 19, 2016 at 12:42 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>> Another option is leave wt_status_get_state() alone, factor out the
>>> rebase-detection code and use that for worktree/checkout. We save a
>>> few syscalls this way too.
>>>
>>> Comments?
>>>
>>>   [01/07] path.c: add git_common_path() and strbuf_git_common_path()
>>>   [02/07] worktree.c: store "id" instead of "git_dir"
>>>   [03/07] path.c: refactor and add worktree_git_path()
>>>   [04/07] worktree.c: add worktree_git_path_..._head()
>>
>> I actually wondered how ky/branch-[dm]-worktree topics to avoid
>> "branch -d" and "branch -m" from removing or renaming a branch that
>> is checked out in other worktrees from screwing them up.  There may
>> be a missed opportunity to clean them up with using these?
>
> branch-m-worktree uses info populated at get_worktrees() phase.
> branch-d-worktree uses find_shared_symref() which is modified in this
> series in order to detect rebase branch. So both topics have the same
> problem when a branch is being rebased and if I do it right, I'll fix
> them both without extra code.

Yup, that was exactly why I brought it up.

> Actually I may need to check branch-m-worktree again, renaming a
> branch under rebase might cause problems, I need to have a closer
> look at git-rebase*.sh.

Thanks.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v2 00/12] fix checking out a being-rebased branch
  2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
                         ` (7 preceding siblings ...)
  2016-04-18 17:42       ` [PATCH 0/7] fix checking out a being-rebased branch Junio C Hamano
@ 2016-04-20 13:24       ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
                           ` (13 more replies)
  8 siblings, 14 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Much happier with this version. This makes

 - git checkout refuse if a branch is under rebase or bisect
   elsewhere

 - git worktree add refuse if a branch is under rebase or bisect

 - git branch -D refuse if a branch is under rebase or bisect.
   This applies for single worktree case as well.

 - git branch -M refuse if a branch is under rebase or bisect
   (single worktree case as well)

  [01/12] path.c: add git_common_path() and strbuf_git_common_path()
  [02/12] worktree.c: store "id" instead of "git_dir"
  [03/12] worktree.c: make find_shared_symref() return struct worktree *
  [04/12] worktree.c: mark current worktree
  [05/12] path.c: refactor and add worktree_git_path()
  [06/12] wt-status.c: split rebase detection out of wt_status_get_state()
  [07/12] wt-status.c: make wt_status_check_rebase() work on any worktree
  [08/12] worktree.c: avoid referencing to worktrees[i] multiple times
  [09/12] worktree.c: test if branch being rebased in another worktree
  [10/12] wt-status.c: split bisect detection out of wt_status_get_state()
  [11/12] worktree.c: test if branch being bisected in another worktree
  [12/12] branch: do not rename a branch under bisect or rebase

Total 13 files changed, 335 insertions(+), 67 deletions(-)

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path()
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 18:11           ` Eric Sunshine
  2016-04-20 13:24         ` [PATCH v2 02/12] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
                           ` (12 subsequent siblings)
  13 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

These are mostly convenient functions to reduce code duplication. Most
of the time, we should be able to get by with git_path() which handles
$GIT_COMMON_DIR internally. However there are a few cases where we need
to construct paths manually, for example some paths from a specific
worktree. These functions will enable that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 cache.h |  3 +++
 path.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/cache.h b/cache.h
index 2711048..c04a17f 100644
--- a/cache.h
+++ b/cache.h
@@ -799,11 +799,14 @@ extern void check_repository_format(void);
  */
 extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
 extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
diff --git a/path.c b/path.c
index bbaea5a..2ebb23d 100644
--- a/path.c
+++ b/path.c
@@ -503,6 +503,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 	va_end(args);
 }
 
+static void do_git_common_path(struct strbuf *buf,
+			       const char *fmt,
+			       va_list args)
+{
+	strbuf_addstr(buf, get_git_common_dir());
+	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+		strbuf_addch(buf, '/');
+	strbuf_vaddf(buf, fmt, args);
+	strbuf_cleanup_path(buf);
+}
+
+const char *git_common_path(const char *fmt, ...)
+{
+	struct strbuf *pathname = get_pathname();
+	va_list args;
+	va_start(args, fmt);
+	do_git_common_path(pathname, fmt, args);
+	va_end(args);
+	return pathname->buf;
+}
+
+void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	do_git_common_path(sb, fmt, args);
+	va_end(args);
+}
+
 int validate_headref(const char *path)
 {
 	struct stat st;
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 02/12] worktree.c: store "id" instead of "git_dir"
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
                           ` (11 subsequent siblings)
  13 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

We can reconstruct git_dir from id quite easily. It's a bit hackier to
do the reverse.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c   |  3 ++-
 worktree.c | 31 ++++++++++++++++++-------------
 worktree.h |  8 +++++++-
 3 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/branch.c b/branch.c
index 4162443..0674a99 100644
--- a/branch.c
+++ b/branch.c
@@ -357,7 +357,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
 		if (strcmp(oldref, worktrees[i]->head_ref))
 			continue;
 
-		if (set_worktree_head_symref(worktrees[i]->git_dir, newref)) {
+		if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
+					     newref)) {
 			ret = -1;
 			error(_("HEAD of working tree %s is not updated"),
 			      worktrees[i]->path);
diff --git a/worktree.c b/worktree.c
index 6181a66..5ae54f0 100644
--- a/worktree.c
+++ b/worktree.c
@@ -9,7 +9,7 @@ void free_worktrees(struct worktree **worktrees)
 
 	for (i = 0; worktrees[i]; i++) {
 		free(worktrees[i]->path);
-		free(worktrees[i]->git_dir);
+		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
 		free(worktrees[i]);
 	}
@@ -74,13 +74,11 @@ static struct worktree *get_main_worktree(void)
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf worktree_path = STRBUF_INIT;
-	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf head_ref = STRBUF_INIT;
 	int is_bare = 0;
 	int is_detached = 0;
 
-	strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
-	strbuf_addbuf(&worktree_path, &gitdir);
+	strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
 	is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
 	if (is_bare)
 		strbuf_strip_suffix(&worktree_path, "/.");
@@ -92,7 +90,7 @@ static struct worktree *get_main_worktree(void)
 
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
-	worktree->git_dir = strbuf_detach(&gitdir, NULL);
+	worktree->id = NULL;
 	worktree->is_bare = is_bare;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
@@ -100,7 +98,6 @@ static struct worktree *get_main_worktree(void)
 
 done:
 	strbuf_release(&path);
-	strbuf_release(&gitdir);
 	strbuf_release(&worktree_path);
 	strbuf_release(&head_ref);
 	return worktree;
@@ -111,16 +108,13 @@ static struct worktree *get_linked_worktree(const char *id)
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf worktree_path = STRBUF_INIT;
-	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf head_ref = STRBUF_INIT;
 	int is_detached = 0;
 
 	if (!id)
 		die("Missing linked worktree name");
 
-	strbuf_addf(&gitdir, "%s/worktrees/%s",
-			absolute_path(get_git_common_dir()), id);
-	strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+	strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
 	if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
 		/* invalid gitdir file */
 		goto done;
@@ -140,7 +134,7 @@ static struct worktree *get_linked_worktree(const char *id)
 
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
-	worktree->git_dir = strbuf_detach(&gitdir, NULL);
+	worktree->id = xstrdup(id);
 	worktree->is_bare = 0;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
@@ -148,7 +142,6 @@ static struct worktree *get_linked_worktree(const char *id)
 
 done:
 	strbuf_release(&path);
-	strbuf_release(&gitdir);
 	strbuf_release(&worktree_path);
 	strbuf_release(&head_ref);
 	return worktree;
@@ -188,6 +181,16 @@ struct worktree **get_worktrees(void)
 	return list;
 }
 
+const char *get_worktree_git_dir(const struct worktree *wt)
+{
+	if (!wt)
+		return get_git_dir();
+	else if (!wt->id)
+		return get_git_common_dir();
+	else
+		return git_common_path("worktrees/%s", wt->id);
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
 	char *existing = NULL;
@@ -199,7 +202,9 @@ char *find_shared_symref(const char *symref, const char *target)
 	for (i = 0; worktrees[i]; i++) {
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
-		strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+		strbuf_addf(&path, "%s/%s",
+			    get_worktree_git_dir(worktrees[i]),
+			    symref);
 
 		if (parse_ref(path.buf, &sb, NULL)) {
 			continue;
diff --git a/worktree.h b/worktree.h
index b4b3dda..3198c8d 100644
--- a/worktree.h
+++ b/worktree.h
@@ -3,7 +3,7 @@
 
 struct worktree {
 	char *path;
-	char *git_dir;
+	char *id;
 	char *head_ref;
 	unsigned char head_sha1[20];
 	int is_detached;
@@ -23,6 +23,12 @@ struct worktree {
 extern struct worktree **get_worktrees(void);
 
 /*
+ * Return git dir of the worktree. Note that the path may be relative.
+ * If wt is NULL, git dir of current worktree is returned.
+ */
+extern const char *get_worktree_git_dir(const struct worktree *wt);
+
+/*
  * Free up the memory for worktree(s)
  */
 extern void free_worktrees(struct worktree **);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree *
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 02/12] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 17:05           ` Junio C Hamano
  2016-04-21  7:02           ` Eric Sunshine
  2016-04-20 13:24         ` [PATCH v2 04/12] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
                           ` (10 subsequent siblings)
  13 siblings, 2 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This gives the caller more information and they can answer things like,
"is it the main worktree" or "is it the current worktree". The latter
question is needed for the "checkout a rebase branch" case later.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c         |  9 +++++----
 builtin/branch.c |  8 ++++----
 builtin/notes.c  |  8 ++++----
 worktree.c       | 14 +++++++++-----
 worktree.h       |  7 ++++---
 5 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/branch.c b/branch.c
index 0674a99..a84fb2c 100644
--- a/branch.c
+++ b/branch.c
@@ -336,12 +336,13 @@ void remove_branch_state(void)
 
 void die_if_checked_out(const char *branch)
 {
-	char *existing;
+	const struct worktree *wt;
 
-	existing = find_shared_symref("HEAD", branch);
-	if (existing) {
+	wt = find_shared_symref("HEAD", branch);
+	if (wt) {
 		skip_prefix(branch, "refs/heads/", &branch);
-		die(_("'%s' is already checked out at '%s'"), branch, existing);
+		die(_("'%s' is already checked out at '%s'"),
+		    branch, wt->path);
 	}
 }
 
diff --git a/builtin/branch.c b/builtin/branch.c
index 0adba62..bcde87d 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -220,12 +220,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 		name = mkpathdup(fmt, bname.buf);
 
 		if (kinds == FILTER_REFS_BRANCHES) {
-			char *worktree = find_shared_symref("HEAD", name);
-			if (worktree) {
+			const struct worktree *wt =
+				find_shared_symref("HEAD", name);
+			if (wt) {
 				error(_("Cannot delete branch '%s' "
 					"checked out at '%s'"),
-				      bname.buf, worktree);
-				free(worktree);
+				      bname.buf, wt->path);
 				ret = 1;
 				continue;
 			}
diff --git a/builtin/notes.c b/builtin/notes.c
index 6fd058d..c65b59a 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -847,15 +847,15 @@ static int merge(int argc, const char **argv, const char *prefix)
 		update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
 	else { /* Merge has unresolved conflicts */
-		char *existing;
+		const struct worktree *wt;
 		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
 		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
 		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-		existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
-		if (existing)
+		wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+		if (wt)
 			die(_("A notes merge into %s is already in-progress at %s"),
-			    default_notes_ref(), existing);
+			    default_notes_ref(), wt->path);
 		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
 			die("Failed to store link to current notes ref (%s)",
 			    default_notes_ref());
diff --git a/worktree.c b/worktree.c
index 5ae54f0..360ba41 100644
--- a/worktree.c
+++ b/worktree.c
@@ -191,14 +191,19 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
-char *find_shared_symref(const char *symref, const char *target)
+const struct worktree *find_shared_symref(const char *symref,
+					  const char *target)
 {
-	char *existing = NULL;
+	const struct worktree *existing = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf sb = STRBUF_INIT;
-	struct worktree **worktrees = get_worktrees();
+	static struct worktree **worktrees;
 	int i = 0;
 
+	if (worktrees)
+		free_worktrees(worktrees);
+	worktrees = get_worktrees();
+
 	for (i = 0; worktrees[i]; i++) {
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
@@ -211,14 +216,13 @@ char *find_shared_symref(const char *symref, const char *target)
 		}
 
 		if (!strcmp(sb.buf, target)) {
-			existing = xstrdup(worktrees[i]->path);
+			existing = worktrees[i];
 			break;
 		}
 	}
 
 	strbuf_release(&path);
 	strbuf_release(&sb);
-	free_worktrees(worktrees);
 
 	return existing;
 }
diff --git a/worktree.h b/worktree.h
index 3198c8d..d71d7ec 100644
--- a/worktree.h
+++ b/worktree.h
@@ -36,9 +36,10 @@ extern void free_worktrees(struct worktree **);
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
  * or any linked worktree, and return the path to the exising worktree
- * if it is.  Returns NULL if there is no existing ref.  The caller is
- * responsible for freeing the returned path.
+ * if it is. Returns NULL if there is no existing ref. The result
+ * may be destroyed by the next call.
  */
-extern char *find_shared_symref(const char *symref, const char *target);
+extern const struct worktree *find_shared_symref(const char *symref,
+						 const char *target);
 
 #endif
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (2 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-21  7:20           ` Eric Sunshine
  2016-04-20 13:24         ` [PATCH v2 05/12] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
                           ` (9 subsequent siblings)
  13 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 18 +++++++++++++++++-
 worktree.h |  1 +
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/worktree.c b/worktree.c
index 360ba41..452f64a 100644
--- a/worktree.c
+++ b/worktree.c
@@ -2,6 +2,7 @@
 #include "refs.h"
 #include "strbuf.h"
 #include "worktree.h"
+#include "dir.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -94,6 +95,7 @@ static struct worktree *get_main_worktree(void)
 	worktree->is_bare = is_bare;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
+	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
 
 done:
@@ -138,6 +140,7 @@ static struct worktree *get_linked_worktree(const char *id)
 	worktree->is_bare = 0;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
+	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
 
 done:
@@ -150,10 +153,11 @@ done:
 struct worktree **get_worktrees(void)
 {
 	struct worktree **list = NULL;
+	struct strbuf git_dir = STRBUF_INIT;
 	struct strbuf path = STRBUF_INIT;
 	DIR *dir;
 	struct dirent *d;
-	int counter = 0, alloc = 2;
+	int i, counter = 0, alloc = 2;
 
 	list = xmalloc(alloc * sizeof(struct worktree *));
 
@@ -178,6 +182,18 @@ struct worktree **get_worktrees(void)
 	}
 	ALLOC_GROW(list, counter + 1, alloc);
 	list[counter] = NULL;
+
+	strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
+	for (i = 0; i < counter; i++) {
+		struct worktree *wt = list[i];
+		strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
+		wt->is_current = !strcmp_icase(git_dir.buf, path.buf);
+		strbuf_reset(&path);
+		if (wt->is_current)
+			break;
+	}
+	strbuf_release(&git_dir);
+	strbuf_release(&path);
 	return list;
 }
 
diff --git a/worktree.h b/worktree.h
index d71d7ec..625fb8d 100644
--- a/worktree.h
+++ b/worktree.h
@@ -8,6 +8,7 @@ struct worktree {
 	unsigned char head_sha1[20];
 	int is_detached;
 	int is_bare;
+	int is_current;
 };
 
 /* Functions for acting on the information about worktrees. */
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 05/12] path.c: refactor and add worktree_git_path()
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (3 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 04/12] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 06/12] wt-status.c: split rebase detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
                           ` (8 subsequent siblings)
  13 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

do_git_path(), which is the common code for all git_path* functions, is
modified to take a worktree struct and can produce paths for any
worktree.

worktree_git_path() is the first function that makes use of this. It can
be used to write code that can examine any worktree. For example,
wt_status_get_state() will be converted using this to take
am/rebase/... state of any worktree.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 path.c     | 34 ++++++++++++++++++++++++++++------
 worktree.h | 11 +++++++++++
 2 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/path.c b/path.c
index 2ebb23d..c421d37 100644
--- a/path.c
+++ b/path.c
@@ -5,6 +5,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
+#include "worktree.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -383,10 +384,11 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
 		update_common_dir(buf, git_dir_len, NULL);
 }
 
-static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+			const char *fmt, va_list args)
 {
 	int gitdir_len;
-	strbuf_addstr(buf, get_git_dir());
+	strbuf_addstr(buf, get_worktree_git_dir(wt));
 	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
 		strbuf_addch(buf, '/');
 	gitdir_len = buf->len;
@@ -400,7 +402,7 @@ char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	va_list args;
 	strbuf_reset(buf);
 	va_start(args, fmt);
-	do_git_path(buf, fmt, args);
+	do_git_path(NULL, buf, fmt, args);
 	va_end(args);
 	return buf->buf;
 }
@@ -409,7 +411,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 {
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(sb, fmt, args);
+	do_git_path(NULL, sb, fmt, args);
 	va_end(args);
 }
 
@@ -418,7 +420,7 @@ const char *git_path(const char *fmt, ...)
 	struct strbuf *pathname = get_pathname();
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(pathname, fmt, args);
+	do_git_path(NULL, pathname, fmt, args);
 	va_end(args);
 	return pathname->buf;
 }
@@ -428,7 +430,7 @@ char *git_pathdup(const char *fmt, ...)
 	struct strbuf path = STRBUF_INIT;
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(&path, fmt, args);
+	do_git_path(NULL, &path, fmt, args);
 	va_end(args);
 	return strbuf_detach(&path, NULL);
 }
@@ -454,6 +456,26 @@ const char *mkpath(const char *fmt, ...)
 	return cleanup_path(pathname->buf);
 }
 
+const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
+{
+	struct strbuf *pathname = get_pathname();
+	va_list args;
+	va_start(args, fmt);
+	do_git_path(wt, pathname, fmt, args);
+	va_end(args);
+	return pathname->buf;
+}
+
+char *worktree_git_pathdup(const struct worktree *wt, const char *fmt, ...)
+{
+	struct strbuf path = STRBUF_INIT;
+	va_list args;
+	va_start(args, fmt);
+	do_git_path(wt, &path, fmt, args);
+	va_end(args);
+	return strbuf_detach(&path, NULL);
+}
+
 static void do_submodule_path(struct strbuf *buf, const char *path,
 			      const char *fmt, va_list args)
 {
diff --git a/worktree.h b/worktree.h
index 625fb8d..9d2463e 100644
--- a/worktree.h
+++ b/worktree.h
@@ -43,4 +43,15 @@ extern void free_worktrees(struct worktree **);
 extern const struct worktree *find_shared_symref(const char *symref,
 						 const char *target);
 
+/*
+ * Similar to git_path() and git_pathdup() but can produce paths for a
+ * specified worktree instead of current one
+ */
+extern const char *worktree_git_path(const struct worktree *wt,
+				     const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+extern char *worktree_git_pathdup(const struct worktree *wt,
+				  const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+
 #endif
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 06/12] wt-status.c: split rebase detection out of wt_status_get_state()
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (4 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 05/12] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:48           ` Ramsay Jones
  2016-04-20 13:24         ` [PATCH v2 07/12] wt-status.c: make wt_status_check_rebase() work on any worktree Nguyễn Thái Ngọc Duy
                           ` (7 subsequent siblings)
  13 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

worktree.c:find_shared_symref() later needs to know if a branch is being
rebased, and only rebased only. Split this code so it can be used
independently from other in-progress tests.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wt-status.c | 23 +++++++++++++++++------
 wt-status.h |  1 +
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index 1ea2ebe..35787ec 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1360,15 +1360,11 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 	strbuf_release(&cb.buf);
 }
 
-void wt_status_get_state(struct wt_status_state *state,
-			 int get_detached_from)
+int wt_status_check_rebase(struct wt_status_state *state)
 {
 	struct stat st;
-	unsigned char sha1[20];
 
-	if (!stat(git_path_merge_head(), &st)) {
-		state->merge_in_progress = 1;
-	} else if (!stat(git_path("rebase-apply"), &st)) {
+	if (!stat(git_path("rebase-apply"), &st)) {
 		if (!stat(git_path("rebase-apply/applying"), &st)) {
 			state->am_in_progress = 1;
 			if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
@@ -1385,6 +1381,21 @@ void wt_status_get_state(struct wt_status_state *state,
 			state->rebase_in_progress = 1;
 		state->branch = read_and_strip_branch("rebase-merge/head-name");
 		state->onto = read_and_strip_branch("rebase-merge/onto");
+	} else
+		return 0;
+	return 1;
+}
+
+void wt_status_get_state(struct wt_status_state *state,
+			 int get_detached_from)
+{
+	struct stat st;
+	unsigned char sha1[20];
+
+	if (!stat(git_path_merge_head(), &st)) {
+		state->merge_in_progress = 1;
+	} else if (wt_status_check_rebase(state)) {
+		/* all set */
 	} else if (!stat(git_path_cherry_pick_head(), &st) &&
 			!get_sha1("CHERRY_PICK_HEAD", sha1)) {
 		state->cherry_pick_in_progress = 1;
diff --git a/wt-status.h b/wt-status.h
index c9b3b74..b398353 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -100,6 +100,7 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
+int wt_status_check_rebase(struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 07/12] wt-status.c: make wt_status_check_rebase() work on any worktree
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (5 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 06/12] wt-status.c: split rebase detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 08/12] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
                           ` (6 subsequent siblings)
  13 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This is a preparation step for find_shared_symref() to detect if any
worktree is being rebased.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wt-status.c | 33 ++++++++++++++++++++-------------
 wt-status.h |  5 ++++-
 2 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index 35787ec..2295682 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -15,6 +15,7 @@
 #include "column.h"
 #include "strbuf.h"
 #include "utf8.h"
+#include "worktree.h"
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -1262,13 +1263,13 @@ static void show_bisect_in_progress(struct wt_status *s,
 /*
  * Extract branch information from rebase/bisect
  */
-static char *read_and_strip_branch(const char *path)
+static char *get_branch(const struct worktree *wt, const char *path)
 {
 	struct strbuf sb = STRBUF_INIT;
 	unsigned char sha1[20];
 	const char *branch_name;
 
-	if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+	if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
 		goto got_nothing;
 
 	while (sb.len && sb.buf[sb.len - 1] == '\n')
@@ -1295,6 +1296,11 @@ got_nothing:
 	return NULL;
 }
 
+static char *read_and_strip_branch(const char *path)
+{
+	return get_branch(NULL, path);
+}
+
 struct grab_1st_switch_cbdata {
 	struct strbuf buf;
 	unsigned char nsha1[20];
@@ -1360,27 +1366,28 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 	strbuf_release(&cb.buf);
 }
 
-int wt_status_check_rebase(struct wt_status_state *state)
+int wt_status_check_rebase(const struct worktree *wt,
+			   struct wt_status_state *state)
 {
 	struct stat st;
 
-	if (!stat(git_path("rebase-apply"), &st)) {
-		if (!stat(git_path("rebase-apply/applying"), &st)) {
+	if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
+		if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
 			state->am_in_progress = 1;
-			if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+			if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
 				state->am_empty_patch = 1;
 		} else {
 			state->rebase_in_progress = 1;
-			state->branch = read_and_strip_branch("rebase-apply/head-name");
-			state->onto = read_and_strip_branch("rebase-apply/onto");
+			state->branch = get_branch(wt, "rebase-apply/head-name");
+			state->onto = get_branch(wt, "rebase-apply/onto");
 		}
-	} else if (!stat(git_path("rebase-merge"), &st)) {
-		if (!stat(git_path("rebase-merge/interactive"), &st))
+	} else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
+		if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
 			state->rebase_interactive_in_progress = 1;
 		else
 			state->rebase_in_progress = 1;
-		state->branch = read_and_strip_branch("rebase-merge/head-name");
-		state->onto = read_and_strip_branch("rebase-merge/onto");
+		state->branch = get_branch(wt, "rebase-merge/head-name");
+		state->onto = get_branch(wt, "rebase-merge/onto");
 	} else
 		return 0;
 	return 1;
@@ -1394,7 +1401,7 @@ void wt_status_get_state(struct wt_status_state *state,
 
 	if (!stat(git_path_merge_head(), &st)) {
 		state->merge_in_progress = 1;
-	} else if (wt_status_check_rebase(state)) {
+	} else if (wt_status_check_rebase(NULL, state)) {
 		/* all set */
 	} else if (!stat(git_path_cherry_pick_head(), &st) &&
 			!get_sha1("CHERRY_PICK_HEAD", sha1)) {
diff --git a/wt-status.h b/wt-status.h
index b398353..c4ddcad 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -6,6 +6,8 @@
 #include "color.h"
 #include "pathspec.h"
 
+struct worktree;
+
 enum color_wt_status {
 	WT_STATUS_HEADER = 0,
 	WT_STATUS_UPDATED,
@@ -100,7 +102,8 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
-int wt_status_check_rebase(struct wt_status_state *state);
+int wt_status_check_rebase(const struct worktree *wt,
+			   struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 08/12] worktree.c: avoid referencing to worktrees[i] multiple times
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (6 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 07/12] wt-status.c: make wt_status_check_rebase() work on any worktree Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree Nguyễn Thái Ngọc Duy
                           ` (5 subsequent siblings)
  13 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/worktree.c b/worktree.c
index 452f64a..b5ca78f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -221,10 +221,12 @@ const struct worktree *find_shared_symref(const char *symref,
 	worktrees = get_worktrees();
 
 	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
 		strbuf_addf(&path, "%s/%s",
-			    get_worktree_git_dir(worktrees[i]),
+			    get_worktree_git_dir(wt),
 			    symref);
 
 		if (parse_ref(path.buf, &sb, NULL)) {
@@ -232,7 +234,7 @@ const struct worktree *find_shared_symref(const char *symref,
 		}
 
 		if (!strcmp(sb.buf, target)) {
-			existing = worktrees[i];
+			existing = wt;
 			break;
 		}
 	}
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (7 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 08/12] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 18:04           ` Junio C Hamano
  2016-04-20 13:24         ` [PATCH v2 10/12] wt-status.c: split bisect detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
                           ` (4 subsequent siblings)
  13 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This function find_shared_symref() is used in a couple places:

1) in builtin/branch.c: it's used to detect if a branch is checked out
   elsewhere and refuse to delete the branch.

2) in builtin/notes.c: it's used to detect if a note is being merged in
   another worktree

3) in branch.c, the function die_if_checked_out() is actually used by
   "git checkout" and "git worktree add" to see if a branch is already
   checked out elsewhere and refuse the operation.

In cases 1 and 3, if a rebase is ongoing, "HEAD" will be in detached
mode, find_shared_symref() fails to detect it and declares "no branch is
checked out here", which is incorrect.

This patch tightens the test. If the given symref is "HEAD", we try to
detect if rebase is ongoing. If so return the branch being rebased. This
makes checkout and branch delete operations safer because you can't
checkout a branch being rebased in another place, or delete it.

Special case for checkout. If the current branch is being rebased,
git-rebase.sh may use "git checkout" to abort and return back to the
original branch. The updated test in find_shared_symref() will prevent
that and "git rebase --abort" will fail as a result.
find_shared_symref() and die_if_checked_out() have to learn a new
option ignore_current_worktree to loose the test a bit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c                |  4 ++--
 branch.h                |  2 +-
 builtin/branch.c        |  2 +-
 builtin/checkout.c      |  2 +-
 builtin/notes.c         |  2 +-
 builtin/worktree.c      |  4 ++--
 t/t2025-worktree-add.sh | 38 ++++++++++++++++++++++++++++++++++++++
 worktree.c              | 32 +++++++++++++++++++++++++++++++-
 worktree.h              |  3 ++-
 9 files changed, 79 insertions(+), 10 deletions(-)

diff --git a/branch.c b/branch.c
index a84fb2c..8e323d3 100644
--- a/branch.c
+++ b/branch.c
@@ -334,11 +334,11 @@ void remove_branch_state(void)
 	unlink(git_path_squash_msg());
 }
 
-void die_if_checked_out(const char *branch)
+void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
 	const struct worktree *wt;
 
-	wt = find_shared_symref("HEAD", branch);
+	wt = find_shared_symref("HEAD", branch, ignore_current_worktree);
 	if (wt) {
 		skip_prefix(branch, "refs/heads/", &branch);
 		die(_("'%s' is already checked out at '%s'"),
diff --git a/branch.h b/branch.h
index d69163d..b2f9649 100644
--- a/branch.h
+++ b/branch.h
@@ -58,7 +58,7 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
  * worktree and die (with a message describing its checkout location) if
  * it is.
  */
-extern void die_if_checked_out(const char *branch);
+extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
 /*
  * Update all per-worktree HEADs pointing at the old ref to point the new ref.
diff --git a/builtin/branch.c b/builtin/branch.c
index bcde87d..bf91bbd 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -221,7 +221,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 
 		if (kinds == FILTER_REFS_BRANCHES) {
 			const struct worktree *wt =
-				find_shared_symref("HEAD", name);
+				find_shared_symref("HEAD", name, 0);
 			if (wt) {
 				error(_("Cannot delete branch '%s' "
 					"checked out at '%s'"),
diff --git a/builtin/checkout.c b/builtin/checkout.c
index efcbd8f..6041718 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1111,7 +1111,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
 		if (head_ref &&
 		    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
-			die_if_checked_out(new->path);
+			die_if_checked_out(new->path, 1);
 		free(head_ref);
 	}
 
diff --git a/builtin/notes.c b/builtin/notes.c
index c65b59a..f154a69 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -852,7 +852,7 @@ static int merge(int argc, const char **argv, const char *prefix)
 		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
 		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-		wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+		wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref(), 0);
 		if (wt)
 			die(_("A notes merge into %s is already in-progress at %s"),
 			    default_notes_ref(), wt->path);
diff --git a/builtin/worktree.c b/builtin/worktree.c
index d8e3795..12c0af7 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -205,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
 	if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
 		 ref_exists(symref.buf)) { /* it's a branch */
 		if (!opts->force)
-			die_if_checked_out(symref.buf);
+			die_if_checked_out(symref.buf, 0);
 	} else { /* must be a commit */
 		commit = lookup_commit_reference_by_name(refname);
 		if (!commit)
@@ -349,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
 		if (!opts.force &&
 		    !strbuf_check_branch_ref(&symref, opts.new_branch) &&
 		    ref_exists(symref.buf))
-			die_if_checked_out(symref.buf);
+			die_if_checked_out(symref.buf, 0);
 		strbuf_release(&symref);
 	}
 
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 3acb992..da54327 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -4,6 +4,8 @@ test_description='test git worktree add'
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success 'setup' '
 	test_commit init
 '
@@ -225,4 +227,40 @@ test_expect_success '"add" worktree with --checkout' '
 	test_cmp init.t swamp2/init.t
 '
 
+test_expect_success 'put a worktree under rebase' '
+	git worktree add under-rebase &&
+	(
+		cd under-rebase &&
+		set_fake_editor &&
+		FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+		git worktree list | grep "under-rebase.*detached HEAD"
+	)
+'
+
+test_expect_success 'add a worktree, checking out a rebased branch' '
+	test_must_fail git worktree add new-rebase under-rebase &&
+	! test -d new-rebase
+'
+
+test_expect_success 'checking out a rebased branch from another worktree' '
+	git worktree add new-place &&
+	test_must_fail git -C new-place checkout under-rebase
+'
+
+test_expect_success 'not allow to delete a branch under rebase' '
+	(
+		cd under-rebase &&
+		test_must_fail git branch -D under-rebase
+	)
+'
+
+test_expect_success 'check out from current worktree branch ok' '
+	(
+		cd under-rebase &&
+		git checkout under-rebase &&
+		git checkout - &&
+		git rebase --abort
+	)
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index b5ca78f..dc380a2 100644
--- a/worktree.c
+++ b/worktree.c
@@ -3,6 +3,7 @@
 #include "strbuf.h"
 #include "worktree.h"
 #include "dir.h"
+#include "wt-status.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -207,8 +208,27 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+static int is_worktree_being_rebased(const struct worktree *wt,
+				     const char *target)
+{
+	struct wt_status_state state;
+	int found_rebase;
+
+	memset(&state, 0, sizeof(state));
+	found_rebase = wt_status_check_rebase(wt, &state) &&
+		((state.rebase_in_progress ||
+		  state.rebase_interactive_in_progress) &&
+		 state.branch &&
+		 starts_with(target, "refs/heads/") &&
+		 !strcmp(state.branch, target + strlen("refs/heads/")));
+	free(state.branch);
+	free(state.onto);
+	return found_rebase;
+}
+
 const struct worktree *find_shared_symref(const char *symref,
-					  const char *target)
+					  const char *target,
+					  int ignore_current_worktree)
 {
 	const struct worktree *existing = NULL;
 	struct strbuf path = STRBUF_INIT;
@@ -223,6 +243,16 @@ const struct worktree *find_shared_symref(const char *symref,
 	for (i = 0; worktrees[i]; i++) {
 		struct worktree *wt = worktrees[i];
 
+		if (ignore_current_worktree && wt->is_current)
+			continue;
+
+		if (wt->is_detached && !strcmp(symref, "HEAD")) {
+			if (is_worktree_being_rebased(wt, target)) {
+				existing = wt;
+				break;
+			}
+		}
+
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
 		strbuf_addf(&path, "%s/%s",
diff --git a/worktree.h b/worktree.h
index 9d2463e..fb9f5cc 100644
--- a/worktree.h
+++ b/worktree.h
@@ -41,7 +41,8 @@ extern void free_worktrees(struct worktree **);
  * may be destroyed by the next call.
  */
 extern const struct worktree *find_shared_symref(const char *symref,
-						 const char *target);
+						 const char *target,
+						 int ignore_current_worktree);
 
 /*
  * Similar to git_path() and git_pathdup() but can produce paths for a
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 10/12] wt-status.c: split bisect detection out of wt_status_get_state()
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (8 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 11/12] worktree.c: test if branch being bisected in another worktree Nguyễn Thái Ngọc Duy
                           ` (3 subsequent siblings)
  13 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

And make it work with any given worktree, in preparation for (again)
find_shared_symref(). read_and_strip_branch() is deleted because it's
no longer used.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wt-status.c | 23 ++++++++++++++---------
 wt-status.h |  2 ++
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index 2295682..36c85f8 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1296,11 +1296,6 @@ got_nothing:
 	return NULL;
 }
 
-static char *read_and_strip_branch(const char *path)
-{
-	return get_branch(NULL, path);
-}
-
 struct grab_1st_switch_cbdata {
 	struct strbuf buf;
 	unsigned char nsha1[20];
@@ -1393,6 +1388,19 @@ int wt_status_check_rebase(const struct worktree *wt,
 	return 1;
 }
 
+int wt_status_check_bisect(const struct worktree *wt,
+			   struct wt_status_state *state)
+{
+	struct stat st;
+
+	if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
+		state->bisect_in_progress = 1;
+		state->branch = get_branch(wt, "BISECT_START");
+		return 1;
+	}
+	return 0;
+}
+
 void wt_status_get_state(struct wt_status_state *state,
 			 int get_detached_from)
 {
@@ -1408,10 +1416,7 @@ void wt_status_get_state(struct wt_status_state *state,
 		state->cherry_pick_in_progress = 1;
 		hashcpy(state->cherry_pick_head_sha1, sha1);
 	}
-	if (!stat(git_path("BISECT_LOG"), &st)) {
-		state->bisect_in_progress = 1;
-		state->branch = read_and_strip_branch("BISECT_START");
-	}
+	wt_status_check_bisect(NULL, state);
 	if (!stat(git_path_revert_head(), &st) &&
 	    !get_sha1("REVERT_HEAD", sha1)) {
 		state->revert_in_progress = 1;
diff --git a/wt-status.h b/wt-status.h
index c4ddcad..2ca93f6 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -104,6 +104,8 @@ void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
 int wt_status_check_rebase(const struct worktree *wt,
 			   struct wt_status_state *state);
+int wt_status_check_bisect(const struct worktree *wt,
+			   struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 11/12] worktree.c: test if branch being bisected in another worktree
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (9 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 10/12] wt-status.c: split bisect detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 13:24         ` [PATCH v2 12/12] branch: do not rename a branch under bisect or rebase Nguyễn Thái Ngọc Duy
                           ` (2 subsequent siblings)
  13 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Similar to the rebase case, we want to detect if "HEAD" in some worktree
is being bisected because

1) we do not want to checkout this branch in another worktree, after
   bisect is done it will want to go back to this branch

2) we do not want to delete the branch is either or git bisect will
   fail to return to the (long gone) branch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t2025-worktree-add.sh | 13 +++++++++++++
 worktree.c              | 19 +++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index da54327..8f53944 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -263,4 +263,17 @@ test_expect_success 'check out from current worktree branch ok' '
 	)
 '
 
+test_expect_success 'checkout a branch under bisect' '
+	git worktree add under-bisect &&
+	(
+		cd under-bisect &&
+		git bisect start &&
+		git bisect bad &&
+		git bisect good HEAD~2 &&
+		git worktree list | grep "under-bisect.*detached HEAD" &&
+		test_must_fail git worktree add new-bisect under-bisect &&
+		! test -d new-bisect
+	)
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index dc380a2..7b66071 100644
--- a/worktree.c
+++ b/worktree.c
@@ -226,6 +226,21 @@ static int is_worktree_being_rebased(const struct worktree *wt,
 	return found_rebase;
 }
 
+static int is_worktree_being_bisected(const struct worktree *wt,
+				      const char *target)
+{
+	struct wt_status_state state;
+	int found_rebase;
+
+	memset(&state, 0, sizeof(state));
+	found_rebase = wt_status_check_bisect(wt, &state) &&
+		state.branch &&
+		starts_with(target, "refs/heads/") &&
+		!strcmp(state.branch, target + strlen("refs/heads/"));
+	free(state.branch);
+	return found_rebase;
+}
+
 const struct worktree *find_shared_symref(const char *symref,
 					  const char *target,
 					  int ignore_current_worktree)
@@ -251,6 +266,10 @@ const struct worktree *find_shared_symref(const char *symref,
 				existing = wt;
 				break;
 			}
+			if (is_worktree_being_bisected(wt, target)) {
+				existing = wt;
+				break;
+			}
 		}
 
 		strbuf_reset(&path);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 12/12] branch: do not rename a branch under bisect or rebase
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (10 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 11/12] worktree.c: test if branch being bisected in another worktree Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:24         ` Nguyễn Thái Ngọc Duy
  2016-04-20 18:09         ` [PATCH v2 00/12] fix checking out a being-rebased branch Junio C Hamano
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
  13 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-20 13:24 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

The branch name in that case could be saved in rebase's head_name or
bisect's BISECT_START files. Ideally we should try to update them as
well. But it's trickier (*). Let's play safe and see if the user
complains about inconveniences before doing that.

(*) If we do it, bisect and rebase need to provide an API to rename
branches. We can't do it in worktree.c or builtin/branch.c because
when other people change rebase/bisect code, they may not be aware of
this code and accidentally break it (e.g. rename the branch file, or
refer to the branch in new files). It's a lot more work.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/branch.c        | 25 +++++++++++++++++++++++++
 t/t2025-worktree-add.sh |  8 ++++++++
 worktree.c              |  8 ++++----
 worktree.h              |  3 +++
 4 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index bf91bbd..3a2eceb 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -524,6 +524,29 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 	ref_array_clear(&array);
 }
 
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+	struct worktree **worktrees = get_worktrees();
+	int i;
+
+	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+
+		if (!wt->is_detached)
+			continue;
+
+		if (is_worktree_being_rebased(wt, target))
+			die(_("Branch %s is being rebased at %s"),
+			    target, wt->path);
+
+		if (is_worktree_being_bisected(wt, target))
+			die(_("Branch %s is being bisected at %s"),
+			    target, wt->path);
+	}
+
+	free_worktrees(worktrees);
+}
+
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
 	struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
@@ -553,6 +576,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 
 	validate_new_branchname(newname, &newref, force, clobber_head_ok);
 
+	reject_rebase_or_bisect_branch(oldref.buf);
+
 	strbuf_addf(&logmsg, "Branch: renamed %s to %s",
 		 oldref.buf, newref.buf);
 
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 8f53944..3a22fc5 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -254,6 +254,10 @@ test_expect_success 'not allow to delete a branch under rebase' '
 	)
 '
 
+test_expect_success 'rename a branch under rebase not allowed' '
+	test_must_fail git branch -M under-rebase rebase-with-new-name
+'
+
 test_expect_success 'check out from current worktree branch ok' '
 	(
 		cd under-rebase &&
@@ -276,4 +280,8 @@ test_expect_success 'checkout a branch under bisect' '
 	)
 '
 
+test_expect_success 'rename a branch under bisect not allowed' '
+	test_must_fail git branch -M under-bisect bisect-with-new-name
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index 7b66071..8a3d394 100644
--- a/worktree.c
+++ b/worktree.c
@@ -208,8 +208,8 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
-static int is_worktree_being_rebased(const struct worktree *wt,
-				     const char *target)
+int is_worktree_being_rebased(const struct worktree *wt,
+			      const char *target)
 {
 	struct wt_status_state state;
 	int found_rebase;
@@ -226,8 +226,8 @@ static int is_worktree_being_rebased(const struct worktree *wt,
 	return found_rebase;
 }
 
-static int is_worktree_being_bisected(const struct worktree *wt,
-				      const char *target)
+int is_worktree_being_bisected(const struct worktree *wt,
+			       const char *target)
 {
 	struct wt_status_state state;
 	int found_rebase;
diff --git a/worktree.h b/worktree.h
index fb9f5cc..d4a3534 100644
--- a/worktree.h
+++ b/worktree.h
@@ -44,6 +44,9 @@ extern const struct worktree *find_shared_symref(const char *symref,
 						 const char *target,
 						 int ignore_current_worktree);
 
+int is_worktree_being_rebased(const struct worktree *wt, const char *target);
+int is_worktree_being_bisected(const struct worktree *wt, const char *target);
+
 /*
  * Similar to git_path() and git_pathdup() but can produce paths for a
  * specified worktree instead of current one
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 06/12] wt-status.c: split rebase detection out of wt_status_get_state()
  2016-04-20 13:24         ` [PATCH v2 06/12] wt-status.c: split rebase detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
@ 2016-04-20 13:48           ` Ramsay Jones
  2016-04-20 13:54             ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Ramsay Jones @ 2016-04-20 13:48 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy, git
  Cc: Junio C Hamano, rethab.ch, rappazzo



On 20/04/16 14:24, Nguyễn Thái Ngọc Duy wrote:
> worktree.c:find_shared_symref() later needs to know if a branch is being
> rebased, and only rebased only. Split this code so it can be used
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Err ... what?

ATB,
Ramsay Jones

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 06/12] wt-status.c: split rebase detection out of wt_status_get_state()
  2016-04-20 13:48           ` Ramsay Jones
@ 2016-04-20 13:54             ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-04-20 13:54 UTC (permalink / raw)
  To: Ramsay Jones
  Cc: Git Mailing List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Wed, Apr 20, 2016 at 8:48 PM, Ramsay Jones
<ramsay@ramsayjones.plus.com> wrote:
>
>
> On 20/04/16 14:24, Nguyễn Thái Ngọc Duy wrote:
>> worktree.c:find_shared_symref() later needs to know if a branch is being
>> rebased, and only rebased only. Split this code so it can be used
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> Err ... what?

wt_status_get_state() detects more than rebase, it does bisect,
cherry-pick, detached head as well. I "only" too much there though.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree *
  2016-04-20 13:24         ` [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
@ 2016-04-20 17:05           ` Junio C Hamano
  2016-04-21  7:02           ` Eric Sunshine
  1 sibling, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-04-20 17:05 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> This gives the caller more information and they can answer things like,
> "is it the main worktree" or "is it the current worktree". The latter
> question is needed for the "checkout a rebase branch" case later.

That makes good sense.

> diff --git a/worktree.h b/worktree.h
> index 3198c8d..d71d7ec 100644
> --- a/worktree.h
> +++ b/worktree.h
> @@ -36,9 +36,10 @@ extern void free_worktrees(struct worktree **);
>  /*
>   * Check if a per-worktree symref points to a ref in the main worktree
>   * or any linked worktree, and return the path to the exising worktree
> - * if it is.  Returns NULL if there is no existing ref.  The caller is
> - * responsible for freeing the returned path.
> + * if it is. Returns NULL if there is no existing ref. The result
> + * may be destroyed by the next call.
>   */

To return and keep alive one worktree[] instance (i.e. "existing"),
the code holds onto the entire return value from get_worktrees(), if
I am not misreading it.  Typically you would have only a handful of
worktrees so this may not be an issue, though.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree
  2016-04-20 13:24         ` [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree Nguyễn Thái Ngọc Duy
@ 2016-04-20 18:04           ` Junio C Hamano
  2016-04-21  0:37             ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-04-20 18:04 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> Subject: Re: [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree

Lacks the verb?  Perhaps s/being/is/ is sufficient.

> This function find_shared_symref() is used in a couple places:
>
> 1) in builtin/branch.c: it's used to detect if a branch is checked out
>    elsewhere and refuse to delete the branch.
>
> 2) in builtin/notes.c: it's used to detect if a note is being merged in
>    another worktree
>
> 3) in branch.c, the function die_if_checked_out() is actually used by
>    "git checkout" and "git worktree add" to see if a branch is already
>    checked out elsewhere and refuse the operation.
>
> In cases 1 and 3, if a rebase is ongoing, "HEAD" will be in detached
> mode, find_shared_symref() fails to detect it and declares "no branch is
> checked out here", which is incorrect.

which is technically correct but is not what we want to check.

> This patch tightens the test. If the given symref is "HEAD", we try to
> detect if rebase is ongoing. If so return the branch being rebased. This
> makes checkout and branch delete operations safer because you can't
> checkout a branch being rebased in another place, or delete it.

Is rebase the only thing that tentatively detach before working on
the real branch?  It may be currently the case, but I would imagine
that we want to makefind-shared-symref to be responsible for
detecting new cases other than rebase that we may introduce in the
future, in which case we may want to leave come comment near the
function to describe that expectation.

> Special case for checkout. If the current branch is being rebased,
> git-rebase.sh may use "git checkout" to abort and return back to the
> original branch. The updated test in find_shared_symref() will prevent
> that and "git rebase --abort" will fail as a result.
> find_shared_symref() and die_if_checked_out() have to learn a new
> option ignore_current_worktree to loose the test a bit.

s/loose/&n/

> +void die_if_checked_out(const char *branch, int ignore_current_worktree)
>  {
>  	const struct worktree *wt;
>  
> -	wt = find_shared_symref("HEAD", branch);
> +	wt = find_shared_symref("HEAD", branch, ignore_current_worktree);
>  	if (wt) {
>  		skip_prefix(branch, "refs/heads/", &branch);
>  		die(_("'%s' is already checked out at '%s'"),
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index efcbd8f..6041718 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -1111,7 +1111,7 @@ static int checkout_branch(struct checkout_opts *opts,
>  		char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
>  		if (head_ref &&
>  		    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
> -			die_if_checked_out(new->path);
> +			die_if_checked_out(new->path, 1);
>  		free(head_ref);
>  	}

So the idea is "if the branch is checked out (or "being worked on"
even if technically the HEAD is detached, like with 'rebase')
anywhere, callers of die-if-checked-out in general want to die; but
for this caller, it is OK if the place the branch is checked out or
being worked on is in this repository"?

I understand die_if_checked_out() taking that "ignore this one" bit
may be sensible, but I do not understand why find_shared_symref()
needs to be told about it.  The change makes the meaning of the
find_shared_symref() function unclear.  It used to mean "This
symbolic ref cannot point at the same ref in different worktrees, so
for a given pair of a symbolic ref and a concrete ref, there can be
at most one worktree in which the symbolic ref points at that ref".
That is already a mouthful.  As the worktree structure already have
"Am I the current worktree?" bit, "ignore" logic can easily be done
inside die_if_checked_out() and that would help find_shared_symref()
stay simpler and more focused function, no?

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 00/12] fix checking out a being-rebased branch
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (11 preceding siblings ...)
  2016-04-20 13:24         ` [PATCH v2 12/12] branch: do not rename a branch under bisect or rebase Nguyễn Thái Ngọc Duy
@ 2016-04-20 18:09         ` Junio C Hamano
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
  13 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-04-20 18:09 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> Much happier with this version. This makes
>
>  - git checkout refuse if a branch is under rebase or bisect
>    elsewhere
>
>  - git worktree add refuse if a branch is under rebase or bisect
>
>  - git branch -D refuse if a branch is under rebase or bisect.
>    This applies for single worktree case as well.
>
>  - git branch -M refuse if a branch is under rebase or bisect
>    (single worktree case as well)

I agree that this reads much nicer than v1, especially with the
abstraction around find_shared_symref() that returns the actual
worktree.

>   [01/12] path.c: add git_common_path() and strbuf_git_common_path()
>   [02/12] worktree.c: store "id" instead of "git_dir"
>   [03/12] worktree.c: make find_shared_symref() return struct worktree *
>   [04/12] worktree.c: mark current worktree
>   [05/12] path.c: refactor and add worktree_git_path()
>   [06/12] wt-status.c: split rebase detection out of wt_status_get_state()
>   [07/12] wt-status.c: make wt_status_check_rebase() work on any worktree
>   [08/12] worktree.c: avoid referencing to worktrees[i] multiple times
>   [09/12] worktree.c: test if branch being rebased in another worktree
>   [10/12] wt-status.c: split bisect detection out of wt_status_get_state()
>   [11/12] worktree.c: test if branch being bisected in another worktree
>   [12/12] branch: do not rename a branch under bisect or rebase
>
> Total 13 files changed, 335 insertions(+), 67 deletions(-)

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path()
  2016-04-20 13:24         ` [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
@ 2016-04-20 18:11           ` Eric Sunshine
  2016-04-21  0:28             ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-04-20 18:11 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, rethab.ch, Mike Rappazzo

On Wed, Apr 20, 2016 at 9:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> diff --git a/path.c b/path.c
> @@ -503,6 +503,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
> +const char *git_common_path(const char *fmt, ...)
> +{
> +       struct strbuf *pathname = get_pathname();
> +       va_list args;
> +       va_start(args, fmt);
> +       do_git_common_path(pathname, fmt, args);
> +       va_end(args);
> +       return pathname->buf;

Is the caller expected to free this value? If not, then shouldn't
'pathname' be static? If so, then perhaps strbuf_detach() would be
clearer (and return 'char *' rather than 'const char *').

> +}

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path()
  2016-04-20 18:11           ` Eric Sunshine
@ 2016-04-21  0:28             ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-04-21  0:28 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Thu, Apr 21, 2016 at 1:11 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Wed, Apr 20, 2016 at 9:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> diff --git a/path.c b/path.c
>> @@ -503,6 +503,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
>> +const char *git_common_path(const char *fmt, ...)
>> +{
>> +       struct strbuf *pathname = get_pathname();
>> +       va_list args;
>> +       va_start(args, fmt);
>> +       do_git_common_path(pathname, fmt, args);
>> +       va_end(args);
>> +       return pathname->buf;
>
> Is the caller expected to free this value? If not, then shouldn't
> 'pathname' be static? If so, then perhaps strbuf_detach() would be
> clearer (and return 'char *' rather than 'const char *').

get_pathname() actually holds a ring of static buffer. So no, we don't
need static pathname, it can be a new buffer next time, mostly to to
make printf("%s %s", git_common_path(..), git_common_path(..)) work.
And no the caller is not supposed to free it, a little bit more
convenient.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree
  2016-04-20 18:04           ` Junio C Hamano
@ 2016-04-21  0:37             ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-04-21  0:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Reto Hablützel, Mike Rappazzo

On Thu, Apr 21, 2016 at 1:04 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> diff --git a/builtin/checkout.c b/builtin/checkout.c
>> index efcbd8f..6041718 100644
>> --- a/builtin/checkout.c
>> +++ b/builtin/checkout.c
>> @@ -1111,7 +1111,7 @@ static int checkout_branch(struct checkout_opts *opts,
>>               char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
>>               if (head_ref &&
>>                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
>> -                     die_if_checked_out(new->path);
>> +                     die_if_checked_out(new->path, 1);
>>               free(head_ref);
>>       }
>
> So the idea is "if the branch is checked out (or "being worked on"
> even if technically the HEAD is detached, like with 'rebase')
> anywhere, callers of die-if-checked-out in general want to die; but
> for this caller, it is OK if the place the branch is checked out or
> being worked on is in this repository"?
>
> I understand die_if_checked_out() taking that "ignore this one" bit
> may be sensible, but I do not understand why find_shared_symref()
> needs to be told about it.  The change makes the meaning of the
> find_shared_symref() function unclear.  It used to mean "This
> symbolic ref cannot point at the same ref in different worktrees, so
> for a given pair of a symbolic ref and a concrete ref, there can be
> at most one worktree in which the symbolic ref points at that ref".
> That is already a mouthful.  As the worktree structure already have
> "Am I the current worktree?" bit, "ignore" logic can easily be done
> inside die_if_checked_out() and that would help find_shared_symref()
> stay simpler and more focused function, no?

That was the intention when I made find_shared_symref() return struct
worktree * instead of char *. I forget why I changed my mind and not
do so. The only case when find_shared_symref should do this is when a
ref is shared twice, then we need to ignore current worktree from
inside the loop. But that can't happen. Will move this
ignore-current-worktree test to die_if_checked_out().
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree *
  2016-04-20 13:24         ` [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
  2016-04-20 17:05           ` Junio C Hamano
@ 2016-04-21  7:02           ` Eric Sunshine
  1 sibling, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-04-21  7:02 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, rethab.ch, Mike Rappazzo

On Wed, Apr 20, 2016 at 9:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> This gives the caller more information and they can answer things like,
> "is it the main worktree" or "is it the current worktree". The latter
> question is needed for the "checkout a rebase branch" case later.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.h b/worktree.h
> @@ -36,9 +36,10 @@ extern void free_worktrees(struct worktree **);
>  /*
>   * Check if a per-worktree symref points to a ref in the main worktree
>   * or any linked worktree, and return the path to the exising worktree

Doesn't "return the path" become outdated with this patch?

Also (not a new problem): s/exising/existing/

> - * if it is.  Returns NULL if there is no existing ref.  The caller is
> - * responsible for freeing the returned path.
> + * if it is. Returns NULL if there is no existing ref. The result
> + * may be destroyed by the next call.
>   */
> -extern char *find_shared_symref(const char *symref, const char *target);
> +extern const struct worktree *find_shared_symref(const char *symref,
> +                                                const char *target);

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-20 13:24         ` [PATCH v2 04/12] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
@ 2016-04-21  7:20           ` Eric Sunshine
  2016-04-21  8:19             ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-04-21  7:20 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Wed, Apr 20, 2016 at 9:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -178,6 +182,18 @@ struct worktree **get_worktrees(void)
>         }
>         ALLOC_GROW(list, counter + 1, alloc);
>         list[counter] = NULL;
> +
> +       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
> +       for (i = 0; i < counter; i++) {
> +               struct worktree *wt = list[i];
> +               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
> +               wt->is_current = !strcmp_icase(git_dir.buf, path.buf);

Can you talk a bit about why this uses 'icase'? Should it be
respecting cache.h:ignore_case?

> +               strbuf_reset(&path);
> +               if (wt->is_current)
> +                       break;
> +       }
> +       strbuf_release(&git_dir);
> +       strbuf_release(&path);

Minor: Would it make sense to place this new code in its own function
-- say, mark_current_worktree() -- to keep get_worktrees() from
becoming overlong?

>         return list;
>  }

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-21  7:20           ` Eric Sunshine
@ 2016-04-21  8:19             ` Duy Nguyen
  2016-04-21  9:33               ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-04-21  8:19 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Thu, Apr 21, 2016 at 2:20 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Wed, Apr 20, 2016 at 9:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>> diff --git a/worktree.c b/worktree.c
>> @@ -178,6 +182,18 @@ struct worktree **get_worktrees(void)
>>         }
>>         ALLOC_GROW(list, counter + 1, alloc);
>>         list[counter] = NULL;
>> +
>> +       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
>> +       for (i = 0; i < counter; i++) {
>> +               struct worktree *wt = list[i];
>> +               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
>> +               wt->is_current = !strcmp_icase(git_dir.buf, path.buf);
>
> Can you talk a bit about why this uses 'icase'? Should it be
> respecting cache.h:ignore_case?

It does.That function (in dir.c) is just one-liner

    return ignore_case ? strcasecmp(a, b) : strcmp(a, b);

I admit though, the naming does not make that clear.

>> +               strbuf_reset(&path);
>> +               if (wt->is_current)
>> +                       break;
>> +       }
>> +       strbuf_release(&git_dir);
>> +       strbuf_release(&path);
>
> Minor: Would it make sense to place this new code in its own function
> -- say, mark_current_worktree() -- to keep get_worktrees() from
> becoming overlong?

Good idea. Will do.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-21  8:19             ` Duy Nguyen
@ 2016-04-21  9:33               ` Duy Nguyen
  2016-04-21 14:23                 ` Eric Sunshine
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-04-21  9:33 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Thu, Apr 21, 2016 at 3:19 PM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Thu, Apr 21, 2016 at 2:20 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>> On Wed, Apr 20, 2016 at 9:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>>> ---
>>> diff --git a/worktree.c b/worktree.c
>>> @@ -178,6 +182,18 @@ struct worktree **get_worktrees(void)
>>>         }
>>>         ALLOC_GROW(list, counter + 1, alloc);
>>>         list[counter] = NULL;
>>> +
>>> +       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
>>> +       for (i = 0; i < counter; i++) {
>>> +               struct worktree *wt = list[i];
>>> +               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
>>> +               wt->is_current = !strcmp_icase(git_dir.buf, path.buf);
>>
>> Can you talk a bit about why this uses 'icase'? Should it be
>> respecting cache.h:ignore_case?
>
> It does.That function (in dir.c) is just one-liner
>
>     return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
>
> I admit though, the naming does not make that clear.

While we're at it, how about renaming it to pathcmp (and its friend
strncmp_icase to pathncmp)?
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-21  9:33               ` Duy Nguyen
@ 2016-04-21 14:23                 ` Eric Sunshine
  2016-04-21 15:13                   ` Jeff King
  0 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-04-21 14:23 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Thu, Apr 21, 2016 at 5:33 AM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Thu, Apr 21, 2016 at 3:19 PM, Duy Nguyen <pclouds@gmail.com> wrote:
>> On Thu, Apr 21, 2016 at 2:20 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>>> On Wed, Apr 20, 2016 at 9:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>>>> +       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
>>>> +       for (i = 0; i < counter; i++) {
>>>> +               struct worktree *wt = list[i];
>>>> +               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
>>>> +               wt->is_current = !strcmp_icase(git_dir.buf, path.buf);
>>>
>>> Can you talk a bit about why this uses 'icase'? Should it be
>>> respecting cache.h:ignore_case?
>>
>> It does.That function (in dir.c) is just one-liner
>>
>>     return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
>>
>> I admit though, the naming does not make that clear.

Ugh, this is only the fourth patch, yet the second stupid review
mistake I've made thus far in this series. For some reason, I kept
reading this as a call to strcasecmp() or stricmp() rather than
strcmp_icase(). Worse, I had even consulted path.c:strcmp_icase() to
see how the issue was handled there.

> While we're at it, how about renaming it to pathcmp (and its friend
> strncmp_icase to pathncmp)?

Yes, that seems like a good idea. For anyone familiar with
strcasecmp() or stricmp(), having "icase" in the name makes it seem as
though it's unconditionally case-insensitive, so dropping it from the
name would likely be beneficial.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-21 14:23                 ` Eric Sunshine
@ 2016-04-21 15:13                   ` Jeff King
  2016-04-21 15:37                     ` Junio C Hamano
  0 siblings, 1 reply; 162+ messages in thread
From: Jeff King @ 2016-04-21 15:13 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Duy Nguyen, Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Thu, Apr 21, 2016 at 10:23:09AM -0400, Eric Sunshine wrote:

> > While we're at it, how about renaming it to pathcmp (and its friend
> > strncmp_icase to pathncmp)?
> 
> Yes, that seems like a good idea. For anyone familiar with
> strcasecmp() or stricmp(), having "icase" in the name makes it seem as
> though it's unconditionally case-insensitive, so dropping it from the
> name would likely be beneficial.

Seconded (thirded?). I have been caught by this confusion in the past,
too.

-Peff

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-21 15:13                   ` Jeff King
@ 2016-04-21 15:37                     ` Junio C Hamano
  2016-04-21 15:40                       ` Jeff King
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-04-21 15:37 UTC (permalink / raw)
  To: Jeff King
  Cc: Eric Sunshine, Duy Nguyen, Git List, Reto Hablützel, Mike Rappazzo

Jeff King <peff@peff.net> writes:

> On Thu, Apr 21, 2016 at 10:23:09AM -0400, Eric Sunshine wrote:
>
>> > While we're at it, how about renaming it to pathcmp (and its friend
>> > strncmp_icase to pathncmp)?
>> 
>> Yes, that seems like a good idea. For anyone familiar with
>> strcasecmp() or stricmp(), having "icase" in the name makes it seem as
>> though it's unconditionally case-insensitive, so dropping it from the
>> name would likely be beneficial.
>
> Seconded (thirded?). I have been caught by this confusion in the past,
> too.

I agree that strcmp_icase() gives a false impression that it always
ignores case differences, but a new name that does not at all hint
that it may do icase comparison as necessary will catch me by an
opposite confusion in the future.

I have not yet formed a firm opinion if pathcmp() conveys enough
hint.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-21 15:37                     ` Junio C Hamano
@ 2016-04-21 15:40                       ` Jeff King
  2016-04-21 15:42                         ` Junio C Hamano
  0 siblings, 1 reply; 162+ messages in thread
From: Jeff King @ 2016-04-21 15:40 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Eric Sunshine, Duy Nguyen, Git List, Reto Hablützel, Mike Rappazzo

On Thu, Apr 21, 2016 at 08:37:32AM -0700, Junio C Hamano wrote:

> >> > While we're at it, how about renaming it to pathcmp (and its friend
> >> > strncmp_icase to pathncmp)?
> >> 
> >> Yes, that seems like a good idea. For anyone familiar with
> >> strcasecmp() or stricmp(), having "icase" in the name makes it seem as
> >> though it's unconditionally case-insensitive, so dropping it from the
> >> name would likely be beneficial.
> >
> > Seconded (thirded?). I have been caught by this confusion in the past,
> > too.
> 
> I agree that strcmp_icase() gives a false impression that it always
> ignores case differences, but a new name that does not at all hint
> that it may do icase comparison as necessary will catch me by an
> opposite confusion in the future.

To me, the benefit is that you don't have to care about ignore_case. You
have asked to compare two paths, and any system-appropriate magic should
be applied. That may be icase, or it may be weird unicode normalization.

I think the key thing missing is that this is only about _filesystem_
paths. You would not want to use it for tree-to-tree pathname
comparisons. So maybe "fspath" or something would be more descriptive.

-Peff

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 04/12] worktree.c: mark current worktree
  2016-04-21 15:40                       ` Jeff King
@ 2016-04-21 15:42                         ` Junio C Hamano
  0 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-04-21 15:42 UTC (permalink / raw)
  To: Jeff King
  Cc: Eric Sunshine, Duy Nguyen, Git List, Reto Hablützel, Mike Rappazzo

Jeff King <peff@peff.net> writes:

> To me, the benefit is that you don't have to care about ignore_case. You
> have asked to compare two paths, and any system-appropriate magic should
> be applied. That may be icase, or it may be weird unicode normalization.
>
> I think the key thing missing is that this is only about _filesystem_
> paths. You would not want to use it for tree-to-tree pathname
> comparisons. So maybe "fspath" or something would be more descriptive.

Yup, I would be very happy with "fs" in the name.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v3 00/13] nd/worktree-various-heads
  2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
                           ` (12 preceding siblings ...)
  2016-04-20 18:09         ` [PATCH v2 00/12] fix checking out a being-rebased branch Junio C Hamano
@ 2016-04-22 13:01         ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 01/13] dir.c: rename str(n)cmp_icase to fspath(n)cmp Nguyễn Thái Ngọc Duy
                             ` (12 more replies)
  13 siblings, 13 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

v3 fixes all the things found in v2, deletes unused stuff and adds a
new patch 01/13 that renames str(n)cmp_icase to fspath(n)cmp.

Interdiff

diff --git a/branch.c b/branch.c
index 8e323d3..a5a8dcb 100644
--- a/branch.c
+++ b/branch.c
@@ -338,12 +338,12 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
 	const struct worktree *wt;
 
-	wt = find_shared_symref("HEAD", branch, ignore_current_worktree);
-	if (wt) {
-		skip_prefix(branch, "refs/heads/", &branch);
-		die(_("'%s' is already checked out at '%s'"),
-		    branch, wt->path);
-	}
+	wt = find_shared_symref("HEAD", branch);
+	if (!wt || (ignore_current_worktree && wt->is_current))
+		return;
+	skip_prefix(branch, "refs/heads/", &branch);
+	die(_("'%s' is already checked out at '%s'"),
+	    branch, wt->path);
 }
 
 int replace_each_worktree_head_symref(const char *oldref, const char *newref)
diff --git a/builtin/branch.c b/builtin/branch.c
index 3a2eceb..b488c3f 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -221,7 +221,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 
 		if (kinds == FILTER_REFS_BRANCHES) {
 			const struct worktree *wt =
-				find_shared_symref("HEAD", name, 0);
+				find_shared_symref("HEAD", name);
 			if (wt) {
 				error(_("Cannot delete branch '%s' "
 					"checked out at '%s'"),
diff --git a/builtin/notes.c b/builtin/notes.c
index f154a69..c65b59a 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -852,7 +852,7 @@ static int merge(int argc, const char **argv, const char *prefix)
 		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
 		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-		wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref(), 0);
+		wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
 		if (wt)
 			die(_("A notes merge into %s is already in-progress at %s"),
 			    default_notes_ref(), wt->path);
diff --git a/dir.c b/dir.c
index 996653b..f04bd3b 100644
--- a/dir.c
+++ b/dir.c
@@ -53,13 +53,12 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 	int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
-/* helper string functions with support for the ignore_case flag */
-int strcmp_icase(const char *a, const char *b)
+int fspathcmp(const char *a, const char *b)
 {
 	return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
 }
 
-int strncmp_icase(const char *a, const char *b, size_t count)
+int fspathncmp(const char *a, const char *b, size_t count)
 {
 	return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
 }
@@ -802,12 +801,12 @@ int match_basename(const char *basename, int basenamelen,
 {
 	if (prefix == patternlen) {
 		if (patternlen == basenamelen &&
-		    !strncmp_icase(pattern, basename, basenamelen))
+		    !fspathncmp(pattern, basename, basenamelen))
 			return 1;
 	} else if (flags & EXC_FLAG_ENDSWITH) {
 		/* "*literal" matching against "fooliteral" */
 		if (patternlen - 1 <= basenamelen &&
-		    !strncmp_icase(pattern + 1,
+		    !fspathncmp(pattern + 1,
 				   basename + basenamelen - (patternlen - 1),
 				   patternlen - 1))
 			return 1;
@@ -844,7 +843,7 @@ int match_pathname(const char *pathname, int pathlen,
 	 */
 	if (pathlen < baselen + 1 ||
 	    (baselen && pathname[baselen] != '/') ||
-	    strncmp_icase(pathname, base, baselen))
+	    fspathncmp(pathname, base, baselen))
 		return 0;
 
 	namelen = baselen ? pathlen - baselen - 1 : pathlen;
@@ -858,7 +857,7 @@ int match_pathname(const char *pathname, int pathlen,
 		if (prefix > namelen)
 			return 0;
 
-		if (strncmp_icase(pattern, name, prefix))
+		if (fspathncmp(pattern, name, prefix))
 			return 0;
 		pattern += prefix;
 		patternlen -= prefix;
diff --git a/dir.h b/dir.h
index 301b737..e34d555 100644
--- a/dir.h
+++ b/dir.h
@@ -270,8 +270,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
 /* tries to remove the path with empty directories along it, ignores ENOENT */
 extern int remove_path(const char *path);
 
-extern int strcmp_icase(const char *a, const char *b);
-extern int strncmp_icase(const char *a, const char *b, size_t count);
+extern int fspathcmp(const char *a, const char *b);
+extern int fspathncmp(const char *a, const char *b, size_t count);
 extern int fnmatch_icase(const char *pattern, const char *string, int flags);
 
 /*
diff --git a/fast-import.c b/fast-import.c
index 9fc7093..339cd38 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1512,7 +1512,7 @@ static int tree_content_set(
 	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
 			if (!*slash1) {
 				if (!S_ISDIR(mode)
 						&& e->versions[1].mode == mode
@@ -1602,7 +1602,7 @@ static int tree_content_remove(
 	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
 			if (*slash1 && !S_ISDIR(e->versions[1].mode))
 				/*
 				 * If p names a file in some subdirectory, and a
@@ -1669,7 +1669,7 @@ static int tree_content_get(
 	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
 			if (!*slash1)
 				goto found_entry;
 			if (!S_ISDIR(e->versions[1].mode))
diff --git a/path.c b/path.c
index c421d37..8fdd187 100644
--- a/path.c
+++ b/path.c
@@ -466,16 +466,6 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
 	return pathname->buf;
 }
 
-char *worktree_git_pathdup(const struct worktree *wt, const char *fmt, ...)
-{
-	struct strbuf path = STRBUF_INIT;
-	va_list args;
-	va_start(args, fmt);
-	do_git_path(wt, &path, fmt, args);
-	va_end(args);
-	return strbuf_detach(&path, NULL);
-}
-
 static void do_submodule_path(struct strbuf *buf, const char *path,
 			      const char *fmt, va_list args)
 {
diff --git a/sha1_file.c b/sha1_file.c
index d0f2aa0..ea6381b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -301,7 +301,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
 			return -1;
 		}
 	}
-	if (!strcmp_icase(ent->base, normalized_objdir)) {
+	if (!fspathcmp(ent->base, normalized_objdir)) {
 		free(ent);
 		return -1;
 	}
diff --git a/worktree.c b/worktree.c
index 8a3d394..4817d60 100644
--- a/worktree.c
+++ b/worktree.c
@@ -151,14 +151,32 @@ done:
 	return worktree;
 }
 
+static void mark_current_worktree(struct worktree **worktrees)
+{
+	struct strbuf git_dir = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	int i;
+
+	strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
+	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+		strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
+		wt->is_current = !fspathcmp(git_dir.buf, path.buf);
+		strbuf_reset(&path);
+		if (wt->is_current)
+			break;
+	}
+	strbuf_release(&git_dir);
+	strbuf_release(&path);
+}
+
 struct worktree **get_worktrees(void)
 {
 	struct worktree **list = NULL;
-	struct strbuf git_dir = STRBUF_INIT;
 	struct strbuf path = STRBUF_INIT;
 	DIR *dir;
 	struct dirent *d;
-	int i, counter = 0, alloc = 2;
+	int counter = 0, alloc = 2;
 
 	list = xmalloc(alloc * sizeof(struct worktree *));
 
@@ -184,17 +202,7 @@ struct worktree **get_worktrees(void)
 	ALLOC_GROW(list, counter + 1, alloc);
 	list[counter] = NULL;
 
-	strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
-	for (i = 0; i < counter; i++) {
-		struct worktree *wt = list[i];
-		strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
-		wt->is_current = !strcmp_icase(git_dir.buf, path.buf);
-		strbuf_reset(&path);
-		if (wt->is_current)
-			break;
-	}
-	strbuf_release(&git_dir);
-	strbuf_release(&path);
+	mark_current_worktree(list);
 	return list;
 }
 
@@ -241,9 +249,14 @@ int is_worktree_being_bisected(const struct worktree *wt,
 	return found_rebase;
 }
 
+/*
+ * note: this function should be able to detect shared symref even if
+ * HEAD is temporarily detached (e.g. in the middle of rebase or
+ * bisect). New commands that do similar things should update this
+ * function as well.
+ */
 const struct worktree *find_shared_symref(const char *symref,
-					  const char *target,
-					  int ignore_current_worktree)
+					  const char *target)
 {
 	const struct worktree *existing = NULL;
 	struct strbuf path = STRBUF_INIT;
@@ -258,9 +271,6 @@ const struct worktree *find_shared_symref(const char *symref,
 	for (i = 0; worktrees[i]; i++) {
 		struct worktree *wt = worktrees[i];
 
-		if (ignore_current_worktree && wt->is_current)
-			continue;
-
 		if (wt->is_detached && !strcmp(symref, "HEAD")) {
 			if (is_worktree_being_rebased(wt, target)) {
 				existing = wt;
diff --git a/worktree.h b/worktree.h
index d4a3534..1394909 100644
--- a/worktree.h
+++ b/worktree.h
@@ -36,26 +36,21 @@ extern void free_worktrees(struct worktree **);
 
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
- * or any linked worktree, and return the path to the exising worktree
- * if it is. Returns NULL if there is no existing ref. The result
- * may be destroyed by the next call.
+ * or any linked worktree, and return the worktree that holds the ref,
+ * or NULL otherwise. The result may be destroyed by the next call.
  */
 extern const struct worktree *find_shared_symref(const char *symref,
-						 const char *target,
-						 int ignore_current_worktree);
+						 const char *target);
 
 int is_worktree_being_rebased(const struct worktree *wt, const char *target);
 int is_worktree_being_bisected(const struct worktree *wt, const char *target);
 
 /*
- * Similar to git_path() and git_pathdup() but can produce paths for a
- * specified worktree instead of current one
+ * Similar to git_path() but can produce paths for a specified
+ * worktree instead of current one
  */
 extern const char *worktree_git_path(const struct worktree *wt,
 				     const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
-extern char *worktree_git_pathdup(const struct worktree *wt,
-				  const char *fmt, ...)
-	__attribute__((format (printf, 2, 3)));
 
 #endif
diff --git a/wt-status.c b/wt-status.c
index 971e071..0032ef5 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1410,7 +1410,7 @@ void wt_status_get_state(struct wt_status_state *state,
 	if (!stat(git_path_merge_head(), &st)) {
 		state->merge_in_progress = 1;
 	} else if (wt_status_check_rebase(NULL, state)) {
-		; /* all set */
+		;		/* all set */
 	} else if (!stat(git_path_cherry_pick_head(), &st) &&
 			!get_sha1("CHERRY_PICK_HEAD", sha1)) {
 		state->cherry_pick_in_progress = 1;

  [01/13] dir.c: rename str(n)cmp_icase to fspath(n)cmp
  [02/13] path.c: add git_common_path() and strbuf_git_common_path()
  [03/13] worktree.c: store "id" instead of "git_dir"
  [04/13] worktree.c: make find_shared_symref() return struct worktree *
  [05/13] worktree.c: mark current worktree
  [06/13] path.c: refactor and add worktree_git_path()
  [07/13] wt-status.c: split rebase detection out of wt_status_get_state()
  [08/13] wt-status.c: make wt_status_check_rebase() work on any worktree
  [09/13] worktree.c: avoid referencing to worktrees[i] multiple times
  [10/13] worktree.c: check whether branch is rebased in another worktree
  [11/13] wt-status.c: split bisect detection out of wt_status_get_state()
  [12/13] worktree.c: check whether branch is bisected in another worktree
  [13/13] branch: do not rename a branch under bisect or rebase

Total 17 files changed, 344 insertions(+), 82 deletions(-)
--
Duy

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 01/13] dir.c: rename str(n)cmp_icase to fspath(n)cmp
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 02/13] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
                             ` (11 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

These functions compare two paths that are taken from file system.
Depending on the running file system, paths may need to be compared
case-sensitively or not, and maybe even something else in future. The
current names do not convey that well.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 dir.c         | 13 ++++++-------
 dir.h         |  4 ++--
 fast-import.c |  6 +++---
 sha1_file.c   |  2 +-
 4 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/dir.c b/dir.c
index 996653b..f04bd3b 100644
--- a/dir.c
+++ b/dir.c
@@ -53,13 +53,12 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 	int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
-/* helper string functions with support for the ignore_case flag */
-int strcmp_icase(const char *a, const char *b)
+int fspathcmp(const char *a, const char *b)
 {
 	return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
 }
 
-int strncmp_icase(const char *a, const char *b, size_t count)
+int fspathncmp(const char *a, const char *b, size_t count)
 {
 	return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
 }
@@ -802,12 +801,12 @@ int match_basename(const char *basename, int basenamelen,
 {
 	if (prefix == patternlen) {
 		if (patternlen == basenamelen &&
-		    !strncmp_icase(pattern, basename, basenamelen))
+		    !fspathncmp(pattern, basename, basenamelen))
 			return 1;
 	} else if (flags & EXC_FLAG_ENDSWITH) {
 		/* "*literal" matching against "fooliteral" */
 		if (patternlen - 1 <= basenamelen &&
-		    !strncmp_icase(pattern + 1,
+		    !fspathncmp(pattern + 1,
 				   basename + basenamelen - (patternlen - 1),
 				   patternlen - 1))
 			return 1;
@@ -844,7 +843,7 @@ int match_pathname(const char *pathname, int pathlen,
 	 */
 	if (pathlen < baselen + 1 ||
 	    (baselen && pathname[baselen] != '/') ||
-	    strncmp_icase(pathname, base, baselen))
+	    fspathncmp(pathname, base, baselen))
 		return 0;
 
 	namelen = baselen ? pathlen - baselen - 1 : pathlen;
@@ -858,7 +857,7 @@ int match_pathname(const char *pathname, int pathlen,
 		if (prefix > namelen)
 			return 0;
 
-		if (strncmp_icase(pattern, name, prefix))
+		if (fspathncmp(pattern, name, prefix))
 			return 0;
 		pattern += prefix;
 		patternlen -= prefix;
diff --git a/dir.h b/dir.h
index 301b737..e34d555 100644
--- a/dir.h
+++ b/dir.h
@@ -270,8 +270,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
 /* tries to remove the path with empty directories along it, ignores ENOENT */
 extern int remove_path(const char *path);
 
-extern int strcmp_icase(const char *a, const char *b);
-extern int strncmp_icase(const char *a, const char *b, size_t count);
+extern int fspathcmp(const char *a, const char *b);
+extern int fspathncmp(const char *a, const char *b, size_t count);
 extern int fnmatch_icase(const char *pattern, const char *string, int flags);
 
 /*
diff --git a/fast-import.c b/fast-import.c
index 9fc7093..339cd38 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1512,7 +1512,7 @@ static int tree_content_set(
 	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
 			if (!*slash1) {
 				if (!S_ISDIR(mode)
 						&& e->versions[1].mode == mode
@@ -1602,7 +1602,7 @@ static int tree_content_remove(
 	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
 			if (*slash1 && !S_ISDIR(e->versions[1].mode))
 				/*
 				 * If p names a file in some subdirectory, and a
@@ -1669,7 +1669,7 @@ static int tree_content_get(
 	t = root->tree;
 	for (i = 0; i < t->entry_count; i++) {
 		e = t->entries[i];
-		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+		if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
 			if (!*slash1)
 				goto found_entry;
 			if (!S_ISDIR(e->versions[1].mode))
diff --git a/sha1_file.c b/sha1_file.c
index d0f2aa0..ea6381b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -301,7 +301,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
 			return -1;
 		}
 	}
-	if (!strcmp_icase(ent->base, normalized_objdir)) {
+	if (!fspathcmp(ent->base, normalized_objdir)) {
 		free(ent);
 		return -1;
 	}
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 02/13] path.c: add git_common_path() and strbuf_git_common_path()
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 01/13] dir.c: rename str(n)cmp_icase to fspath(n)cmp Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 03/13] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
                             ` (10 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

These are mostly convenient functions to reduce code duplication. Most
of the time, we should be able to get by with git_path() which handles
$GIT_COMMON_DIR internally. However there are a few cases where we need
to construct paths manually, for example some paths from a specific
worktree. These functions will enable that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 cache.h |  3 +++
 path.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/cache.h b/cache.h
index 2711048..c04a17f 100644
--- a/cache.h
+++ b/cache.h
@@ -799,11 +799,14 @@ extern void check_repository_format(void);
  */
 extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
 extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
diff --git a/path.c b/path.c
index bbaea5a..2ebb23d 100644
--- a/path.c
+++ b/path.c
@@ -503,6 +503,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 	va_end(args);
 }
 
+static void do_git_common_path(struct strbuf *buf,
+			       const char *fmt,
+			       va_list args)
+{
+	strbuf_addstr(buf, get_git_common_dir());
+	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+		strbuf_addch(buf, '/');
+	strbuf_vaddf(buf, fmt, args);
+	strbuf_cleanup_path(buf);
+}
+
+const char *git_common_path(const char *fmt, ...)
+{
+	struct strbuf *pathname = get_pathname();
+	va_list args;
+	va_start(args, fmt);
+	do_git_common_path(pathname, fmt, args);
+	va_end(args);
+	return pathname->buf;
+}
+
+void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	do_git_common_path(sb, fmt, args);
+	va_end(args);
+}
+
 int validate_headref(const char *path)
 {
 	struct stat st;
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 03/13] worktree.c: store "id" instead of "git_dir"
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 01/13] dir.c: rename str(n)cmp_icase to fspath(n)cmp Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 02/13] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 04/13] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
                             ` (9 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

We can reconstruct git_dir from id quite easily. It's a bit hackier to
do the reverse.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c   |  3 ++-
 worktree.c | 31 ++++++++++++++++++-------------
 worktree.h |  8 +++++++-
 3 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/branch.c b/branch.c
index 4162443..0674a99 100644
--- a/branch.c
+++ b/branch.c
@@ -357,7 +357,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
 		if (strcmp(oldref, worktrees[i]->head_ref))
 			continue;
 
-		if (set_worktree_head_symref(worktrees[i]->git_dir, newref)) {
+		if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
+					     newref)) {
 			ret = -1;
 			error(_("HEAD of working tree %s is not updated"),
 			      worktrees[i]->path);
diff --git a/worktree.c b/worktree.c
index 6181a66..5ae54f0 100644
--- a/worktree.c
+++ b/worktree.c
@@ -9,7 +9,7 @@ void free_worktrees(struct worktree **worktrees)
 
 	for (i = 0; worktrees[i]; i++) {
 		free(worktrees[i]->path);
-		free(worktrees[i]->git_dir);
+		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
 		free(worktrees[i]);
 	}
@@ -74,13 +74,11 @@ static struct worktree *get_main_worktree(void)
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf worktree_path = STRBUF_INIT;
-	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf head_ref = STRBUF_INIT;
 	int is_bare = 0;
 	int is_detached = 0;
 
-	strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
-	strbuf_addbuf(&worktree_path, &gitdir);
+	strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
 	is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
 	if (is_bare)
 		strbuf_strip_suffix(&worktree_path, "/.");
@@ -92,7 +90,7 @@ static struct worktree *get_main_worktree(void)
 
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
-	worktree->git_dir = strbuf_detach(&gitdir, NULL);
+	worktree->id = NULL;
 	worktree->is_bare = is_bare;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
@@ -100,7 +98,6 @@ static struct worktree *get_main_worktree(void)
 
 done:
 	strbuf_release(&path);
-	strbuf_release(&gitdir);
 	strbuf_release(&worktree_path);
 	strbuf_release(&head_ref);
 	return worktree;
@@ -111,16 +108,13 @@ static struct worktree *get_linked_worktree(const char *id)
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf worktree_path = STRBUF_INIT;
-	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf head_ref = STRBUF_INIT;
 	int is_detached = 0;
 
 	if (!id)
 		die("Missing linked worktree name");
 
-	strbuf_addf(&gitdir, "%s/worktrees/%s",
-			absolute_path(get_git_common_dir()), id);
-	strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+	strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
 	if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
 		/* invalid gitdir file */
 		goto done;
@@ -140,7 +134,7 @@ static struct worktree *get_linked_worktree(const char *id)
 
 	worktree = xmalloc(sizeof(struct worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
-	worktree->git_dir = strbuf_detach(&gitdir, NULL);
+	worktree->id = xstrdup(id);
 	worktree->is_bare = 0;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
@@ -148,7 +142,6 @@ static struct worktree *get_linked_worktree(const char *id)
 
 done:
 	strbuf_release(&path);
-	strbuf_release(&gitdir);
 	strbuf_release(&worktree_path);
 	strbuf_release(&head_ref);
 	return worktree;
@@ -188,6 +181,16 @@ struct worktree **get_worktrees(void)
 	return list;
 }
 
+const char *get_worktree_git_dir(const struct worktree *wt)
+{
+	if (!wt)
+		return get_git_dir();
+	else if (!wt->id)
+		return get_git_common_dir();
+	else
+		return git_common_path("worktrees/%s", wt->id);
+}
+
 char *find_shared_symref(const char *symref, const char *target)
 {
 	char *existing = NULL;
@@ -199,7 +202,9 @@ char *find_shared_symref(const char *symref, const char *target)
 	for (i = 0; worktrees[i]; i++) {
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
-		strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+		strbuf_addf(&path, "%s/%s",
+			    get_worktree_git_dir(worktrees[i]),
+			    symref);
 
 		if (parse_ref(path.buf, &sb, NULL)) {
 			continue;
diff --git a/worktree.h b/worktree.h
index b4b3dda..3198c8d 100644
--- a/worktree.h
+++ b/worktree.h
@@ -3,7 +3,7 @@
 
 struct worktree {
 	char *path;
-	char *git_dir;
+	char *id;
 	char *head_ref;
 	unsigned char head_sha1[20];
 	int is_detached;
@@ -23,6 +23,12 @@ struct worktree {
 extern struct worktree **get_worktrees(void);
 
 /*
+ * Return git dir of the worktree. Note that the path may be relative.
+ * If wt is NULL, git dir of current worktree is returned.
+ */
+extern const char *get_worktree_git_dir(const struct worktree *wt);
+
+/*
  * Free up the memory for worktree(s)
  */
 extern void free_worktrees(struct worktree **);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 04/13] worktree.c: make find_shared_symref() return struct worktree *
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (2 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 03/13] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 05/13] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
                             ` (8 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This gives the caller more information and they can answer things like,
"is it the main worktree" or "is it the current worktree". The latter
question is needed for the "checkout a rebase branch" case later.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c         | 13 +++++++------
 builtin/branch.c |  8 ++++----
 builtin/notes.c  |  8 ++++----
 worktree.c       | 14 +++++++++-----
 worktree.h       |  8 ++++----
 5 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/branch.c b/branch.c
index 0674a99..1f1fbf5 100644
--- a/branch.c
+++ b/branch.c
@@ -336,13 +336,14 @@ void remove_branch_state(void)
 
 void die_if_checked_out(const char *branch)
 {
-	char *existing;
+	const struct worktree *wt;
 
-	existing = find_shared_symref("HEAD", branch);
-	if (existing) {
-		skip_prefix(branch, "refs/heads/", &branch);
-		die(_("'%s' is already checked out at '%s'"), branch, existing);
-	}
+	wt = find_shared_symref("HEAD", branch);
+	if (!wt)
+		return;
+	skip_prefix(branch, "refs/heads/", &branch);
+	die(_("'%s' is already checked out at '%s'"),
+	    branch, wt->path);
 }
 
 int replace_each_worktree_head_symref(const char *oldref, const char *newref)
diff --git a/builtin/branch.c b/builtin/branch.c
index 0adba62..bcde87d 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -220,12 +220,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 		name = mkpathdup(fmt, bname.buf);
 
 		if (kinds == FILTER_REFS_BRANCHES) {
-			char *worktree = find_shared_symref("HEAD", name);
-			if (worktree) {
+			const struct worktree *wt =
+				find_shared_symref("HEAD", name);
+			if (wt) {
 				error(_("Cannot delete branch '%s' "
 					"checked out at '%s'"),
-				      bname.buf, worktree);
-				free(worktree);
+				      bname.buf, wt->path);
 				ret = 1;
 				continue;
 			}
diff --git a/builtin/notes.c b/builtin/notes.c
index 6fd058d..c65b59a 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -847,15 +847,15 @@ static int merge(int argc, const char **argv, const char *prefix)
 		update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
 	else { /* Merge has unresolved conflicts */
-		char *existing;
+		const struct worktree *wt;
 		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
 		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
 		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-		existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
-		if (existing)
+		wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+		if (wt)
 			die(_("A notes merge into %s is already in-progress at %s"),
-			    default_notes_ref(), existing);
+			    default_notes_ref(), wt->path);
 		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
 			die("Failed to store link to current notes ref (%s)",
 			    default_notes_ref());
diff --git a/worktree.c b/worktree.c
index 5ae54f0..360ba41 100644
--- a/worktree.c
+++ b/worktree.c
@@ -191,14 +191,19 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
-char *find_shared_symref(const char *symref, const char *target)
+const struct worktree *find_shared_symref(const char *symref,
+					  const char *target)
 {
-	char *existing = NULL;
+	const struct worktree *existing = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf sb = STRBUF_INIT;
-	struct worktree **worktrees = get_worktrees();
+	static struct worktree **worktrees;
 	int i = 0;
 
+	if (worktrees)
+		free_worktrees(worktrees);
+	worktrees = get_worktrees();
+
 	for (i = 0; worktrees[i]; i++) {
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
@@ -211,14 +216,13 @@ char *find_shared_symref(const char *symref, const char *target)
 		}
 
 		if (!strcmp(sb.buf, target)) {
-			existing = xstrdup(worktrees[i]->path);
+			existing = worktrees[i];
 			break;
 		}
 	}
 
 	strbuf_release(&path);
 	strbuf_release(&sb);
-	free_worktrees(worktrees);
 
 	return existing;
 }
diff --git a/worktree.h b/worktree.h
index 3198c8d..ca50e73 100644
--- a/worktree.h
+++ b/worktree.h
@@ -35,10 +35,10 @@ extern void free_worktrees(struct worktree **);
 
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
- * or any linked worktree, and return the path to the exising worktree
- * if it is.  Returns NULL if there is no existing ref.  The caller is
- * responsible for freeing the returned path.
+ * or any linked worktree, and return the worktree that holds the ref,
+ * or NULL otherwise. The result may be destroyed by the next call.
  */
-extern char *find_shared_symref(const char *symref, const char *target);
+extern const struct worktree *find_shared_symref(const char *symref,
+						 const char *target);
 
 #endif
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 05/13] worktree.c: mark current worktree
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (3 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 04/13] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-05-06  7:51             ` Eric Sunshine
  2016-04-22 13:01           ` [PATCH v3 06/13] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
                             ` (7 subsequent siblings)
  12 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 24 ++++++++++++++++++++++++
 worktree.h |  1 +
 2 files changed, 25 insertions(+)

diff --git a/worktree.c b/worktree.c
index 360ba41..dc092a7 100644
--- a/worktree.c
+++ b/worktree.c
@@ -2,6 +2,7 @@
 #include "refs.h"
 #include "strbuf.h"
 #include "worktree.h"
+#include "dir.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -94,6 +95,7 @@ static struct worktree *get_main_worktree(void)
 	worktree->is_bare = is_bare;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
+	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
 
 done:
@@ -138,6 +140,7 @@ static struct worktree *get_linked_worktree(const char *id)
 	worktree->is_bare = 0;
 	worktree->head_ref = NULL;
 	worktree->is_detached = is_detached;
+	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
 
 done:
@@ -147,6 +150,25 @@ done:
 	return worktree;
 }
 
+static void mark_current_worktree(struct worktree **worktrees)
+{
+	struct strbuf git_dir = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	int i;
+
+	strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
+	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+		strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
+		wt->is_current = !fspathcmp(git_dir.buf, path.buf);
+		strbuf_reset(&path);
+		if (wt->is_current)
+			break;
+	}
+	strbuf_release(&git_dir);
+	strbuf_release(&path);
+}
+
 struct worktree **get_worktrees(void)
 {
 	struct worktree **list = NULL;
@@ -178,6 +200,8 @@ struct worktree **get_worktrees(void)
 	}
 	ALLOC_GROW(list, counter + 1, alloc);
 	list[counter] = NULL;
+
+	mark_current_worktree(list);
 	return list;
 }
 
diff --git a/worktree.h b/worktree.h
index ca50e73..ccdf69a 100644
--- a/worktree.h
+++ b/worktree.h
@@ -8,6 +8,7 @@ struct worktree {
 	unsigned char head_sha1[20];
 	int is_detached;
 	int is_bare;
+	int is_current;
 };
 
 /* Functions for acting on the information about worktrees. */
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 06/13] path.c: refactor and add worktree_git_path()
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (4 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 05/13] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 07/13] wt-status.c: split rebase detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
                             ` (6 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

do_git_path(), which is the common code for all git_path* functions, is
modified to take a worktree struct and can produce paths for any
worktree.

worktree_git_path() is the first function that makes use of this. It can
be used to write code that can examine any worktree. For example,
wt_status_get_state() will be converted using this to take
am/rebase/... state of any worktree.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 path.c     | 24 ++++++++++++++++++------
 worktree.h |  8 ++++++++
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/path.c b/path.c
index 2ebb23d..8fdd187 100644
--- a/path.c
+++ b/path.c
@@ -5,6 +5,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
+#include "worktree.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -383,10 +384,11 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
 		update_common_dir(buf, git_dir_len, NULL);
 }
 
-static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+			const char *fmt, va_list args)
 {
 	int gitdir_len;
-	strbuf_addstr(buf, get_git_dir());
+	strbuf_addstr(buf, get_worktree_git_dir(wt));
 	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
 		strbuf_addch(buf, '/');
 	gitdir_len = buf->len;
@@ -400,7 +402,7 @@ char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	va_list args;
 	strbuf_reset(buf);
 	va_start(args, fmt);
-	do_git_path(buf, fmt, args);
+	do_git_path(NULL, buf, fmt, args);
 	va_end(args);
 	return buf->buf;
 }
@@ -409,7 +411,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 {
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(sb, fmt, args);
+	do_git_path(NULL, sb, fmt, args);
 	va_end(args);
 }
 
@@ -418,7 +420,7 @@ const char *git_path(const char *fmt, ...)
 	struct strbuf *pathname = get_pathname();
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(pathname, fmt, args);
+	do_git_path(NULL, pathname, fmt, args);
 	va_end(args);
 	return pathname->buf;
 }
@@ -428,7 +430,7 @@ char *git_pathdup(const char *fmt, ...)
 	struct strbuf path = STRBUF_INIT;
 	va_list args;
 	va_start(args, fmt);
-	do_git_path(&path, fmt, args);
+	do_git_path(NULL, &path, fmt, args);
 	va_end(args);
 	return strbuf_detach(&path, NULL);
 }
@@ -454,6 +456,16 @@ const char *mkpath(const char *fmt, ...)
 	return cleanup_path(pathname->buf);
 }
 
+const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
+{
+	struct strbuf *pathname = get_pathname();
+	va_list args;
+	va_start(args, fmt);
+	do_git_path(wt, pathname, fmt, args);
+	va_end(args);
+	return pathname->buf;
+}
+
 static void do_submodule_path(struct strbuf *buf, const char *path,
 			      const char *fmt, va_list args)
 {
diff --git a/worktree.h b/worktree.h
index ccdf69a..0da8c1f 100644
--- a/worktree.h
+++ b/worktree.h
@@ -42,4 +42,12 @@ extern void free_worktrees(struct worktree **);
 extern const struct worktree *find_shared_symref(const char *symref,
 						 const char *target);
 
+/*
+ * Similar to git_path() but can produce paths for a specified
+ * worktree instead of current one
+ */
+extern const char *worktree_git_path(const struct worktree *wt,
+				     const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+
 #endif
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 07/13] wt-status.c: split rebase detection out of wt_status_get_state()
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (5 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 06/13] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 08/13] wt-status.c: make wt_status_check_rebase() work on any worktree Nguyễn Thái Ngọc Duy
                             ` (5 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

worktree.c:find_shared_symref() later needs to know if a branch is being
rebased, and only rebase, no cherry-pick, do detached branch... Split
this code so it can be used independently from other in-progress tests.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wt-status.c | 23 +++++++++++++++++------
 wt-status.h |  1 +
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index 1ea2ebe..ec9240d 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1360,15 +1360,11 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 	strbuf_release(&cb.buf);
 }
 
-void wt_status_get_state(struct wt_status_state *state,
-			 int get_detached_from)
+int wt_status_check_rebase(struct wt_status_state *state)
 {
 	struct stat st;
-	unsigned char sha1[20];
 
-	if (!stat(git_path_merge_head(), &st)) {
-		state->merge_in_progress = 1;
-	} else if (!stat(git_path("rebase-apply"), &st)) {
+	if (!stat(git_path("rebase-apply"), &st)) {
 		if (!stat(git_path("rebase-apply/applying"), &st)) {
 			state->am_in_progress = 1;
 			if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
@@ -1385,6 +1381,21 @@ void wt_status_get_state(struct wt_status_state *state,
 			state->rebase_in_progress = 1;
 		state->branch = read_and_strip_branch("rebase-merge/head-name");
 		state->onto = read_and_strip_branch("rebase-merge/onto");
+	} else
+		return 0;
+	return 1;
+}
+
+void wt_status_get_state(struct wt_status_state *state,
+			 int get_detached_from)
+{
+	struct stat st;
+	unsigned char sha1[20];
+
+	if (!stat(git_path_merge_head(), &st)) {
+		state->merge_in_progress = 1;
+	} else if (wt_status_check_rebase(state)) {
+		;		/* all set */
 	} else if (!stat(git_path_cherry_pick_head(), &st) &&
 			!get_sha1("CHERRY_PICK_HEAD", sha1)) {
 		state->cherry_pick_in_progress = 1;
diff --git a/wt-status.h b/wt-status.h
index c9b3b74..b398353 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -100,6 +100,7 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
+int wt_status_check_rebase(struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 08/13] wt-status.c: make wt_status_check_rebase() work on any worktree
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (6 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 07/13] wt-status.c: split rebase detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 09/13] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
                             ` (4 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This is a preparation step for find_shared_symref() to detect if any
worktree is being rebased.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wt-status.c | 33 ++++++++++++++++++++-------------
 wt-status.h |  5 ++++-
 2 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index ec9240d..ce5080c 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -15,6 +15,7 @@
 #include "column.h"
 #include "strbuf.h"
 #include "utf8.h"
+#include "worktree.h"
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -1262,13 +1263,13 @@ static void show_bisect_in_progress(struct wt_status *s,
 /*
  * Extract branch information from rebase/bisect
  */
-static char *read_and_strip_branch(const char *path)
+static char *get_branch(const struct worktree *wt, const char *path)
 {
 	struct strbuf sb = STRBUF_INIT;
 	unsigned char sha1[20];
 	const char *branch_name;
 
-	if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+	if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
 		goto got_nothing;
 
 	while (sb.len && sb.buf[sb.len - 1] == '\n')
@@ -1295,6 +1296,11 @@ got_nothing:
 	return NULL;
 }
 
+static char *read_and_strip_branch(const char *path)
+{
+	return get_branch(NULL, path);
+}
+
 struct grab_1st_switch_cbdata {
 	struct strbuf buf;
 	unsigned char nsha1[20];
@@ -1360,27 +1366,28 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 	strbuf_release(&cb.buf);
 }
 
-int wt_status_check_rebase(struct wt_status_state *state)
+int wt_status_check_rebase(const struct worktree *wt,
+			   struct wt_status_state *state)
 {
 	struct stat st;
 
-	if (!stat(git_path("rebase-apply"), &st)) {
-		if (!stat(git_path("rebase-apply/applying"), &st)) {
+	if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
+		if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
 			state->am_in_progress = 1;
-			if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+			if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
 				state->am_empty_patch = 1;
 		} else {
 			state->rebase_in_progress = 1;
-			state->branch = read_and_strip_branch("rebase-apply/head-name");
-			state->onto = read_and_strip_branch("rebase-apply/onto");
+			state->branch = get_branch(wt, "rebase-apply/head-name");
+			state->onto = get_branch(wt, "rebase-apply/onto");
 		}
-	} else if (!stat(git_path("rebase-merge"), &st)) {
-		if (!stat(git_path("rebase-merge/interactive"), &st))
+	} else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
+		if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
 			state->rebase_interactive_in_progress = 1;
 		else
 			state->rebase_in_progress = 1;
-		state->branch = read_and_strip_branch("rebase-merge/head-name");
-		state->onto = read_and_strip_branch("rebase-merge/onto");
+		state->branch = get_branch(wt, "rebase-merge/head-name");
+		state->onto = get_branch(wt, "rebase-merge/onto");
 	} else
 		return 0;
 	return 1;
@@ -1394,7 +1401,7 @@ void wt_status_get_state(struct wt_status_state *state,
 
 	if (!stat(git_path_merge_head(), &st)) {
 		state->merge_in_progress = 1;
-	} else if (wt_status_check_rebase(state)) {
+	} else if (wt_status_check_rebase(NULL, state)) {
 		;		/* all set */
 	} else if (!stat(git_path_cherry_pick_head(), &st) &&
 			!get_sha1("CHERRY_PICK_HEAD", sha1)) {
diff --git a/wt-status.h b/wt-status.h
index b398353..c4ddcad 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -6,6 +6,8 @@
 #include "color.h"
 #include "pathspec.h"
 
+struct worktree;
+
 enum color_wt_status {
 	WT_STATUS_HEADER = 0,
 	WT_STATUS_UPDATED,
@@ -100,7 +102,8 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
-int wt_status_check_rebase(struct wt_status_state *state);
+int wt_status_check_rebase(const struct worktree *wt,
+			   struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 09/13] worktree.c: avoid referencing to worktrees[i] multiple times
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (7 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 08/13] wt-status.c: make wt_status_check_rebase() work on any worktree Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 10/13] worktree.c: check whether branch is rebased in another worktree Nguyễn Thái Ngọc Duy
                             ` (3 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/worktree.c b/worktree.c
index dc092a7..927905b 100644
--- a/worktree.c
+++ b/worktree.c
@@ -229,10 +229,12 @@ const struct worktree *find_shared_symref(const char *symref,
 	worktrees = get_worktrees();
 
 	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
 		strbuf_addf(&path, "%s/%s",
-			    get_worktree_git_dir(worktrees[i]),
+			    get_worktree_git_dir(wt),
 			    symref);
 
 		if (parse_ref(path.buf, &sb, NULL)) {
@@ -240,7 +242,7 @@ const struct worktree *find_shared_symref(const char *symref,
 		}
 
 		if (!strcmp(sb.buf, target)) {
-			existing = worktrees[i];
+			existing = wt;
 			break;
 		}
 	}
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 10/13] worktree.c: check whether branch is rebased in another worktree
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (8 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 09/13] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 11/13] wt-status.c: split bisect detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
                             ` (2 subsequent siblings)
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This function find_shared_symref() is used in a couple places:

1) in builtin/branch.c: it's used to detect if a branch is checked out
   elsewhere and refuse to delete the branch.

2) in builtin/notes.c: it's used to detect if a note is being merged in
   another worktree

3) in branch.c, the function die_if_checked_out() is actually used by
   "git checkout" and "git worktree add" to see if a branch is already
   checked out elsewhere and refuse the operation.

In cases 1 and 3, if a rebase is ongoing, "HEAD" will be in detached
mode, find_shared_symref() fails to detect it and declares "no branch is
checked out here", which is not really what we want.

This patch tightens the test. If the given symref is "HEAD", we try to
detect if rebase is ongoing. If so return the branch being rebased. This
makes checkout and branch delete operations safer because you can't
checkout a branch being rebased in another place, or delete it.

Special case for checkout. If the current branch is being rebased,
git-rebase.sh may use "git checkout" to abort and return back to the
original branch. The updated test in find_shared_symref() will prevent
that and "git rebase --abort" will fail as a result.
find_shared_symref() and die_if_checked_out() have to learn a new
option ignore_current_worktree to loosen the test a bit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c                |  4 ++--
 branch.h                |  2 +-
 builtin/checkout.c      |  2 +-
 builtin/worktree.c      |  4 ++--
 t/t2025-worktree-add.sh | 38 ++++++++++++++++++++++++++++++++++++++
 worktree.c              | 32 ++++++++++++++++++++++++++++++++
 6 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/branch.c b/branch.c
index 1f1fbf5..a5a8dcb 100644
--- a/branch.c
+++ b/branch.c
@@ -334,12 +334,12 @@ void remove_branch_state(void)
 	unlink(git_path_squash_msg());
 }
 
-void die_if_checked_out(const char *branch)
+void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
 	const struct worktree *wt;
 
 	wt = find_shared_symref("HEAD", branch);
-	if (!wt)
+	if (!wt || (ignore_current_worktree && wt->is_current))
 		return;
 	skip_prefix(branch, "refs/heads/", &branch);
 	die(_("'%s' is already checked out at '%s'"),
diff --git a/branch.h b/branch.h
index d69163d..b2f9649 100644
--- a/branch.h
+++ b/branch.h
@@ -58,7 +58,7 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
  * worktree and die (with a message describing its checkout location) if
  * it is.
  */
-extern void die_if_checked_out(const char *branch);
+extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
 /*
  * Update all per-worktree HEADs pointing at the old ref to point the new ref.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index efcbd8f..6041718 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1111,7 +1111,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
 		if (head_ref &&
 		    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
-			die_if_checked_out(new->path);
+			die_if_checked_out(new->path, 1);
 		free(head_ref);
 	}
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index d8e3795..12c0af7 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -205,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
 	if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
 		 ref_exists(symref.buf)) { /* it's a branch */
 		if (!opts->force)
-			die_if_checked_out(symref.buf);
+			die_if_checked_out(symref.buf, 0);
 	} else { /* must be a commit */
 		commit = lookup_commit_reference_by_name(refname);
 		if (!commit)
@@ -349,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
 		if (!opts.force &&
 		    !strbuf_check_branch_ref(&symref, opts.new_branch) &&
 		    ref_exists(symref.buf))
-			die_if_checked_out(symref.buf);
+			die_if_checked_out(symref.buf, 0);
 		strbuf_release(&symref);
 	}
 
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 3acb992..da54327 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -4,6 +4,8 @@ test_description='test git worktree add'
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success 'setup' '
 	test_commit init
 '
@@ -225,4 +227,40 @@ test_expect_success '"add" worktree with --checkout' '
 	test_cmp init.t swamp2/init.t
 '
 
+test_expect_success 'put a worktree under rebase' '
+	git worktree add under-rebase &&
+	(
+		cd under-rebase &&
+		set_fake_editor &&
+		FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+		git worktree list | grep "under-rebase.*detached HEAD"
+	)
+'
+
+test_expect_success 'add a worktree, checking out a rebased branch' '
+	test_must_fail git worktree add new-rebase under-rebase &&
+	! test -d new-rebase
+'
+
+test_expect_success 'checking out a rebased branch from another worktree' '
+	git worktree add new-place &&
+	test_must_fail git -C new-place checkout under-rebase
+'
+
+test_expect_success 'not allow to delete a branch under rebase' '
+	(
+		cd under-rebase &&
+		test_must_fail git branch -D under-rebase
+	)
+'
+
+test_expect_success 'check out from current worktree branch ok' '
+	(
+		cd under-rebase &&
+		git checkout under-rebase &&
+		git checkout - &&
+		git rebase --abort
+	)
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index 927905b..5043756 100644
--- a/worktree.c
+++ b/worktree.c
@@ -3,6 +3,7 @@
 #include "strbuf.h"
 #include "worktree.h"
 #include "dir.h"
+#include "wt-status.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -215,6 +216,30 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+static int is_worktree_being_rebased(const struct worktree *wt,
+				     const char *target)
+{
+	struct wt_status_state state;
+	int found_rebase;
+
+	memset(&state, 0, sizeof(state));
+	found_rebase = wt_status_check_rebase(wt, &state) &&
+		((state.rebase_in_progress ||
+		  state.rebase_interactive_in_progress) &&
+		 state.branch &&
+		 starts_with(target, "refs/heads/") &&
+		 !strcmp(state.branch, target + strlen("refs/heads/")));
+	free(state.branch);
+	free(state.onto);
+	return found_rebase;
+}
+
+/*
+ * note: this function should be able to detect shared symref even if
+ * HEAD is temporarily detached (e.g. in the middle of rebase or
+ * bisect). New commands that do similar things should update this
+ * function as well.
+ */
 const struct worktree *find_shared_symref(const char *symref,
 					  const char *target)
 {
@@ -231,6 +256,13 @@ const struct worktree *find_shared_symref(const char *symref,
 	for (i = 0; worktrees[i]; i++) {
 		struct worktree *wt = worktrees[i];
 
+		if (wt->is_detached && !strcmp(symref, "HEAD")) {
+			if (is_worktree_being_rebased(wt, target)) {
+				existing = wt;
+				break;
+			}
+		}
+
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
 		strbuf_addf(&path, "%s/%s",
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 11/13] wt-status.c: split bisect detection out of wt_status_get_state()
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (9 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 10/13] worktree.c: check whether branch is rebased in another worktree Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 12/13] worktree.c: check whether branch is bisected in another worktree Nguyễn Thái Ngọc Duy
  2016-04-22 13:01           ` [PATCH v3 13/13] branch: do not rename a branch under bisect or rebase Nguyễn Thái Ngọc Duy
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

And make it work with any given worktree, in preparation for (again)
find_shared_symref(). read_and_strip_branch() is deleted because it's
no longer used.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wt-status.c | 23 ++++++++++++++---------
 wt-status.h |  2 ++
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index ce5080c..0032ef5 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1296,11 +1296,6 @@ got_nothing:
 	return NULL;
 }
 
-static char *read_and_strip_branch(const char *path)
-{
-	return get_branch(NULL, path);
-}
-
 struct grab_1st_switch_cbdata {
 	struct strbuf buf;
 	unsigned char nsha1[20];
@@ -1393,6 +1388,19 @@ int wt_status_check_rebase(const struct worktree *wt,
 	return 1;
 }
 
+int wt_status_check_bisect(const struct worktree *wt,
+			   struct wt_status_state *state)
+{
+	struct stat st;
+
+	if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
+		state->bisect_in_progress = 1;
+		state->branch = get_branch(wt, "BISECT_START");
+		return 1;
+	}
+	return 0;
+}
+
 void wt_status_get_state(struct wt_status_state *state,
 			 int get_detached_from)
 {
@@ -1408,10 +1416,7 @@ void wt_status_get_state(struct wt_status_state *state,
 		state->cherry_pick_in_progress = 1;
 		hashcpy(state->cherry_pick_head_sha1, sha1);
 	}
-	if (!stat(git_path("BISECT_LOG"), &st)) {
-		state->bisect_in_progress = 1;
-		state->branch = read_and_strip_branch("BISECT_START");
-	}
+	wt_status_check_bisect(NULL, state);
 	if (!stat(git_path_revert_head(), &st) &&
 	    !get_sha1("REVERT_HEAD", sha1)) {
 		state->revert_in_progress = 1;
diff --git a/wt-status.h b/wt-status.h
index c4ddcad..2ca93f6 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -104,6 +104,8 @@ void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
 int wt_status_check_rebase(const struct worktree *wt,
 			   struct wt_status_state *state);
+int wt_status_check_bisect(const struct worktree *wt,
+			   struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 12/13] worktree.c: check whether branch is bisected in another worktree
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (10 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 11/13] wt-status.c: split bisect detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  2016-05-11  5:45             ` Eric Sunshine
  2016-04-22 13:01           ` [PATCH v3 13/13] branch: do not rename a branch under bisect or rebase Nguyễn Thái Ngọc Duy
  12 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Similar to the rebase case, we want to detect if "HEAD" in some worktree
is being bisected because

1) we do not want to checkout this branch in another worktree, after
   bisect is done it will want to go back to this branch

2) we do not want to delete the branch is either or git bisect will
   fail to return to the (long gone) branch

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t2025-worktree-add.sh | 13 +++++++++++++
 worktree.c              | 19 +++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index da54327..8f53944 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -263,4 +263,17 @@ test_expect_success 'check out from current worktree branch ok' '
 	)
 '
 
+test_expect_success 'checkout a branch under bisect' '
+	git worktree add under-bisect &&
+	(
+		cd under-bisect &&
+		git bisect start &&
+		git bisect bad &&
+		git bisect good HEAD~2 &&
+		git worktree list | grep "under-bisect.*detached HEAD" &&
+		test_must_fail git worktree add new-bisect under-bisect &&
+		! test -d new-bisect
+	)
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index 5043756..aab4b95 100644
--- a/worktree.c
+++ b/worktree.c
@@ -234,6 +234,21 @@ static int is_worktree_being_rebased(const struct worktree *wt,
 	return found_rebase;
 }
 
+static int is_worktree_being_bisected(const struct worktree *wt,
+				      const char *target)
+{
+	struct wt_status_state state;
+	int found_rebase;
+
+	memset(&state, 0, sizeof(state));
+	found_rebase = wt_status_check_bisect(wt, &state) &&
+		state.branch &&
+		starts_with(target, "refs/heads/") &&
+		!strcmp(state.branch, target + strlen("refs/heads/"));
+	free(state.branch);
+	return found_rebase;
+}
+
 /*
  * note: this function should be able to detect shared symref even if
  * HEAD is temporarily detached (e.g. in the middle of rebase or
@@ -261,6 +276,10 @@ const struct worktree *find_shared_symref(const char *symref,
 				existing = wt;
 				break;
 			}
+			if (is_worktree_being_bisected(wt, target)) {
+				existing = wt;
+				break;
+			}
 		}
 
 		strbuf_reset(&path);
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 13/13] branch: do not rename a branch under bisect or rebase
  2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
                             ` (11 preceding siblings ...)
  2016-04-22 13:01           ` [PATCH v3 12/13] worktree.c: check whether branch is bisected in another worktree Nguyễn Thái Ngọc Duy
@ 2016-04-22 13:01           ` Nguyễn Thái Ngọc Duy
  12 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-04-22 13:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

The branch name in that case could be saved in rebase's head_name or
bisect's BISECT_START files. Ideally we should try to update them as
well. But it's trickier (*). Let's play safe and see if the user
complains about inconveniences before doing that.

(*) If we do it, bisect and rebase need to provide an API to rename
branches. We can't do it in worktree.c or builtin/branch.c because
when other people change rebase/bisect code, they may not be aware of
this code and accidentally break it (e.g. rename the branch file, or
refer to the branch in new files). It's a lot more work.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/branch.c        | 25 +++++++++++++++++++++++++
 t/t2025-worktree-add.sh |  8 ++++++++
 worktree.c              |  8 ++++----
 worktree.h              |  3 +++
 4 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index bcde87d..b488c3f 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -524,6 +524,29 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 	ref_array_clear(&array);
 }
 
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+	struct worktree **worktrees = get_worktrees();
+	int i;
+
+	for (i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+
+		if (!wt->is_detached)
+			continue;
+
+		if (is_worktree_being_rebased(wt, target))
+			die(_("Branch %s is being rebased at %s"),
+			    target, wt->path);
+
+		if (is_worktree_being_bisected(wt, target))
+			die(_("Branch %s is being bisected at %s"),
+			    target, wt->path);
+	}
+
+	free_worktrees(worktrees);
+}
+
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
 	struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
@@ -553,6 +576,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 
 	validate_new_branchname(newname, &newref, force, clobber_head_ok);
 
+	reject_rebase_or_bisect_branch(oldref.buf);
+
 	strbuf_addf(&logmsg, "Branch: renamed %s to %s",
 		 oldref.buf, newref.buf);
 
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 8f53944..3a22fc5 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -254,6 +254,10 @@ test_expect_success 'not allow to delete a branch under rebase' '
 	)
 '
 
+test_expect_success 'rename a branch under rebase not allowed' '
+	test_must_fail git branch -M under-rebase rebase-with-new-name
+'
+
 test_expect_success 'check out from current worktree branch ok' '
 	(
 		cd under-rebase &&
@@ -276,4 +280,8 @@ test_expect_success 'checkout a branch under bisect' '
 	)
 '
 
+test_expect_success 'rename a branch under bisect not allowed' '
+	test_must_fail git branch -M under-bisect bisect-with-new-name
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index aab4b95..4817d60 100644
--- a/worktree.c
+++ b/worktree.c
@@ -216,8 +216,8 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
-static int is_worktree_being_rebased(const struct worktree *wt,
-				     const char *target)
+int is_worktree_being_rebased(const struct worktree *wt,
+			      const char *target)
 {
 	struct wt_status_state state;
 	int found_rebase;
@@ -234,8 +234,8 @@ static int is_worktree_being_rebased(const struct worktree *wt,
 	return found_rebase;
 }
 
-static int is_worktree_being_bisected(const struct worktree *wt,
-				      const char *target)
+int is_worktree_being_bisected(const struct worktree *wt,
+			       const char *target)
 {
 	struct wt_status_state state;
 	int found_rebase;
diff --git a/worktree.h b/worktree.h
index 0da8c1f..1394909 100644
--- a/worktree.h
+++ b/worktree.h
@@ -42,6 +42,9 @@ extern void free_worktrees(struct worktree **);
 extern const struct worktree *find_shared_symref(const char *symref,
 						 const char *target);
 
+int is_worktree_being_rebased(const struct worktree *wt, const char *target);
+int is_worktree_being_bisected(const struct worktree *wt, const char *target);
+
 /*
  * Similar to git_path() but can produce paths for a specified
  * worktree instead of current one
-- 
2.8.0.rc0.210.gd302cd2

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 05/13] worktree.c: mark current worktree
  2016-04-22 13:01           ` [PATCH v3 05/13] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
@ 2016-05-06  7:51             ` Eric Sunshine
  2016-05-06 10:21               ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-05-06  7:51 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Fri, Apr 22, 2016 at 9:01 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -147,6 +150,25 @@ done:
> +static void mark_current_worktree(struct worktree **worktrees)
> +{
> +       struct strbuf git_dir = STRBUF_INIT;
> +       struct strbuf path = STRBUF_INIT;
> +       int i;
> +
> +       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));

This could also just be:

    char *git_dir = xstrdup(absolute_path(...));

with a corresponding 'free(git_dir)' below.

> +       for (i = 0; worktrees[i]; i++) {
> +               struct worktree *wt = worktrees[i];
> +               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
> +               wt->is_current = !fspathcmp(git_dir.buf, path.buf);

Similarly, it looks like 'path' doesn't need to be a strbuf at all
since the result of absolute_path() should remain valid long enough
for fspathcmp(). It could just be:

    const char *path = absolute_path(...);
    wt->is_current = !fspathcmp(git_dir, path);

But these are very minor; probably not worth a re-roll.

> +               strbuf_reset(&path);
> +               if (wt->is_current)
> +                       break;
> +       }
> +       strbuf_release(&git_dir);
> +       strbuf_release(&path);
> +}

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 05/13] worktree.c: mark current worktree
  2016-05-06  7:51             ` Eric Sunshine
@ 2016-05-06 10:21               ` Duy Nguyen
  2016-05-10 14:14                 ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-05-06 10:21 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Fri, May 6, 2016 at 2:51 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Fri, Apr 22, 2016 at 9:01 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>> diff --git a/worktree.c b/worktree.c
>> @@ -147,6 +150,25 @@ done:
>> +static void mark_current_worktree(struct worktree **worktrees)
>> +{
>> +       struct strbuf git_dir = STRBUF_INIT;
>> +       struct strbuf path = STRBUF_INIT;
>> +       int i;
>> +
>> +       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
>
> This could also just be:
>
>     char *git_dir = xstrdup(absolute_path(...));
>
> with a corresponding 'free(git_dir)' below.
>
>> +       for (i = 0; worktrees[i]; i++) {
>> +               struct worktree *wt = worktrees[i];
>> +               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
>> +               wt->is_current = !fspathcmp(git_dir.buf, path.buf);
>
> Similarly, it looks like 'path' doesn't need to be a strbuf at all
> since the result of absolute_path() should remain valid long enough
> for fspathcmp(). It could just be:
>
>     const char *path = absolute_path(...);
>     wt->is_current = !fspathcmp(git_dir, path);
>
> But these are very minor; probably not worth a re-roll.

Yeah. I think the use of strbuf is influenced by the code in
get_worktrees(). But since this code is now in a separate function, it
makes little sense to go with the strbuf hammer. If there's no big
change in this series, I'll just do this as a cleanup step in my next
series, worktree-move.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 05/13] worktree.c: mark current worktree
  2016-05-06 10:21               ` Duy Nguyen
@ 2016-05-10 14:14                 ` Duy Nguyen
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
                                     ` (5 more replies)
  0 siblings, 6 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-05-10 14:14 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Fri, May 06, 2016 at 05:21:05PM +0700, Duy Nguyen wrote:
> > Similarly, it looks like 'path' doesn't need to be a strbuf at all
> > since the result of absolute_path() should remain valid long enough
> > for fspathcmp(). It could just be:
> >
> >     const char *path = absolute_path(...);
> >     wt->is_current = !fspathcmp(git_dir, path);
> >
> > But these are very minor; probably not worth a re-roll.
> 
> Yeah. I think the use of strbuf is influenced by the code in
> get_worktrees(). But since this code is now in a separate function, it
> makes little sense to go with the strbuf hammer. If there's no big
> change in this series, I'll just do this as a cleanup step in my next
> series, worktree-move.

On second thought, why hold patches back, lengthen the worktree-move
series and make it a pain to review? I moved a few patches from
worktree-move into this series and I took two other out to create
nd/error-errno. So I'm going to take more patches out of it, creating
two bite-sized series, to be sent shortly.

The first one is purely cleanup (ok, 1/7 is not exactly cleanup)

  [1/7] completion: support git-worktree
  [2/7] worktree.c: rewrite mark_current_worktree() to avoid
  [3/7] git-worktree.txt: keep subcommand listing in alphabetical
  [4/7] worktree.c: use is_dot_or_dotdot()
  [5/7] worktree.c: add clear_worktree()
  [6/7] worktree: avoid 0{40}, too many zeroes, hard to read
  [7/7] worktree: simplify prefixing paths

And the second one adds "git worktree lock" and "git worktree
unlock". This series is built on top of the first one, it needs 1/7.

  [1/5] worktree.c: add find_worktree_by_path()
  [2/5] worktree.c: add is_main_worktree()
  [3/5] worktree.c: add is_worktree_locked()
  [4/5] worktree: add "lock" command
  [5/5] worktree: add "unlock" command

After this, worktree-move becomes ~10 patch series.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH 1/7] completion: support git-worktree
  2016-05-10 14:14                 ` Duy Nguyen
@ 2016-05-10 14:15                   ` Nguyễn Thái Ngọc Duy
  2016-05-10 14:15                     ` [PATCH 2/7] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
                                       ` (6 more replies)
  2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
                                     ` (4 subsequent siblings)
  5 siblings, 7 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:15 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 3402475..d3ac391 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2595,6 +2595,29 @@ _git_whatchanged ()
 	_git_log
 }
 
+_git_worktree ()
+{
+	local subcommands="add list prune"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+	else
+		case "$subcommand,$cur" in
+		add,--*)
+			__gitcomp "--detach --force"
+			;;
+		list,--*)
+			__gitcomp "--porcelain"
+			;;
+		prune,--*)
+			__gitcomp "--dry-run --expire --verbose"
+			;;
+		*)
+			;;
+		esac
+	fi
+}
+
 __git_main ()
 {
 	local i c=1 command __git_dir
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 2/7] worktree.c: rewrite mark_current_worktree() to avoid strbuf
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:15                     ` Nguyễn Thái Ngọc Duy
  2016-05-11  6:16                       ` Eric Sunshine
  2016-05-10 14:15                     ` [PATCH 3/7] git-worktree.txt: keep subcommand listing in alphabetical order Nguyễn Thái Ngọc Duy
                                       ` (5 subsequent siblings)
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:15 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

strbuf is a bit overkill for this function. What we need is call
absolute_path() twice and make sure the second call does not destroy the
result of the first. One buffer allocation is enough.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/worktree.c b/worktree.c
index 4817d60..6a11611 100644
--- a/worktree.c
+++ b/worktree.c
@@ -153,21 +153,19 @@ done:
 
 static void mark_current_worktree(struct worktree **worktrees)
 {
-	struct strbuf git_dir = STRBUF_INIT;
-	struct strbuf path = STRBUF_INIT;
+	char *git_dir = xstrdup(absolute_path(get_git_dir()));
 	int i;
 
-	strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
 	for (i = 0; worktrees[i]; i++) {
 		struct worktree *wt = worktrees[i];
-		strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
-		wt->is_current = !fspathcmp(git_dir.buf, path.buf);
-		strbuf_reset(&path);
-		if (wt->is_current)
+		const char *wt_git_dir = get_worktree_git_dir(wt);
+
+		if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
+			wt->is_current = 1;
 			break;
+		}
 	}
-	strbuf_release(&git_dir);
-	strbuf_release(&path);
+	free(git_dir);
 }
 
 struct worktree **get_worktrees(void)
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 3/7] git-worktree.txt: keep subcommand listing in alphabetical order
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
  2016-05-10 14:15                     ` [PATCH 2/7] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:15                     ` Nguyễn Thái Ngọc Duy
  2016-05-10 14:15                     ` [PATCH 4/7] worktree.c: use is_dot_or_dotdot() Nguyễn Thái Ngọc Duy
                                       ` (4 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:15 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This is probably not the best order. But it makes it no-brainer to know
where to insert new commands. At some point we might want to reorder at
least the synopsis part again, grouping commonly use subcommands together.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt | 10 +++++-----
 builtin/worktree.c             |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index c622345..27feff6 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,8 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
-'git worktree prune' [-n] [-v] [--expire <expire>]
 'git worktree list' [--porcelain]
+'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
 -----------
@@ -54,10 +54,6 @@ If `<branch>` is omitted and neither `-b` nor `-B` nor `--detached` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
 
-prune::
-
-Prune working tree information in $GIT_DIR/worktrees.
-
 list::
 
 List details of each worktree.  The main worktree is listed first, followed by
@@ -65,6 +61,10 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+prune::
+
+Prune working tree information in $GIT_DIR/worktrees.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 12c0af7..bf80111 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -13,8 +13,8 @@
 
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<branch>]"),
-	N_("git worktree prune [<options>]"),
 	N_("git worktree list [<options>]"),
+	N_("git worktree prune [<options>]"),
 	NULL
 };
 
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 4/7] worktree.c: use is_dot_or_dotdot()
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
  2016-05-10 14:15                     ` [PATCH 2/7] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
  2016-05-10 14:15                     ` [PATCH 3/7] git-worktree.txt: keep subcommand listing in alphabetical order Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:15                     ` Nguyễn Thái Ngọc Duy
  2016-05-10 14:15                     ` [PATCH 5/7] worktree.c: add clear_worktree() Nguyễn Thái Ngọc Duy
                                       ` (3 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:15 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/worktree.c | 2 +-
 worktree.c         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index bf80111..aaee0e2 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -95,7 +95,7 @@ static void prune_worktrees(void)
 	if (!dir)
 		return;
 	while ((d = readdir(dir)) != NULL) {
-		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+		if (is_dot_or_dotdot(d->d_name))
 			continue;
 		strbuf_reset(&reason);
 		if (!prune_worktree(d->d_name, &reason))
diff --git a/worktree.c b/worktree.c
index 6a11611..f4a4f38 100644
--- a/worktree.c
+++ b/worktree.c
@@ -187,7 +187,7 @@ struct worktree **get_worktrees(void)
 	if (dir) {
 		while ((d = readdir(dir)) != NULL) {
 			struct worktree *linked = NULL;
-			if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+			if (is_dot_or_dotdot(d->d_name))
 				continue;
 
 			if ((linked = get_linked_worktree(d->d_name))) {
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 5/7] worktree.c: add clear_worktree()
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
                                       ` (2 preceding siblings ...)
  2016-05-10 14:15                     ` [PATCH 4/7] worktree.c: use is_dot_or_dotdot() Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:15                     ` Nguyễn Thái Ngọc Duy
  2016-05-11  6:36                       ` Eric Sunshine
  2016-05-10 14:15                     ` [PATCH 6/7] worktree: avoid 0{40}, too many zeroes, hard to read Nguyễn Thái Ngọc Duy
                                       ` (2 subsequent siblings)
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:15 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

The use case is keep some worktree and discard the rest of the worktree
list.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 14 +++++++++++---
 worktree.h |  5 +++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/worktree.c b/worktree.c
index f4a4f38..335c58f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -5,14 +5,22 @@
 #include "dir.h"
 #include "wt-status.h"
 
+void clear_worktree(struct worktree *wt)
+{
+	if (!wt)
+		return;
+	free(wt->path);
+	free(wt->id);
+	free(wt->head_ref);
+	memset(wt, 0, sizeof(*wt));
+}
+
 void free_worktrees(struct worktree **worktrees)
 {
 	int i = 0;
 
 	for (i = 0; worktrees[i]; i++) {
-		free(worktrees[i]->path);
-		free(worktrees[i]->id);
-		free(worktrees[i]->head_ref);
+		clear_worktree(worktrees[i]);
 		free(worktrees[i]);
 	}
 	free (worktrees);
diff --git a/worktree.h b/worktree.h
index 1394909..7430a4e 100644
--- a/worktree.h
+++ b/worktree.h
@@ -29,6 +29,11 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
+/*
+ * Free up the memory for worktree
+ */
+extern void clear_worktree(struct worktree *);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 6/7] worktree: avoid 0{40}, too many zeroes, hard to read
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
                                       ` (3 preceding siblings ...)
  2016-05-10 14:15                     ` [PATCH 5/7] worktree.c: add clear_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:15                     ` Nguyễn Thái Ngọc Duy
  2016-05-10 14:15                     ` [PATCH 7/7] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
  2016-05-11  6:12                     ` [PATCH 1/7] completion: support git-worktree Eric Sunshine
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:15 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/worktree.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index aaee0e2..b53f802 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -262,7 +262,7 @@ static int add_worktree(const char *path, const char *refname,
 	 */
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-	write_file(sb.buf, "0000000000000000000000000000000000000000");
+	write_file(sb.buf, sha1_to_hex(null_sha1));
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
 	write_file(sb.buf, "../..");
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 7/7] worktree: simplify prefixing paths
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
                                       ` (4 preceding siblings ...)
  2016-05-10 14:15                     ` [PATCH 6/7] worktree: avoid 0{40}, too many zeroes, hard to read Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:15                     ` Nguyễn Thái Ngọc Duy
  2016-05-10 23:07                       ` Junio C Hamano
  2016-05-11  6:12                     ` [PATCH 1/7] completion: support git-worktree Eric Sunshine
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:15 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/worktree.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index b53f802..f9dac37 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -337,7 +337,7 @@ static int add(int ac, const char **av, const char *prefix)
 	if (ac < 1 || ac > 2)
 		usage_with_options(worktree_usage, options);
 
-	path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
+	path = prefix_filename(prefix, strlen(prefix), av[0]);
 	branch = ac < 2 ? "HEAD" : av[1];
 
 	opts.force_new_branch = !!new_branch_force;
@@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 
 	if (ac < 2)
 		usage_with_options(worktree_usage, options);
+	if (!prefix)
+		prefix = "";
 	if (!strcmp(av[1], "add"))
 		return add(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "prune"))
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 1/5] worktree.c: add find_worktree_by_path()
  2016-05-10 14:14                 ` Duy Nguyen
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:17                   ` Nguyễn Thái Ngọc Duy
  2016-05-10 14:17                     ` [PATCH 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
                                       ` (4 more replies)
  2016-05-10 17:32                   ` [PATCH v3 05/13] worktree.c: mark current worktree Junio C Hamano
                                     ` (3 subsequent siblings)
  5 siblings, 5 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:17 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

So far we haven't needed to identify an existing worktree from command
line. Future commands such as lock or move will need it. There are of
course other options for identifying a worktree, for example by branch
or even by internal id. They may be added later if proved useful.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 16 ++++++++++++++++
 worktree.h |  6 ++++++
 2 files changed, 22 insertions(+)

diff --git a/worktree.c b/worktree.c
index 335c58f..a6d1ad1 100644
--- a/worktree.c
+++ b/worktree.c
@@ -222,6 +222,22 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+struct worktree *find_worktree_by_path(struct worktree **list,
+				       const char *path_)
+{
+	char *path = xstrdup(real_path(path_));
+	struct worktree *wt = NULL;
+
+	while (*list) {
+		wt = *list++;
+		if (!fspathcmp(path, real_path(wt->path)))
+			break;
+		wt = NULL;
+	}
+	free(path);
+	return wt;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 7430a4e..7775d1b 100644
--- a/worktree.h
+++ b/worktree.h
@@ -29,6 +29,12 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
+/*
+ * Search a worktree by its path. Paths are normalized internally.
+ */
+extern struct worktree *find_worktree_by_path(struct worktree **list,
+					      const char *path_);
+
 /*
  * Free up the memory for worktree
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 2/5] worktree.c: add is_main_worktree()
  2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:17                     ` Nguyễn Thái Ngọc Duy
  2016-05-13 16:32                       ` Eric Sunshine
  2016-05-10 14:17                     ` [PATCH 3/5] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
                                       ` (3 subsequent siblings)
  4 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:17 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Main worktree _is_ different. You can lock a linked worktree but not the
main one, for example. Provide an API for checking that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 5 +++++
 worktree.h | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/worktree.c b/worktree.c
index a6d1ad1..37fea3d 100644
--- a/worktree.c
+++ b/worktree.c
@@ -238,6 +238,11 @@ struct worktree *find_worktree_by_path(struct worktree **list,
 	return wt;
 }
 
+int is_main_worktree(const struct worktree *wt)
+{
+	return wt && !wt->id;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 7775d1b..77a9e09 100644
--- a/worktree.h
+++ b/worktree.h
@@ -35,6 +35,11 @@ extern const char *get_worktree_git_dir(const struct worktree *wt);
 extern struct worktree *find_worktree_by_path(struct worktree **list,
 					      const char *path_);
 
+/*
+ * Return true if the given worktree is the main one.
+ */
+extern int is_main_worktree(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 3/5] worktree.c: add is_worktree_locked()
  2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
  2016-05-10 14:17                     ` [PATCH 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:17                     ` Nguyễn Thái Ngọc Duy
  2016-05-13 16:52                       ` Eric Sunshine
  2016-05-10 14:17                     ` [PATCH 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
                                       ` (2 subsequent siblings)
  4 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:17 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This provides an API for checking if a worktree is locked. We need to
check this to avoid double locking a worktree, or try to unlock one when
it's not even locked.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 18 ++++++++++++++++++
 worktree.h |  6 ++++++
 2 files changed, 24 insertions(+)

diff --git a/worktree.c b/worktree.c
index 37fea3d..6805deb 100644
--- a/worktree.c
+++ b/worktree.c
@@ -243,6 +243,24 @@ int is_main_worktree(const struct worktree *wt)
 	return wt && !wt->id;
 }
 
+const char *is_worktree_locked(const struct worktree *wt)
+{
+	static struct strbuf sb = STRBUF_INIT;
+
+	if (!file_exists(git_common_path("worktrees/%s/locked", wt->id)))
+		return NULL;
+
+	strbuf_reset(&sb);
+	if (strbuf_read_file(&sb,
+			     git_common_path("worktrees/%s/locked", wt->id),
+			     0) < 0)
+		die_errno(_("failed to read '%s'"),
+			  git_common_path("worktrees/%s/locked", wt->id));
+
+	strbuf_rtrim(&sb);
+	return sb.buf;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 77a9e09..12906be 100644
--- a/worktree.h
+++ b/worktree.h
@@ -40,6 +40,12 @@ extern struct worktree *find_worktree_by_path(struct worktree **list,
  */
 extern int is_main_worktree(const struct worktree *wt);
 
+/*
+ * Return the reason string if the given worktree is locked. Return
+ * NULL otherwise.
+ */
+extern const char *is_worktree_locked(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 4/5] worktree: add "lock" command
  2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
  2016-05-10 14:17                     ` [PATCH 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
  2016-05-10 14:17                     ` [PATCH 3/5] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:17                     ` Nguyễn Thái Ngọc Duy
  2016-05-16  0:09                       ` Eric Sunshine
  2016-05-16  0:40                       ` Eric Sunshine
  2016-05-10 14:17                     ` [PATCH 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
  2016-05-13 16:29                     ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Eric Sunshine
  4 siblings, 2 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:17 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         | 12 ++++++++--
 builtin/worktree.c                     | 41 ++++++++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 ++++-
 t/t2028-worktree-move.sh (new +x)      | 34 ++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 3 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 27feff6..74583c1 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
 'git worktree list' [--porcelain]
+'git worktree lock' [--reason <string>] <path>
 'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
@@ -61,6 +62,12 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+lock::
+
+When a worktree is locked, it cannot be pruned, moved or deleted. For
+example, if the worktree is on portable device that is not available
+when "git worktree <command>" is executed.
+
 prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
@@ -110,6 +117,9 @@ OPTIONS
 --expire <time>::
 	With `prune`, only expire unused working trees older than <time>.
 
+--reason <string>:
+	An explanation why the worktree is locked.
+
 DETAILS
 -------
 Each linked working tree has a private sub-directory in the repository's
@@ -226,8 +236,6 @@ performed manually, such as:
 - `remove` to remove a linked working tree and its administrative files (and
   warn if the working tree is dirty)
 - `mv` to move or rename a working tree and update its administrative files
-- `lock` to prevent automatic pruning of administrative files (for instance,
-  for a working tree on a portable device)
 
 GIT
 ---
diff --git a/builtin/worktree.c b/builtin/worktree.c
index f9dac37..51fa103 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -14,6 +14,7 @@
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<branch>]"),
 	N_("git worktree list [<options>]"),
+	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
 	NULL
 };
@@ -459,6 +460,44 @@ static int list(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+	const char *reason = "", *old_reason;
+	struct option options[] = {
+		OPT_STRING(0, "reason", &reason, N_("string"),
+			   N_("reason for locking")),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	struct strbuf dst = STRBUF_INIT;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	strbuf_addstr(&dst, prefix_filename(prefix,
+					    strlen(prefix),
+					    av[0]));
+
+	worktrees = get_worktrees();
+	wt = find_worktree_by_path(worktrees, dst.buf);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+
+	old_reason = is_worktree_locked(wt);
+	if (old_reason) {
+		if (*old_reason)
+			die(_("already locked, reason: %s"), old_reason);
+		die(_("already locked, no reason"));
+	}
+
+	write_file(git_common_path("worktrees/%s/locked", wt->id),
+		   "%s", reason);
+	return 0;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -475,5 +514,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return prune(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "list"))
 		return list(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "lock"))
+		return lock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d3ac391..6c321aa 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list prune"
+	local subcommands="add list lock prune"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
@@ -2609,6 +2609,9 @@ _git_worktree ()
 		list,--*)
 			__gitcomp "--porcelain"
 			;;
+		lock,--*)
+			__gitcomp "--reason"
+			;;
 		prune,--*)
 			__gitcomp "--dry-run --expire --verbose"
 			;;
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
new file mode 100755
index 0000000..97434be
--- /dev/null
+++ b/t/t2028-worktree-move.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='test git worktree move, remove, lock and unlock'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init &&
+	git worktree add source &&
+	git worktree list --porcelain | grep "^worktree" >actual &&
+	cat <<-EOF >expected &&
+	worktree $TRASH_DIRECTORY
+	worktree $TRASH_DIRECTORY/source
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'lock main worktree' '
+	test_must_fail git worktree lock .
+'
+
+test_expect_success 'lock linked worktree' '
+	git worktree lock --reason hahaha source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice' '
+	test_must_fail git worktree lock source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH 5/5] worktree: add "unlock" command
  2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
                                       ` (2 preceding siblings ...)
  2016-05-10 14:17                     ` [PATCH 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-05-10 14:17                     ` Nguyễn Thái Ngọc Duy
  2016-05-16  0:42                       ` Eric Sunshine
  2016-05-13 16:29                     ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Eric Sunshine
  4 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-10 14:17 UTC (permalink / raw)
  To: git
  Cc: Eric Sunshine, Junio C Hamano, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         |  5 +++++
 builtin/worktree.c                     | 31 +++++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  2 +-
 t/t2028-worktree-move.sh               | 14 ++++++++++++++
 4 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 74583c1..9ac1129 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <path>
 'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree unlock' <path>
 
 DESCRIPTION
 -----------
@@ -72,6 +73,10 @@ prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
 
+unlock::
+
+Unlock a worktree, allowing it to be pruned, moved or deleted.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 51fa103..53e5f5a 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -16,6 +16,7 @@ static const char * const worktree_usage[] = {
 	N_("git worktree list [<options>]"),
 	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
+	N_("git worktree unlock <path>"),
 	NULL
 };
 
@@ -498,6 +499,34 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	struct strbuf dst = STRBUF_INIT;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	strbuf_addstr(&dst, prefix_filename(prefix,
+					    strlen(prefix),
+					    av[0]));
+
+	worktrees = get_worktrees();
+	wt = find_worktree_by_path(worktrees, dst.buf);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+	if (!is_worktree_locked(wt))
+		die(_("not locked"));
+
+	return unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -516,5 +545,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return list(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "lock"))
 		return lock_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "unlock"))
+		return unlock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 6c321aa..db4b0e7 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list lock prune"
+	local subcommands="add list lock prune unlock"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 97434be..f4b2816 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -31,4 +31,18 @@ test_expect_success 'lock worktree twice' '
 	test_cmp expected .git/worktrees/source/locked
 '
 
+test_expect_success 'unlock main worktree' '
+	test_must_fail git worktree unlock .
+'
+
+test_expect_success 'unlock linked worktree' '
+	git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
+test_expect_success 'unlock worktree twice' '
+	test_must_fail git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
 test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 05/13] worktree.c: mark current worktree
  2016-05-10 14:14                 ` Duy Nguyen
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
  2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
@ 2016-05-10 17:32                   ` Junio C Hamano
  2016-05-10 23:03                   ` Junio C Hamano
                                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-05-10 17:32 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Eric Sunshine, Git List, Reto Hablützel, Mike Rappazzo

Duy Nguyen <pclouds@gmail.com> writes:

> On second thought, why hold patches back, lengthen the worktree-move
> series and make it a pain to review? I moved a few patches from
> worktree-move into this series and I took two other out to create
> nd/error-errno. So I'm going to take more patches out of it, creating
> two bite-sized series, to be sent shortly.
>
> The first one is purely cleanup (ok, 1/7 is not exactly cleanup)
>
>   [1/7] completion: support git-worktree
>   [2/7] worktree.c: rewrite mark_current_worktree() to avoid
>   [3/7] git-worktree.txt: keep subcommand listing in alphabetical
>   [4/7] worktree.c: use is_dot_or_dotdot()
>   [5/7] worktree.c: add clear_worktree()
>   [6/7] worktree: avoid 0{40}, too many zeroes, hard to read
>   [7/7] worktree: simplify prefixing paths
>
> And the second one adds "git worktree lock" and "git worktree
> unlock". This series is built on top of the first one, it needs 1/7.
>
>   [1/5] worktree.c: add find_worktree_by_path()
>   [2/5] worktree.c: add is_main_worktree()
>   [3/5] worktree.c: add is_worktree_locked()
>   [4/5] worktree: add "lock" command
>   [5/5] worktree: add "unlock" command
>
> After this, worktree-move becomes ~10 patch series.

Yay.  Thanks; will take a look.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 05/13] worktree.c: mark current worktree
  2016-05-10 14:14                 ` Duy Nguyen
                                     ` (2 preceding siblings ...)
  2016-05-10 17:32                   ` [PATCH v3 05/13] worktree.c: mark current worktree Junio C Hamano
@ 2016-05-10 23:03                   ` Junio C Hamano
  2016-05-11  0:33                     ` Duy Nguyen
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
  5 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-05-10 23:03 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Eric Sunshine, Git List, Reto Hablützel, Mike Rappazzo

Duy Nguyen <pclouds@gmail.com> writes:

> On second thought, why hold patches back, lengthen the worktree-move
> series and make it a pain to review? I moved a few patches from
> worktree-move into this series and I took two other out to create
> nd/error-errno. So I'm going to take more patches out of it, creating
> two bite-sized series, to be sent shortly.
>
> The first one is purely cleanup (ok, 1/7 is not exactly cleanup)
>
>   [1/7] completion: support git-worktree
>   [2/7] worktree.c: rewrite mark_current_worktree() to avoid
>   [3/7] git-worktree.txt: keep subcommand listing in alphabetical
>   [4/7] worktree.c: use is_dot_or_dotdot()
>   [5/7] worktree.c: add clear_worktree()
>   [6/7] worktree: avoid 0{40}, too many zeroes, hard to read
>   [7/7] worktree: simplify prefixing paths

Where are these patches designed to apply?

It appears that this depends on something in 'next' (probably
nd/worktree-various-heads topic?)

>
> And the second one adds "git worktree lock" and "git worktree
> unlock". This series is built on top of the first one, it needs 1/7.
>
>   [1/5] worktree.c: add find_worktree_by_path()
>   [2/5] worktree.c: add is_main_worktree()
>   [3/5] worktree.c: add is_worktree_locked()
>   [4/5] worktree: add "lock" command
>   [5/5] worktree: add "unlock" command
>
> After this, worktree-move becomes ~10 patch series.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 7/7] worktree: simplify prefixing paths
  2016-05-10 14:15                     ` [PATCH 7/7] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
@ 2016-05-10 23:07                       ` Junio C Hamano
  2016-05-11  0:46                         ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-05-10 23:07 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---

This changes semantics, doesn't it?  prefix_filename() seems to do a
lot more than just strbuf_vadd("%s%s", prefix, filename); would do.

It may be a good change (e.g. turn '\' into '/' on Windows), but
this is way more than "simplify prefixing".  It is something else
whose effect needs to be explained.

>  builtin/worktree.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/worktree.c b/builtin/worktree.c
> index b53f802..f9dac37 100644
> --- a/builtin/worktree.c
> +++ b/builtin/worktree.c
> @@ -337,7 +337,7 @@ static int add(int ac, const char **av, const char *prefix)
>  	if (ac < 1 || ac > 2)
>  		usage_with_options(worktree_usage, options);
>  
> -	path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
> +	path = prefix_filename(prefix, strlen(prefix), av[0]);
>  	branch = ac < 2 ? "HEAD" : av[1];
>  
>  	opts.force_new_branch = !!new_branch_force;
> @@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
>  
>  	if (ac < 2)
>  		usage_with_options(worktree_usage, options);
> +	if (!prefix)
> +		prefix = "";
>  	if (!strcmp(av[1], "add"))
>  		return add(ac - 1, av + 1, prefix);
>  	if (!strcmp(av[1], "prune"))

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 05/13] worktree.c: mark current worktree
  2016-05-10 23:03                   ` Junio C Hamano
@ 2016-05-11  0:33                     ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-05-11  0:33 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Eric Sunshine, Git List, Reto Hablützel, Mike Rappazzo

On Wed, May 11, 2016 at 6:03 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Duy Nguyen <pclouds@gmail.com> writes:
>
>> On second thought, why hold patches back, lengthen the worktree-move
>> series and make it a pain to review? I moved a few patches from
>> worktree-move into this series and I took two other out to create
>> nd/error-errno. So I'm going to take more patches out of it, creating
>> two bite-sized series, to be sent shortly.
>>
>> The first one is purely cleanup (ok, 1/7 is not exactly cleanup)
>>
>>   [1/7] completion: support git-worktree
>>   [2/7] worktree.c: rewrite mark_current_worktree() to avoid
>>   [3/7] git-worktree.txt: keep subcommand listing in alphabetical
>>   [4/7] worktree.c: use is_dot_or_dotdot()
>>   [5/7] worktree.c: add clear_worktree()
>>   [6/7] worktree: avoid 0{40}, too many zeroes, hard to read
>>   [7/7] worktree: simplify prefixing paths
>
> Where are these patches designed to apply?
>
> It appears that this depends on something in 'next' (probably
> nd/worktree-various-heads topic?)

Yes. Sorry I forgot to mention that. Though if you move 2/7 to
nd/worktree-various-heads (and deal with some conflicts in worktree.c)
then it may become independent.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 7/7] worktree: simplify prefixing paths
  2016-05-10 23:07                       ` Junio C Hamano
@ 2016-05-11  0:46                         ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-05-11  0:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

On Wed, May 11, 2016 at 6:07 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>
> This changes semantics, doesn't it?  prefix_filename() seems to do a
> lot more than just strbuf_vadd("%s%s", prefix, filename); would do.
>
> It may be a good change (e.g. turn '\' into '/' on Windows), but
> this is way more than "simplify prefixing".  It is something else
> whose effect needs to be explained.

On Wed, May 11, 2016 at 6:07 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>
> This changes semantics, doesn't it?  prefix_filename() seems to do a
> lot more than just strbuf_vadd("%s%s", prefix, filename); would do.
>
> It may be a good change (e.g. turn '\' into '/' on Windows), but
> this is way more than "simplify prefixing".  It is something else
> whose effect needs to be explained.

I admit I forgot about Windows part in prefix_filename(). For
non-Windows code, it's exactly the same behavior. Maybe we should do
this to emphasize that prefix_filename() is no-op when pfx_len is
zero? The same pattern is used elsewhere too (or I'm spreading it in
my local tree..) Unless of course if convert_slashes() is a good thing
to always do, then I need to update my commit message (+Johannes for
this question).

diff --git a/abspath.c b/abspath.c
index 2825de8..bf454e0 100644
--- a/abspath.c
+++ b/abspath.c
@@ -160,8 +160,11 @@ const char *absolute_path(const char *path)
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
  static struct strbuf path = STRBUF_INIT;
+
+ if (!pfx_len)
+ return arg;
 #ifndef GIT_WINDOWS_NATIVE
- if (!pfx_len || is_absolute_path(arg))
+ if (is_absolute_path(arg))
  return arg;
  strbuf_reset(&path);
  strbuf_add(&path, pfx, pfx_len);

>
>>  builtin/worktree.c | 4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/builtin/worktree.c b/builtin/worktree.c
>> index b53f802..f9dac37 100644
>> --- a/builtin/worktree.c
>> +++ b/builtin/worktree.c
>> @@ -337,7 +337,7 @@ static int add(int ac, const char **av, const char *prefix)
>>       if (ac < 1 || ac > 2)
>>               usage_with_options(worktree_usage, options);
>>
>> -     path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
>> +     path = prefix_filename(prefix, strlen(prefix), av[0]);
>>       branch = ac < 2 ? "HEAD" : av[1];
>>
>>       opts.force_new_branch = !!new_branch_force;
>> @@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
>>
>>       if (ac < 2)
>>               usage_with_options(worktree_usage, options);
>> +     if (!prefix)
>> +             prefix = "";
>>       if (!strcmp(av[1], "add"))
>>               return add(ac - 1, av + 1, prefix);
>>       if (!strcmp(av[1], "prune"))
-- 
Duy

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 12/13] worktree.c: check whether branch is bisected in another worktree
  2016-04-22 13:01           ` [PATCH v3 12/13] worktree.c: check whether branch is bisected in another worktree Nguyễn Thái Ngọc Duy
@ 2016-05-11  5:45             ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-11  5:45 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Fri, Apr 22, 2016 at 9:01 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Similar to the rebase case, we want to detect if "HEAD" in some worktree
> is being bisected because
>
> 1) we do not want to checkout this branch in another worktree, after
>    bisect is done it will want to go back to this branch
>
> 2) we do not want to delete the branch is either or git bisect will
>    fail to return to the (long gone) branch

I'm very far behind with my reviews and I see that this series is
already in 'next', so perhaps these comments are too late...

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
> @@ -263,4 +263,17 @@ test_expect_success 'check out from current worktree branch ok' '
> +test_expect_success 'checkout a branch under bisect' '
> +       git worktree add under-bisect &&
> +       (
> +               cd under-bisect &&
> +               git bisect start &&
> +               git bisect bad &&
> +               git bisect good HEAD~2 &&
> +               git worktree list | grep "under-bisect.*detached HEAD" &&
> +               test_must_fail git worktree add new-bisect under-bisect &&

Nit: I realize that the checking 'worktree add' is sufficient, but
it's a bit confusing to read in the commit message about how deleting
the branch would be bad, but then see it testing only 'add'.

> +               ! test -d new-bisect
> +       )
> +'
> diff --git a/worktree.c b/worktree.c
> @@ -234,6 +234,21 @@ static int is_worktree_being_rebased(const struct worktree *wt,
> +static int is_worktree_being_bisected(const struct worktree *wt,
> +                                     const char *target)
> +{
> +       struct wt_status_state state;
> +       int found_rebase;

s/rebase/bisect/ perhaps?

> +       memset(&state, 0, sizeof(state));
> +       found_rebase = wt_status_check_bisect(wt, &state) &&
> +               state.branch &&
> +               starts_with(target, "refs/heads/") &&
> +               !strcmp(state.branch, target + strlen("refs/heads/"));
> +       free(state.branch);
> +       return found_rebase;
> +}

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 1/7] completion: support git-worktree
  2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
                                       ` (5 preceding siblings ...)
  2016-05-10 14:15                     ` [PATCH 7/7] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
@ 2016-05-11  6:12                     ` Eric Sunshine
  6 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-11  6:12 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:15 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> completion: support git-worktree

See [1] and, especially, [2] for previous attempts...

[1]: http://thread.gmane.org/gmane.comp.version-control.git/274526
[2]: http://thread.gmane.org/gmane.comp.version-control.git/274526/focus=276333

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index 3402475..d3ac391 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -2595,6 +2595,29 @@ _git_whatchanged ()
>         _git_log
>  }
>
> +_git_worktree ()
> +{
> +       local subcommands="add list prune"
> +       local subcommand="$(__git_find_on_cmdline "$subcommands")"
> +       if [ -z "$subcommand" ]; then
> +               __gitcomp "$subcommands"
> +       else
> +               case "$subcommand,$cur" in
> +               add,--*)
> +                       __gitcomp "--detach --force"
> +                       ;;
> +               list,--*)
> +                       __gitcomp "--porcelain"
> +                       ;;
> +               prune,--*)
> +                       __gitcomp "--dry-run --expire --verbose"
> +                       ;;
> +               *)
> +                       ;;
> +               esac
> +       fi
> +}
> +
>  __git_main ()
>  {
>         local i c=1 command __git_dir
> --
> 2.8.2.524.g6ff3d78

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 2/7] worktree.c: rewrite mark_current_worktree() to avoid strbuf
  2016-05-10 14:15                     ` [PATCH 2/7] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
@ 2016-05-11  6:16                       ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-11  6:16 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:15 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> strbuf is a bit overkill for this function. What we need is call

s/is call/is to call/

> absolute_path() twice and make sure the second call does not destroy the
> result of the first. One buffer allocation is enough.

The patch itself looks good.

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -153,21 +153,19 @@ done:
>  static void mark_current_worktree(struct worktree **worktrees)
>  {
> -       struct strbuf git_dir = STRBUF_INIT;
> -       struct strbuf path = STRBUF_INIT;
> +       char *git_dir = xstrdup(absolute_path(get_git_dir()));
>         int i;
>
> -       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
>         for (i = 0; worktrees[i]; i++) {
>                 struct worktree *wt = worktrees[i];
> -               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
> -               wt->is_current = !fspathcmp(git_dir.buf, path.buf);
> -               strbuf_reset(&path);
> -               if (wt->is_current)
> +               const char *wt_git_dir = get_worktree_git_dir(wt);
> +
> +               if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
> +                       wt->is_current = 1;
>                         break;
> +               }
>         }
> -       strbuf_release(&git_dir);
> -       strbuf_release(&path);
> +       free(git_dir);
>  }

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 5/7] worktree.c: add clear_worktree()
  2016-05-10 14:15                     ` [PATCH 5/7] worktree.c: add clear_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-11  6:36                       ` Eric Sunshine
  2016-05-11  6:42                         ` Eric Sunshine
  2016-05-22  8:58                         ` Duy Nguyen
  0 siblings, 2 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-11  6:36 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:15 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> The use case is keep some worktree and discard the rest of the worktree
> list.

So, you're saying that rather than a client freeing the entire
worktree list like this:

    free_worktrees(worktrees);

it might instead want to keep some element ('n') and free all others
plus the list itself, like this:

    struct worktree *keep = worktrees[n];
    struct worktree **p = worktrees;
    for (; *p; p++)
        if (*p != keep)
            clear_worktree(*p);
    free(worktrees);

Is that correct?

If so, then doesn't this require the client to have far too intimate
knowledge of the internals of free_worktrees(). Or, was your idea that
the client would simply leak the 'worktrees' array after freeing the
unwanted elements?

In either case, a cleaner approach might be to provide a function for
copying a worktree element. Perhaps:

    struct worktree *copy_worktree(const struct worktree *);

or something?

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> index f4a4f38..335c58f 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -5,14 +5,22 @@
>  #include "dir.h"
>  #include "wt-status.h"
>
> +void clear_worktree(struct worktree *wt)
> +{
> +       if (!wt)
> +               return;
> +       free(wt->path);
> +       free(wt->id);
> +       free(wt->head_ref);
> +       memset(wt, 0, sizeof(*wt));
> +}
> +
>  void free_worktrees(struct worktree **worktrees)
>  {
>         int i = 0;
>
>         for (i = 0; worktrees[i]; i++) {
> -               free(worktrees[i]->path);
> -               free(worktrees[i]->id);
> -               free(worktrees[i]->head_ref);
> +               clear_worktree(worktrees[i]);
>                 free(worktrees[i]);
>         }
>         free (worktrees);
> diff --git a/worktree.h b/worktree.h
> index 1394909..7430a4e 100644
> --- a/worktree.h
> +++ b/worktree.h
> @@ -29,6 +29,11 @@ extern struct worktree **get_worktrees(void);
>   */
>  extern const char *get_worktree_git_dir(const struct worktree *wt);
>
> +/*
> + * Free up the memory for worktree
> + */
> +extern void clear_worktree(struct worktree *);
> +
>  /*
>   * Free up the memory for worktree(s)
>   */
> --
> 2.8.2.524.g6ff3d78

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 5/7] worktree.c: add clear_worktree()
  2016-05-11  6:36                       ` Eric Sunshine
@ 2016-05-11  6:42                         ` Eric Sunshine
  2016-05-22  8:58                         ` Duy Nguyen
  1 sibling, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-11  6:42 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Wed, May 11, 2016 at 2:36 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Tue, May 10, 2016 at 10:15 AM, Nguyễn Thái Ngọc Duy
> <pclouds@gmail.com> wrote:
>> The use case is keep some worktree and discard the rest of the worktree
>> list.
>
> So, you're saying that rather than a client freeing the entire
> worktree list like this:
>
>     free_worktrees(worktrees);
>
> it might instead want to keep some element ('n') and free all others
> plus the list itself, like this:
>
>     struct worktree *keep = worktrees[n];
>     struct worktree **p = worktrees;
>     for (; *p; p++)
>         if (*p != keep)
>             clear_worktree(*p);
>     free(worktrees);
>
> Is that correct?
>
> If so, then doesn't this require the client to have far too intimate
> knowledge of the internals of free_worktrees(). Or, was your idea that
> the client would simply leak the 'worktrees' array after freeing the
> unwanted elements?
>
> In either case, a cleaner approach might be to provide a function for
> copying a worktree element. Perhaps:
>
>     struct worktree *copy_worktree(const struct worktree *);

Of course, you'd still need a clear_worktree() or free_worktree()
function to dispose of the copy.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 1/5] worktree.c: add find_worktree_by_path()
  2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
                                       ` (3 preceding siblings ...)
  2016-05-10 14:17                     ` [PATCH 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-05-13 16:29                     ` Eric Sunshine
  4 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-13 16:29 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:17 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> So far we haven't needed to identify an existing worktree from command
> line. Future commands such as lock or move will need it. There are of
> course other options for identifying a worktree, for example by branch
> or even by internal id. They may be added later if proved useful.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -222,6 +222,22 @@ const char *get_worktree_git_dir(const struct worktree *wt)
> +struct worktree *find_worktree_by_path(struct worktree **list,
> +                                      const char *path_)
> +{
> +       char *path = xstrdup(real_path(path_));
> +       struct worktree *wt = NULL;
> +
> +       while (*list) {
> +               wt = *list++;
> +               if (!fspathcmp(path, real_path(wt->path)))
> +                       break;
> +               wt = NULL;
> +       }
> +       free(path);
> +       return wt;
> +}

Very slightly shorter and perhaps idiomatic (but not at all worth a re-roll):

    for (; *list; list++)
        if (!fspathcmp(path, real_path((*list)->path)))
            break;
    free(path);
    return *list;

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 2/5] worktree.c: add is_main_worktree()
  2016-05-10 14:17                     ` [PATCH 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-13 16:32                       ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-13 16:32 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:17 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> Main worktree _is_ different. You can lock a linked worktree but not the
> main one, for example. Provide an API for checking that.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -238,6 +238,11 @@ struct worktree *find_worktree_by_path(struct worktree **list,
> +int is_main_worktree(const struct worktree *wt)
> +{
> +       return wt && !wt->id;
> +}

Is or will there ever be a case when it is valid for 'wt' to be NULL?
If not, perhaps assert() or die() if given NULL?

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 3/5] worktree.c: add is_worktree_locked()
  2016-05-10 14:17                     ` [PATCH 3/5] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
@ 2016-05-13 16:52                       ` Eric Sunshine
  2016-05-22  9:53                         ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-05-13 16:52 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:17 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> This provides an API for checking if a worktree is locked. We need to
> check this to avoid double locking a worktree, or try to unlock one when
> it's not even locked.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -243,6 +243,24 @@ int is_main_worktree(const struct worktree *wt)
> +const char *is_worktree_locked(const struct worktree *wt)
> +{
> +       static struct strbuf sb = STRBUF_INIT;
> +
> +       if (!file_exists(git_common_path("worktrees/%s/locked", wt->id)))
> +               return NULL;

The git_common_path(...) invocation is textually lengthy and repeated
three times in this function. If you instead assign the result to a
variable (possibly xstrdup'ing it if needed), then the below
strbuf_read_file() would likely fit on one line, thus be easier to
read.

> +
> +       strbuf_reset(&sb);
> +       if (strbuf_read_file(&sb,
> +                            git_common_path("worktrees/%s/locked", wt->id),
> +                            0) < 0)

It's too bad that strbuf_read_file() doesn't guarantee anything about
'errno', such as indicating that the file did not exist, in which case
the !file_exists() check would not be needed, and a bit of raciness
eliminated, but that's outside the scope of this series.

> +               die_errno(_("failed to read '%s'"),
> +                         git_common_path("worktrees/%s/locked", wt->id));
> +
> +       strbuf_rtrim(&sb);

Since this file is presumably human-editable (historically and at this
point in the series) in order to specify the lock reason, should this
be doing a full trim() rather than only rtrim()?

> +       return sb.buf;
> +}
> diff --git a/worktree.h b/worktree.h
> @@ -40,6 +40,12 @@ extern struct worktree *find_worktree_by_path(struct worktree **list,
> +/*
> + * Return the reason string if the given worktree is locked. Return
> + * NULL otherwise.
> + */

Does this need to mention that the returned "locked reason" string is
only guaranteed valid until the next invocation?

Actually, I recall that when I suggested the idea of 'struct worktree'
and get_worktrees() to Mike that it would be natural for the structure
to have a 'locked' (or 'locked_reason') field, in which case the
reason could be stored there instead of in this static strbuf. In
fact, my idea at the time was that get_worktrees() would populate that
field automatically, rather than having to do so via a separate
on-demand function call as in this patch.

> +extern const char *is_worktree_locked(const struct worktree *wt);

I was wondering if builtin/worktree.c:prune_worktree() should be
updated to invoke this new function instead of consulting
"worktrees/<id>/locked" manually, but I see that the entire "prune
worktrees" functionality in builting/worktree.c first needs to be
updated to the get_worktrees() API for that to happen.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 4/5] worktree: add "lock" command
  2016-05-10 14:17                     ` [PATCH 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-05-16  0:09                       ` Eric Sunshine
  2016-05-22 10:31                         ` Duy Nguyen
  2016-05-16  0:40                       ` Eric Sunshine
  1 sibling, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-05-16  0:09 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:17 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
> @@ -11,6 +11,7 @@ SYNOPSIS
>  [verse]
>  'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
>  'git worktree list' [--porcelain]
> +'git worktree lock' [--reason <string>] <path>
>  'git worktree prune' [-n] [-v] [--expire <expire>]
>
>  DESCRIPTION

This forgets to update the paragraph in DESCRIPTION which currently
talks about manually creating the 'locked' file. Perhaps it can be
rewritten like this:

    If a linked working tree is stored on a portable device or
    network share which is not always mounted, you can prevent its
    administrative files from being pruned by issuing the `git
    worktree lock` command, optionally specifying `--reason` to
    explain why the working tree is locked.

The DETAILS section might also need a small update. It currently says:

    ...add a file named 'locked'...

but perhaps it should instead say:

    ...use the `git worktree lock` command, which adds a
    file named `locked`,...

> @@ -61,6 +62,12 @@ each of the linked worktrees.  The output details include if the worktree is
> +lock::
> +
> +When a worktree is locked, it cannot be pruned, moved or deleted. For
> +example, if the worktree is on portable device that is not available
> +when "git worktree <command>" is executed.

The really important reason for locking a worktree is to prevent its
administrative files from being pruned *automatically*. This
description doesn't properly emphasize that point.

Also, bc48328 (Documentation/git-worktree: consistently use term
"linked working tree", 2015-07-20) comprehensively replaced the term
"worktree" with "working tree" and, although some new instances
slipped in with bb9c03b (worktree: add 'list' command, 2015-10-08), we
should probably avoid adding more.

Perhaps the description of "lock" can be rewritten like this:

    If a working tree is on a portable device or network share which
    is not always mounted, lock it to prevent its administrative
    files from being pruned automatically. This also prevents it from
    being moved or deleted. Optionally, specify a reason for the lock
    with `--reason`.

I guess preventing it from being deleted when locked is a safety measure?

> @@ -110,6 +117,9 @@ OPTIONS
>  --expire <time>::
>         With `prune`, only expire unused working trees older than <time>.
>
> +--reason <string>:
> +       An explanation why the worktree is locked.

For consistency with other option descriptions, perhaps:

    With `lock`, an explanation ...

though I don't feel strongly about it.

> diff --git a/builtin/worktree.c b/builtin/worktree.c
> @@ -459,6 +460,44 @@ static int list(int ac, const char **av, const char *prefix)
> +static int lock_worktree(int ac, const char **av, const char *prefix)
> +{
> +       const char *reason = "", *old_reason;
> +       struct option options[] = {
> +               OPT_STRING(0, "reason", &reason, N_("string"),
> +                          N_("reason for locking")),
> +               OPT_END()
> +       };
> +       struct worktree **worktrees, *wt;
> +       struct strbuf dst = STRBUF_INIT;
> +
> +       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
> +       if (ac != 1)
> +               usage_with_options(worktree_usage, options);
> +
> +       strbuf_addstr(&dst, prefix_filename(prefix,
> +                                           strlen(prefix),
> +                                           av[0]));
> +
> +       worktrees = get_worktrees();
> +       wt = find_worktree_by_path(worktrees, dst.buf);
> +       if (!wt)
> +               die(_("'%s' is not a working directory"), av[0]);
> +       if (is_main_worktree(wt))
> +               die(_("'%s' is a main working directory"), av[0]);
> +
> +       old_reason = is_worktree_locked(wt);
> +       if (old_reason) {
> +               if (*old_reason)
> +                       die(_("already locked, reason: %s"), old_reason);
> +               die(_("already locked, no reason"));
> +       }

As a convenience, would it make sense to allow a worktree to be
re-locked with a different reason rather than erroring out?

Also, the "no reason" in the error message might be confusing. Perhaps
it would be better to say merely "already locked" in this case.

> +       write_file(git_common_path("worktrees/%s/locked", wt->id),
> +                  "%s", reason);
> +       return 0;
> +}

This is a tangent, but it would be nice for the "git worktree list"
command to show whether a worktree is locked, and the reason (if
available), both in pretty and porcelain formats. (That was another
reason why I suggested to Mike, back when he was adding the "list"
command, that 'struct worktree' should have a 'locked' field and
get_worktrees() should populate it automatically.)

> diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
> @@ -0,0 +1,34 @@
> +#!/bin/sh
> +
> +test_description='test git worktree move, remove, lock and unlock'
> +
> +. ./test-lib.sh
> +
> +test_expect_success 'setup' '
> +       test_commit init &&
> +       git worktree add source &&
> +       git worktree list --porcelain | grep "^worktree" >actual &&
> +       cat <<-EOF >expected &&
> +       worktree $TRASH_DIRECTORY
> +       worktree $TRASH_DIRECTORY/source
> +       EOF
> +       test_cmp expected actual
> +'
> +
> +test_expect_success 'lock main worktree' '
> +       test_must_fail git worktree lock .
> +'
> +
> +test_expect_success 'lock linked worktree' '
> +       git worktree lock --reason hahaha source &&
> +       echo hahaha >expected &&
> +       test_cmp expected .git/worktrees/source/locked
> +'

Would it make sense to also add tests of locking from within the
worktree being locked, and from within a worktree other than the one
being locked (and other than the main one)?

> +test_expect_success 'lock worktree twice' '
> +       test_must_fail git worktree lock source &&
> +       echo hahaha >expected &&
> +       test_cmp expected .git/worktrees/source/locked
> +'
> +
> +test_done

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 4/5] worktree: add "lock" command
  2016-05-10 14:17                     ` [PATCH 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
  2016-05-16  0:09                       ` Eric Sunshine
@ 2016-05-16  0:40                       ` Eric Sunshine
  1 sibling, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-16  0:40 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:17 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> +static int lock_worktree(int ac, const char **av, const char *prefix)
> +{
> +       const char *reason = "", *old_reason;
> +       struct option options[] = {
> +               OPT_STRING(0, "reason", &reason, N_("string"),
> +                          N_("reason for locking")),
> +               OPT_END()
> +       };
> +       struct worktree **worktrees, *wt;
> +       struct strbuf dst = STRBUF_INIT;
> +
> +       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
> +       if (ac != 1)
> +               usage_with_options(worktree_usage, options);
> +
> +       strbuf_addstr(&dst, prefix_filename(prefix,
> +                                           strlen(prefix),
> +                                           av[0]));
> +
> +       worktrees = get_worktrees();
> +       wt = find_worktree_by_path(worktrees, dst.buf);

Oh, I forgot to mention in my previous email that this is leaking
'strbuf dst'. It could be released right here at its point of final
use.

> +       if (!wt)
> +               die(_("'%s' is not a working directory"), av[0]);
> +       if (is_main_worktree(wt))
> +               die(_("'%s' is a main working directory"), av[0]);
> +
> +       old_reason = is_worktree_locked(wt);
> +       if (old_reason) {
> +               if (*old_reason)
> +                       die(_("already locked, reason: %s"), old_reason);
> +               die(_("already locked, no reason"));
> +       }
> +
> +       write_file(git_common_path("worktrees/%s/locked", wt->id),
> +                  "%s", reason);
> +       return 0;
> +}

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 5/5] worktree: add "unlock" command
  2016-05-10 14:17                     ` [PATCH 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-05-16  0:42                       ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-16  0:42 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Tue, May 10, 2016 at 10:17 AM, Nguyễn Thái Ngọc Duy
<pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/builtin/worktree.c b/builtin/worktree.c
> @@ -498,6 +499,34 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
> +static int unlock_worktree(int ac, const char **av, const char *prefix)
> +{
> +       struct option options[] = {
> +               OPT_END()
> +       };
> +       struct worktree **worktrees, *wt;
> +       struct strbuf dst = STRBUF_INIT;
> +
> +       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
> +       if (ac != 1)
> +               usage_with_options(worktree_usage, options);
> +
> +       strbuf_addstr(&dst, prefix_filename(prefix,
> +                                           strlen(prefix),
> +                                           av[0]));
> +
> +       worktrees = get_worktrees();
> +       wt = find_worktree_by_path(worktrees, dst.buf);

strbuf_release(&dst);

> +       if (!wt)
> +               die(_("'%s' is not a working directory"), av[0]);
> +       if (is_main_worktree(wt))
> +               die(_("'%s' is a main working directory"), av[0]);
> +       if (!is_worktree_locked(wt))
> +               die(_("not locked"));
> +
> +       return unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
> +}

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 5/7] worktree.c: add clear_worktree()
  2016-05-11  6:36                       ` Eric Sunshine
  2016-05-11  6:42                         ` Eric Sunshine
@ 2016-05-22  8:58                         ` Duy Nguyen
  1 sibling, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-05-22  8:58 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Wed, May 11, 2016 at 1:36 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Tue, May 10, 2016 at 10:15 AM, Nguyễn Thái Ngọc Duy
> <pclouds@gmail.com> wrote:
>> The use case is keep some worktree and discard the rest of the worktree
>> list.
>
> So, you're saying that rather than a client freeing the entire
> worktree list like this:
>
>     free_worktrees(worktrees);
>
> it might instead want to keep some element ('n') and free all others
> plus the list itself, like this:
>
>     struct worktree *keep = worktrees[n];
>     struct worktree **p = worktrees;
>     for (; *p; p++)
>         if (*p != keep)
>             clear_worktree(*p);
>     free(worktrees);
>
> Is that correct?
>
> If so, then doesn't this require the client to have far too intimate
> knowledge of the internals of free_worktrees(). Or, was your idea that
> the client would simply leak the 'worktrees' array after freeing the
> unwanted elements?

I looked back in my trees to see why I needed it in the first place.
It turns out my explanation was a lie. I needed it for refactoring
builtin/worktree.c:add_worktree(), where I initialized struct worktree
separately, not from get_worktrees() [1]. Because it's not clear of
the exact use case for this commit, and because that commit I
mentioned may take even longer time to be here (it adds "git worktree
move --repository", a lot more complex operation than a "git worktree
move"), I'll take this commit out of the series for now.

[1] https://github.com/pclouds/git/commit/528d81ce5609b5fbbe1ba193389f7aeecc83d992

> In either case, a cleaner approach might be to provide a function for
> copying a worktree element. Perhaps:
>
>     struct worktree *copy_worktree(const struct worktree *);
>
> or something?
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update
  2016-05-10 14:14                 ` Duy Nguyen
                                     ` (3 preceding siblings ...)
  2016-05-10 23:03                   ` Junio C Hamano
@ 2016-05-22  9:33                   ` Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 1/6] completion: support git-worktree Nguyễn Thái Ngọc Duy
                                       ` (6 more replies)
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
  5 siblings, 7 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22  9:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine, Nguyễn Thái Ngọc Duy

This is mostly commit message update plus

 - dropping "--force" from the completion patch (a comment from Szeder
   Gabor in a previous attempt of worktree completion support).
 
 - the removal of clear_worktree() patch.

I keep the completion patch anyway even though two versions of it were
posted by Eric and Szeder (and didn't get discussed much) because I
needed this for new commands and because I didn't aim high. It's meant
to provide very basic completion support. Fancy stuff like ref
completion can be added later by people who are more experienced in
bash completion stuff.

Anyway, interdiff

-- 8< --
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d3ac391..951a186 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2604,7 +2604,7 @@ _git_worktree ()
 	else
 		case "$subcommand,$cur" in
 		add,--*)
-			__gitcomp "--detach --force"
+			__gitcomp "--detach"
 			;;
 		list,--*)
 			__gitcomp "--porcelain"
diff --git a/worktree.c b/worktree.c
index 335c58f..f4a4f38 100644
--- a/worktree.c
+++ b/worktree.c
@@ -5,22 +5,14 @@
 #include "dir.h"
 #include "wt-status.h"
 
-void clear_worktree(struct worktree *wt)
-{
-	if (!wt)
-		return;
-	free(wt->path);
-	free(wt->id);
-	free(wt->head_ref);
-	memset(wt, 0, sizeof(*wt));
-}
-
 void free_worktrees(struct worktree **worktrees)
 {
 	int i = 0;
 
 	for (i = 0; worktrees[i]; i++) {
-		clear_worktree(worktrees[i]);
+		free(worktrees[i]->path);
+		free(worktrees[i]->id);
+		free(worktrees[i]->head_ref);
 		free(worktrees[i]);
 	}
 	free (worktrees);
diff --git a/worktree.h b/worktree.h
index 7430a4e..1394909 100644
--- a/worktree.h
+++ b/worktree.h
@@ -29,11 +29,6 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
-/*
- * Free up the memory for worktree
- */
-extern void clear_worktree(struct worktree *);
-
 /*
  * Free up the memory for worktree(s)
  */
-- 8< --

Nguyễn Thái Ngọc Duy (6):
  completion: support git-worktree
  worktree.c: rewrite mark_current_worktree() to avoid strbuf
  git-worktree.txt: keep subcommand listing in alphabetical order
  worktree.c: use is_dot_or_dotdot()
  worktree: avoid 0{40}, too many zeroes, hard to read
  worktree: simplify prefixing paths

 Documentation/git-worktree.txt         | 10 +++++-----
 builtin/worktree.c                     | 10 ++++++----
 contrib/completion/git-completion.bash | 23 +++++++++++++++++++++++
 worktree.c                             | 18 ++++++++----------
 4 files changed, 42 insertions(+), 19 deletions(-)

-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 1/6] completion: support git-worktree
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
@ 2016-05-22  9:33                     ` Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 2/6] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
                                       ` (5 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22  9:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine, Nguyễn Thái Ngọc Duy

This adds bare-bone completion support for git-worktree. More advanced
completion (e.g. ref completion in git-worktree-add) can be added later.

--force completion in "worktree add" is left out because that option
should be handled with care.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 contrib/completion/git-completion.bash | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 3402475..951a186 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2595,6 +2595,29 @@ _git_whatchanged ()
 	_git_log
 }
 
+_git_worktree ()
+{
+	local subcommands="add list prune"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+	else
+		case "$subcommand,$cur" in
+		add,--*)
+			__gitcomp "--detach"
+			;;
+		list,--*)
+			__gitcomp "--porcelain"
+			;;
+		prune,--*)
+			__gitcomp "--dry-run --expire --verbose"
+			;;
+		*)
+			;;
+		esac
+	fi
+}
+
 __git_main ()
 {
 	local i c=1 command __git_dir
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 2/6] worktree.c: rewrite mark_current_worktree() to avoid strbuf
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 1/6] completion: support git-worktree Nguyễn Thái Ngọc Duy
@ 2016-05-22  9:33                     ` Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 3/6] git-worktree.txt: keep subcommand listing in alphabetical order Nguyễn Thái Ngọc Duy
                                       ` (4 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22  9:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine, Nguyễn Thái Ngọc Duy

strbuf is a bit overkill for this function. What we need is to call
absolute_path() twice and make sure the second call does not destroy the
result of the first. One buffer allocation is enough.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/worktree.c b/worktree.c
index 4817d60..6a11611 100644
--- a/worktree.c
+++ b/worktree.c
@@ -153,21 +153,19 @@ done:
 
 static void mark_current_worktree(struct worktree **worktrees)
 {
-	struct strbuf git_dir = STRBUF_INIT;
-	struct strbuf path = STRBUF_INIT;
+	char *git_dir = xstrdup(absolute_path(get_git_dir()));
 	int i;
 
-	strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
 	for (i = 0; worktrees[i]; i++) {
 		struct worktree *wt = worktrees[i];
-		strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
-		wt->is_current = !fspathcmp(git_dir.buf, path.buf);
-		strbuf_reset(&path);
-		if (wt->is_current)
+		const char *wt_git_dir = get_worktree_git_dir(wt);
+
+		if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
+			wt->is_current = 1;
 			break;
+		}
 	}
-	strbuf_release(&git_dir);
-	strbuf_release(&path);
+	free(git_dir);
 }
 
 struct worktree **get_worktrees(void)
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 3/6] git-worktree.txt: keep subcommand listing in alphabetical order
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 1/6] completion: support git-worktree Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 2/6] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
@ 2016-05-22  9:33                     ` Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 4/6] worktree.c: use is_dot_or_dotdot() Nguyễn Thái Ngọc Duy
                                       ` (3 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22  9:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine, Nguyễn Thái Ngọc Duy

This is probably not the best order. But it makes it no-brainer to know
where to insert new commands. At some point we might want to reorder at
least the synopsis part again, grouping commonly use subcommands together.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt | 10 +++++-----
 builtin/worktree.c             |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index c622345..27feff6 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,8 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
-'git worktree prune' [-n] [-v] [--expire <expire>]
 'git worktree list' [--porcelain]
+'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
 -----------
@@ -54,10 +54,6 @@ If `<branch>` is omitted and neither `-b` nor `-B` nor `--detached` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
 
-prune::
-
-Prune working tree information in $GIT_DIR/worktrees.
-
 list::
 
 List details of each worktree.  The main worktree is listed first, followed by
@@ -65,6 +61,10 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+prune::
+
+Prune working tree information in $GIT_DIR/worktrees.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 12c0af7..bf80111 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -13,8 +13,8 @@
 
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<branch>]"),
-	N_("git worktree prune [<options>]"),
 	N_("git worktree list [<options>]"),
+	N_("git worktree prune [<options>]"),
 	NULL
 };
 
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 4/6] worktree.c: use is_dot_or_dotdot()
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
                                       ` (2 preceding siblings ...)
  2016-05-22  9:33                     ` [PATCH v2 3/6] git-worktree.txt: keep subcommand listing in alphabetical order Nguyễn Thái Ngọc Duy
@ 2016-05-22  9:33                     ` Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 5/6] worktree: avoid 0{40}, too many zeroes, hard to read Nguyễn Thái Ngọc Duy
                                       ` (2 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22  9:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/worktree.c | 2 +-
 worktree.c         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index bf80111..aaee0e2 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -95,7 +95,7 @@ static void prune_worktrees(void)
 	if (!dir)
 		return;
 	while ((d = readdir(dir)) != NULL) {
-		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+		if (is_dot_or_dotdot(d->d_name))
 			continue;
 		strbuf_reset(&reason);
 		if (!prune_worktree(d->d_name, &reason))
diff --git a/worktree.c b/worktree.c
index 6a11611..f4a4f38 100644
--- a/worktree.c
+++ b/worktree.c
@@ -187,7 +187,7 @@ struct worktree **get_worktrees(void)
 	if (dir) {
 		while ((d = readdir(dir)) != NULL) {
 			struct worktree *linked = NULL;
-			if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+			if (is_dot_or_dotdot(d->d_name))
 				continue;
 
 			if ((linked = get_linked_worktree(d->d_name))) {
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 5/6] worktree: avoid 0{40}, too many zeroes, hard to read
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
                                       ` (3 preceding siblings ...)
  2016-05-22  9:33                     ` [PATCH v2 4/6] worktree.c: use is_dot_or_dotdot() Nguyễn Thái Ngọc Duy
@ 2016-05-22  9:33                     ` Nguyễn Thái Ngọc Duy
  2016-05-22  9:33                     ` [PATCH v2 6/6] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
  2016-05-23  1:09                     ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Eric Sunshine
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22  9:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/worktree.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index aaee0e2..b53f802 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -262,7 +262,7 @@ static int add_worktree(const char *path, const char *refname,
 	 */
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-	write_file(sb.buf, "0000000000000000000000000000000000000000");
+	write_file(sb.buf, sha1_to_hex(null_sha1));
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
 	write_file(sb.buf, "../..");
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 6/6] worktree: simplify prefixing paths
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
                                       ` (4 preceding siblings ...)
  2016-05-22  9:33                     ` [PATCH v2 5/6] worktree: avoid 0{40}, too many zeroes, hard to read Nguyễn Thái Ngọc Duy
@ 2016-05-22  9:33                     ` Nguyễn Thái Ngọc Duy
  2016-05-22 23:32                       ` Eric Sunshine
  2016-05-23  1:09                     ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Eric Sunshine
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22  9:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine, Nguyễn Thái Ngọc Duy

This also makes slash conversion always happen on Windows (a side effect
of prefix_filename). Which is a good thing.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/worktree.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index b53f802..f9dac37 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -337,7 +337,7 @@ static int add(int ac, const char **av, const char *prefix)
 	if (ac < 1 || ac > 2)
 		usage_with_options(worktree_usage, options);
 
-	path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
+	path = prefix_filename(prefix, strlen(prefix), av[0]);
 	branch = ac < 2 ? "HEAD" : av[1];
 
 	opts.force_new_branch = !!new_branch_force;
@@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 
 	if (ac < 2)
 		usage_with_options(worktree_usage, options);
+	if (!prefix)
+		prefix = "";
 	if (!strcmp(av[1], "add"))
 		return add(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "prune"))
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH 3/5] worktree.c: add is_worktree_locked()
  2016-05-13 16:52                       ` Eric Sunshine
@ 2016-05-22  9:53                         ` Duy Nguyen
  2016-05-23  2:04                           ` Eric Sunshine
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-05-22  9:53 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Fri, May 13, 2016 at 11:52 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> Actually, I recall that when I suggested the idea of 'struct worktree'
> and get_worktrees() to Mike that it would be natural for the structure
> to have a 'locked' (or 'locked_reason') field, in which case the
> reason could be stored there instead of in this static strbuf. In
> fact, my idea at the time was that get_worktrees() would populate that
> field automatically, rather than having to do so via a separate
> on-demand function call as in this patch.

I'm keeping this as a separate function for now. I don't like
get_worktrees() doing extra work unless it has to. We soon will see
the complete picture of "git worktree" then we can merge it back to
get_worktrees() if it turns out checking "locked" is the common
operation. is_worktree_locked() then may become a thin wrapper to
access this "locked" field.

>> +extern const char *is_worktree_locked(const struct worktree *wt);
>
> I was wondering if builtin/worktree.c:prune_worktree() should be
> updated to invoke this new function instead of consulting
> "worktrees/<id>/locked" manually, but I see that the entire "prune
> worktrees" functionality in builting/worktree.c first needs to be
> updated to the get_worktrees() API for that to happen.

I thought about updating prune too. But it is in a bit special
situation where it may need to consider invalid (or partly invalid)
worktrees as well. So far worktree api is about valid worktrees only
if I'm not mistaken and we probably should keep it that way, otherwise
all callers may need to check "is this worktree valid" all over the
place.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 4/5] worktree: add "lock" command
  2016-05-16  0:09                       ` Eric Sunshine
@ 2016-05-22 10:31                         ` Duy Nguyen
  2016-05-23  4:21                           ` Eric Sunshine
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-05-22 10:31 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

(the answer to rest of your questions is "yes you're right, will fix"
or something along that line so I will not quote them here)

On Mon, May 16, 2016 at 7:09 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>> +       old_reason = is_worktree_locked(wt);
>> +       if (old_reason) {
>> +               if (*old_reason)
>> +                       die(_("already locked, reason: %s"), old_reason);
>> +               die(_("already locked, no reason"));
>> +       }
>
> As a convenience, would it make sense to allow a worktree to be
> re-locked with a different reason rather than erroring out?

For now I think the user can just unlock then relock, after examining
the previous reason and maybe incorporating it to the new reason. Yes
there is a race but for these operations I don't think it really
matters. If it's done often enough, we can lift this restriction.

>> +       write_file(git_common_path("worktrees/%s/locked", wt->id),
>> +                  "%s", reason);
>> +       return 0;
>> +}
>
> This is a tangent, but it would be nice for the "git worktree list"
> command to show whether a worktree is locked, and the reason (if
> available), both in pretty and porcelain formats. (That was another
> reason why I suggested to Mike, back when he was adding the "list"
> command, that 'struct worktree' should have a 'locked' field and
> get_worktrees() should populate it automatically.)

Yes. And a good reason for get_worktrees() to read lock status
automatically too. I will not do it right away though or at least
until after I'm done with move/remove and the shared config file
problem.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v2 0/5] worktree lock/unlock
  2016-05-10 14:14                 ` Duy Nguyen
                                     ` (4 preceding siblings ...)
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
@ 2016-05-22 10:43                   ` Nguyễn Thái Ngọc Duy
  2016-05-22 10:43                     ` [PATCH v2 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
                                       ` (6 more replies)
  5 siblings, 7 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22 10:43 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This should address all of Eric's comments (thanks!). An extra change
I made is free_worktrees() at the end of {,un}lock_worktree() to avoid
leaking. This series depends on nd/worktree-cleanup-post-head-protection.

Interdiff
-- 8< --
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 9ac1129..e0f2605 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -40,9 +40,8 @@ section "DETAILS" for more information.
 
 If a linked working tree is stored on a portable device or network share
 which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'locked' alongside the other
-administrative files, optionally containing a plain text reason that
-pruning should be suppressed. See section "DETAILS" for more information.
+being pruned by issuing the `git worktree lock` command, optionally
+specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
@@ -65,9 +64,11 @@ bare, the revision currently checked out, and the branch currently checked out
 
 lock::
 
-When a worktree is locked, it cannot be pruned, moved or deleted. For
-example, if the worktree is on portable device that is not available
-when "git worktree <command>" is executed.
+If a working tree is on a portable device or network share which
+is not always mounted, lock it to prevent its administrative
+files from being pruned automatically. This also prevents it from
+being moved or deleted. Optionally, specify a reason for the lock
+with `--reason`.
 
 prune::
 
@@ -123,7 +124,7 @@ OPTIONS
 	With `prune`, only expire unused working trees older than <time>.
 
 --reason <string>:
-	An explanation why the worktree is locked.
+	With `lock`, an explanation why the worktree is locked.
 
 DETAILS
 -------
@@ -165,7 +166,8 @@ instead.
 
 To prevent a $GIT_DIR/worktrees entry from being pruned (which
 can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), add a file named
+entry's working tree is stored on a portable device), use the
+`git worktree lock` comamnd, which adds a file named
 'locked' to the entry's directory. The file contains the reason in
 plain text. For example, if a linked working tree's `.git` file points
 to `/path/main/.git/worktrees/test-next` then a file named
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 53e5f5a..da9f771 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -482,6 +482,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 
 	worktrees = get_worktrees();
 	wt = find_worktree_by_path(worktrees, dst.buf);
+	strbuf_release(&dst);
 	if (!wt)
 		die(_("'%s' is not a working directory"), av[0]);
 	if (is_main_worktree(wt))
@@ -491,11 +492,12 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 	if (old_reason) {
 		if (*old_reason)
 			die(_("already locked, reason: %s"), old_reason);
-		die(_("already locked, no reason"));
+		die(_("already locked"));
 	}
 
 	write_file(git_common_path("worktrees/%s/locked", wt->id),
 		   "%s", reason);
+	free_worktrees(worktrees);
 	return 0;
 }
 
@@ -506,6 +508,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
 	};
 	struct worktree **worktrees, *wt;
 	struct strbuf dst = STRBUF_INIT;
+	int ret;
 
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac != 1)
@@ -517,14 +520,16 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
 
 	worktrees = get_worktrees();
 	wt = find_worktree_by_path(worktrees, dst.buf);
+	strbuf_release(&dst);
 	if (!wt)
 		die(_("'%s' is not a working directory"), av[0]);
 	if (is_main_worktree(wt))
 		die(_("'%s' is a main working directory"), av[0]);
 	if (!is_worktree_locked(wt))
 		die(_("not locked"));
-
-	return unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+	free_worktrees(worktrees);
+	return ret;
 }
 
 int cmd_worktree(int ac, const char **av, const char *prefix)
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index f4b2816..1927537 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -25,12 +25,32 @@ test_expect_success 'lock linked worktree' '
 	test_cmp expected .git/worktrees/source/locked
 '
 
+test_expect_success 'lock linked worktree from another worktree' '
+	rm .git/worktrees/source/locked &&
+	git worktree add elsewhere &&
+	(
+		cd elsewhere &&
+		git worktree lock --reason hahaha ../source
+	) &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
 test_expect_success 'lock worktree twice' '
 	test_must_fail git worktree lock source &&
 	echo hahaha >expected &&
 	test_cmp expected .git/worktrees/source/locked
 '
 
+test_expect_success 'lock worktree twice (from the locked worktree)' '
+	(
+		cd source &&
+		test_must_fail git worktree lock .
+	) &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
 test_expect_success 'unlock main worktree' '
 	test_must_fail git worktree unlock .
 '
diff --git a/worktree.c b/worktree.c
index a9cfbb3..cb30af3 100644
--- a/worktree.c
+++ b/worktree.c
@@ -218,38 +218,37 @@ struct worktree *find_worktree_by_path(struct worktree **list,
 				       const char *path_)
 {
 	char *path = xstrdup(real_path(path_));
-	struct worktree *wt = NULL;
 
-	while (*list) {
-		wt = *list++;
-		if (!fspathcmp(path, real_path(wt->path)))
+	for (; *list; list++)
+		if (!fspathcmp(path, real_path((*list)->path)))
 			break;
-		wt = NULL;
-	}
 	free(path);
-	return wt;
+	return *list;
 }
 
 int is_main_worktree(const struct worktree *wt)
 {
-	return wt && !wt->id;
+	return !wt->id;
 }
 
 const char *is_worktree_locked(const struct worktree *wt)
 {
 	static struct strbuf sb = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+
+	strbuf_git_common_path(&path, "worktrees/%s/locked", wt->id);
 
-	if (!file_exists(git_common_path("worktrees/%s/locked", wt->id)))
+	if (!file_exists(path.buf)) {
+		strbuf_release(&path);
 		return NULL;
+	}
 
 	strbuf_reset(&sb);
-	if (strbuf_read_file(&sb,
-			     git_common_path("worktrees/%s/locked", wt->id),
-			     0) < 0)
-		die_errno(_("failed to read '%s'"),
-			  git_common_path("worktrees/%s/locked", wt->id));
+	if (strbuf_read_file(&sb, path.buf, 0) < 0)
+		die_errno(_("failed to read '%s'"), path.buf);
+	strbuf_release(&path);
 
-	strbuf_rtrim(&sb);
+	strbuf_trim(&sb);
 	return sb.buf;
 }
 
diff --git a/worktree.h b/worktree.h
index 19a6790..3a780c2 100644
--- a/worktree.h
+++ b/worktree.h
@@ -42,7 +42,7 @@ extern int is_main_worktree(const struct worktree *wt);
 
 /*
  * Return the reason string if the given worktree is locked. Return
- * NULL otherwise.
+ * NULL otherwise. Return value is only valid until the next invocation.
  */
 extern const char *is_worktree_locked(const struct worktree *wt);
-- 8< --

Nguyễn Thái Ngọc Duy (5):
  worktree.c: add find_worktree_by_path()
  worktree.c: add is_main_worktree()
  worktree.c: add is_worktree_locked()
  worktree: add "lock" command
  worktree: add "unlock" command

 Documentation/git-worktree.txt         | 27 +++++++++---
 builtin/worktree.c                     | 77 ++++++++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 ++-
 t/t2028-worktree-move.sh (new +x)      | 68 ++++++++++++++++++++++++++++++
 worktree.c                             | 38 +++++++++++++++++
 worktree.h                             | 17 ++++++++
 6 files changed, 225 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 1/5] worktree.c: add find_worktree_by_path()
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
@ 2016-05-22 10:43                     ` Nguyễn Thái Ngọc Duy
  2016-05-23  1:41                       ` Eric Sunshine
  2016-05-23  4:11                       ` Eric Sunshine
  2016-05-22 10:43                     ` [PATCH v2 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
                                       ` (5 subsequent siblings)
  6 siblings, 2 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22 10:43 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

So far we haven't needed to identify an existing worktree from command
line. Future commands such as lock or move will need it. There are of
course other options for identifying a worktree, for example by branch
or even by internal id. They may be added later if proved useful.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 12 ++++++++++++
 worktree.h |  6 ++++++
 2 files changed, 18 insertions(+)

diff --git a/worktree.c b/worktree.c
index f4a4f38..c5d45a6 100644
--- a/worktree.c
+++ b/worktree.c
@@ -214,6 +214,18 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+struct worktree *find_worktree_by_path(struct worktree **list,
+				       const char *path_)
+{
+	char *path = xstrdup(real_path(path_));
+
+	for (; *list; list++)
+		if (!fspathcmp(path, real_path((*list)->path)))
+			break;
+	free(path);
+	return *list;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 1394909..91627fd 100644
--- a/worktree.h
+++ b/worktree.h
@@ -29,6 +29,12 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
+/*
+ * Search a worktree by its path. Paths are normalized internally.
+ */
+extern struct worktree *find_worktree_by_path(struct worktree **list,
+					      const char *path_);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 2/5] worktree.c: add is_main_worktree()
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
  2016-05-22 10:43                     ` [PATCH v2 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
@ 2016-05-22 10:43                     ` Nguyễn Thái Ngọc Duy
  2016-05-22 10:43                     ` [PATCH v2 3/5] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
                                       ` (4 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22 10:43 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Main worktree _is_ different. You can lock a linked worktree but not the
main one, for example. Provide an API for checking that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 5 +++++
 worktree.h | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/worktree.c b/worktree.c
index c5d45a6..6b3015b 100644
--- a/worktree.c
+++ b/worktree.c
@@ -226,6 +226,11 @@ struct worktree *find_worktree_by_path(struct worktree **list,
 	return *list;
 }
 
+int is_main_worktree(const struct worktree *wt)
+{
+	return !wt->id;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 91627fd..39f1aa2 100644
--- a/worktree.h
+++ b/worktree.h
@@ -35,6 +35,11 @@ extern const char *get_worktree_git_dir(const struct worktree *wt);
 extern struct worktree *find_worktree_by_path(struct worktree **list,
 					      const char *path_);
 
+/*
+ * Return true if the given worktree is the main one.
+ */
+extern int is_main_worktree(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 3/5] worktree.c: add is_worktree_locked()
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
  2016-05-22 10:43                     ` [PATCH v2 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
  2016-05-22 10:43                     ` [PATCH v2 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-22 10:43                     ` Nguyễn Thái Ngọc Duy
  2016-05-22 10:43                     ` [PATCH v2 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
                                       ` (3 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22 10:43 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This provides an API for checking if a worktree is locked. We need to
check this to avoid double locking a worktree, or try to unlock one when
it's not even locked.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 21 +++++++++++++++++++++
 worktree.h |  6 ++++++
 2 files changed, 27 insertions(+)

diff --git a/worktree.c b/worktree.c
index 6b3015b..cb30af3 100644
--- a/worktree.c
+++ b/worktree.c
@@ -231,6 +231,27 @@ int is_main_worktree(const struct worktree *wt)
 	return !wt->id;
 }
 
+const char *is_worktree_locked(const struct worktree *wt)
+{
+	static struct strbuf sb = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+
+	strbuf_git_common_path(&path, "worktrees/%s/locked", wt->id);
+
+	if (!file_exists(path.buf)) {
+		strbuf_release(&path);
+		return NULL;
+	}
+
+	strbuf_reset(&sb);
+	if (strbuf_read_file(&sb, path.buf, 0) < 0)
+		die_errno(_("failed to read '%s'"), path.buf);
+	strbuf_release(&path);
+
+	strbuf_trim(&sb);
+	return sb.buf;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 39f1aa2..3a780c2 100644
--- a/worktree.h
+++ b/worktree.h
@@ -40,6 +40,12 @@ extern struct worktree *find_worktree_by_path(struct worktree **list,
  */
 extern int is_main_worktree(const struct worktree *wt);
 
+/*
+ * Return the reason string if the given worktree is locked. Return
+ * NULL otherwise. Return value is only valid until the next invocation.
+ */
+extern const char *is_worktree_locked(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 4/5] worktree: add "lock" command
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                       ` (2 preceding siblings ...)
  2016-05-22 10:43                     ` [PATCH v2 3/5] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
@ 2016-05-22 10:43                     ` Nguyễn Thái Ngọc Duy
  2016-05-23  4:36                       ` Eric Sunshine
  2016-05-22 10:43                     ` [PATCH v2 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
                                       ` (2 subsequent siblings)
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22 10:43 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         | 22 ++++++++++----
 builtin/worktree.c                     | 43 +++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 +++-
 t/t2028-worktree-move.sh (new +x)      | 54 ++++++++++++++++++++++++++++++++++
 4 files changed, 117 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 27feff6..004d770 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
 'git worktree list' [--porcelain]
+'git worktree lock' [--reason <string>] <path>
 'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
@@ -38,9 +39,8 @@ section "DETAILS" for more information.
 
 If a linked working tree is stored on a portable device or network share
 which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'locked' alongside the other
-administrative files, optionally containing a plain text reason that
-pruning should be suppressed. See section "DETAILS" for more information.
+being pruned by issuing the `git worktree lock` command, optionally
+specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
@@ -61,6 +61,14 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+lock::
+
+If a working tree is on a portable device or network share which
+is not always mounted, lock it to prevent its administrative
+files from being pruned automatically. This also prevents it from
+being moved or deleted. Optionally, specify a reason for the lock
+with `--reason`.
+
 prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
@@ -110,6 +118,9 @@ OPTIONS
 --expire <time>::
 	With `prune`, only expire unused working trees older than <time>.
 
+--reason <string>:
+	With `lock`, an explanation why the worktree is locked.
+
 DETAILS
 -------
 Each linked working tree has a private sub-directory in the repository's
@@ -150,7 +161,8 @@ instead.
 
 To prevent a $GIT_DIR/worktrees entry from being pruned (which
 can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), add a file named
+entry's working tree is stored on a portable device), use the
+`git worktree lock` comamnd, which adds a file named
 'locked' to the entry's directory. The file contains the reason in
 plain text. For example, if a linked working tree's `.git` file points
 to `/path/main/.git/worktrees/test-next` then a file named
@@ -226,8 +238,6 @@ performed manually, such as:
 - `remove` to remove a linked working tree and its administrative files (and
   warn if the working tree is dirty)
 - `mv` to move or rename a working tree and update its administrative files
-- `lock` to prevent automatic pruning of administrative files (for instance,
-  for a working tree on a portable device)
 
 GIT
 ---
diff --git a/builtin/worktree.c b/builtin/worktree.c
index f9dac37..9008dcd 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -14,6 +14,7 @@
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<branch>]"),
 	N_("git worktree list [<options>]"),
+	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
 	NULL
 };
@@ -459,6 +460,46 @@ static int list(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+	const char *reason = "", *old_reason;
+	struct option options[] = {
+		OPT_STRING(0, "reason", &reason, N_("string"),
+			   N_("reason for locking")),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	struct strbuf dst = STRBUF_INIT;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	strbuf_addstr(&dst, prefix_filename(prefix,
+					    strlen(prefix),
+					    av[0]));
+
+	worktrees = get_worktrees();
+	wt = find_worktree_by_path(worktrees, dst.buf);
+	strbuf_release(&dst);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+
+	old_reason = is_worktree_locked(wt);
+	if (old_reason) {
+		if (*old_reason)
+			die(_("already locked, reason: %s"), old_reason);
+		die(_("already locked"));
+	}
+
+	write_file(git_common_path("worktrees/%s/locked", wt->id),
+		   "%s", reason);
+	free_worktrees(worktrees);
+	return 0;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -475,5 +516,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return prune(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "list"))
 		return list(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "lock"))
+		return lock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 951a186..f88727d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list prune"
+	local subcommands="add list lock prune"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
@@ -2609,6 +2609,9 @@ _git_worktree ()
 		list,--*)
 			__gitcomp "--porcelain"
 			;;
+		lock,--*)
+			__gitcomp "--reason"
+			;;
 		prune,--*)
 			__gitcomp "--dry-run --expire --verbose"
 			;;
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
new file mode 100755
index 0000000..aeac848
--- /dev/null
+++ b/t/t2028-worktree-move.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+test_description='test git worktree move, remove, lock and unlock'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init &&
+	git worktree add source &&
+	git worktree list --porcelain | grep "^worktree" >actual &&
+	cat <<-EOF >expected &&
+	worktree $TRASH_DIRECTORY
+	worktree $TRASH_DIRECTORY/source
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'lock main worktree' '
+	test_must_fail git worktree lock .
+'
+
+test_expect_success 'lock linked worktree' '
+	git worktree lock --reason hahaha source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock linked worktree from another worktree' '
+	rm .git/worktrees/source/locked &&
+	git worktree add elsewhere &&
+	(
+		cd elsewhere &&
+		git worktree lock --reason hahaha ../source
+	) &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice' '
+	test_must_fail git worktree lock source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice (from the locked worktree)' '
+	(
+		cd source &&
+		test_must_fail git worktree lock .
+	) &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v2 5/5] worktree: add "unlock" command
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                       ` (3 preceding siblings ...)
  2016-05-22 10:43                     ` [PATCH v2 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-05-22 10:43                     ` Nguyễn Thái Ngọc Duy
  2016-05-23  4:39                       ` Eric Sunshine
  2016-05-23  4:51                     ` [PATCH v2 0/5] worktree lock/unlock Eric Sunshine
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-22 10:43 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         |  5 +++++
 builtin/worktree.c                     | 34 ++++++++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  2 +-
 t/t2028-worktree-move.sh               | 14 ++++++++++++++
 4 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 004d770..e0f2605 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <path>
 'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree unlock' <path>
 
 DESCRIPTION
 -----------
@@ -73,6 +74,10 @@ prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
 
+unlock::
+
+Unlock a worktree, allowing it to be pruned, moved or deleted.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 9008dcd..da9f771 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -16,6 +16,7 @@ static const char * const worktree_usage[] = {
 	N_("git worktree list [<options>]"),
 	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
+	N_("git worktree unlock <path>"),
 	NULL
 };
 
@@ -500,6 +501,37 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	struct strbuf dst = STRBUF_INIT;
+	int ret;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	strbuf_addstr(&dst, prefix_filename(prefix,
+					    strlen(prefix),
+					    av[0]));
+
+	worktrees = get_worktrees();
+	wt = find_worktree_by_path(worktrees, dst.buf);
+	strbuf_release(&dst);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+	if (!is_worktree_locked(wt))
+		die(_("not locked"));
+	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+	free_worktrees(worktrees);
+	return ret;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -518,5 +550,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return list(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "lock"))
 		return lock_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "unlock"))
+		return unlock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index f88727d..0e3841d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list lock prune"
+	local subcommands="add list lock prune unlock"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index aeac848..1927537 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -51,4 +51,18 @@ test_expect_success 'lock worktree twice (from the locked worktree)' '
 	test_cmp expected .git/worktrees/source/locked
 '
 
+test_expect_success 'unlock main worktree' '
+	test_must_fail git worktree unlock .
+'
+
+test_expect_success 'unlock linked worktree' '
+	git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
+test_expect_success 'unlock worktree twice' '
+	test_must_fail git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
 test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 6/6] worktree: simplify prefixing paths
  2016-05-22  9:33                     ` [PATCH v2 6/6] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
@ 2016-05-22 23:32                       ` Eric Sunshine
  2016-05-23  4:31                         ` Eric Sunshine
  0 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-05-22 23:32 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano

On Sun, May 22, 2016 at 5:33 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> This also makes slash conversion always happen on Windows (a side effect
> of prefix_filename). Which is a good thing.

Selling this patch as a mere simplification seems misguided
(especially as it's subjective whether this is indeed simpler). I'd
find it more easy to buy this change if it was described primarily as
benefiting Windows, in the same vein as[1].

More below...

[1]: http://git.661346.n2.nabble.com/PATCH-Do-not-skip-prefix-filename-when-there-is-no-prefix-tp7657038.html

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/builtin/worktree.c b/builtin/worktree.c
> @@ -337,7 +337,7 @@ static int add(int ac, const char **av, const char *prefix)
>         if (ac < 1 || ac > 2)
>                 usage_with_options(worktree_usage, options);
>
> -       path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
> +       path = prefix_filename(prefix, strlen(prefix), av[0]);
>         branch = ac < 2 ? "HEAD" : av[1];
>
>         opts.force_new_branch = !!new_branch_force;
> @@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
>
>         if (ac < 2)
>                 usage_with_options(worktree_usage, options);
> +       if (!prefix)
> +               prefix = "";

Considering that all the other existing git-worktree subcommands
already handle NULL prefix acceptably, it makes me a bit uncomfortable
that this "fix", which could be local to add(), leaks into all the
other subcommands, thus making the assumption that they (or other new
subcommands) will never care about the distinction between NULL and
"".

Not a big objection; just a minor niggle, probably not worth a re-roll.

>         if (!strcmp(av[1], "add"))
>                 return add(ac - 1, av + 1, prefix);
>         if (!strcmp(av[1], "prune"))

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update
  2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
                                       ` (5 preceding siblings ...)
  2016-05-22  9:33                     ` [PATCH v2 6/6] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
@ 2016-05-23  1:09                     ` Eric Sunshine
  6 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  1:09 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano

On Sun, May 22, 2016 at 5:33 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> This is mostly commit message update plus
>
>  - dropping "--force" from the completion patch (a comment from Szeder
>    Gabor in a previous attempt of worktree completion support).
>
>  - the removal of clear_worktree() patch.
>
> I keep the completion patch anyway even though two versions of it were
> posted by Eric and Szeder (and didn't get discussed much) because I
> needed this for new commands and because I didn't aim high. It's meant
> to provide very basic completion support. Fancy stuff like ref
> completion can be added later by people who are more experienced in
> bash completion stuff.

Thanks. Dropping the clear_worktree() patch does indeed seem like a
good idea. I've re-read the entire series, and aside from my minor
comments on 6/6, everything looks fine and the series is:

    Reviewed by: Eric Sunshine <sunshine@sunshineco.com>

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 1/5] worktree.c: add find_worktree_by_path()
  2016-05-22 10:43                     ` [PATCH v2 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
@ 2016-05-23  1:41                       ` Eric Sunshine
  2016-05-23  4:11                       ` Eric Sunshine
  1 sibling, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  1:41 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Sun, May 22, 2016 at 6:43 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> So far we haven't needed to identify an existing worktree from command
> line. Future commands such as lock or move will need it. There are of
> course other options for identifying a worktree, for example by branch
> or even by internal id. They may be added later if proved useful.
>
> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/worktree.h b/worktree.h
> @@ -29,6 +29,12 @@ extern struct worktree **get_worktrees(void);
> +/*
> + * Search a worktree by its path. Paths are normalized internally.
> + */
> +extern struct worktree *find_worktree_by_path(struct worktree **list,
> +                                             const char *path_);
> +

Sorry for not noticing last time, but I'd probably have named this
argument 'path' rather than 'path_' here in the header.

Not worth a re-roll, of course.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 3/5] worktree.c: add is_worktree_locked()
  2016-05-22  9:53                         ` Duy Nguyen
@ 2016-05-23  2:04                           ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  2:04 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Sun, May 22, 2016 at 5:53 AM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Fri, May 13, 2016 at 11:52 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>> Actually, I recall that when I suggested the idea of 'struct worktree'
>> and get_worktrees() to Mike that it would be natural for the structure
>> to have a 'locked' (or 'locked_reason') field, in which case the
>> reason could be stored there instead of in this static strbuf. In
>> fact, my idea at the time was that get_worktrees() would populate that
>> field automatically, rather than having to do so via a separate
>> on-demand function call as in this patch.
>
> I'm keeping this as a separate function for now. I don't like
> get_worktrees() doing extra work unless it has to. We soon will see
> the complete picture of "git worktree" then we can merge it back to
> get_worktrees() if it turns out checking "locked" is the common
> operation. is_worktree_locked() then may become a thin wrapper to
> access this "locked" field.

is_worktree_locked() could also just go away since the 'struct
worktree::locked' field would be non-NULL for locked, else NULL.

>>> +extern const char *is_worktree_locked(const struct worktree *wt);
>>
>> I was wondering if builtin/worktree.c:prune_worktree() should be
>> updated to invoke this new function instead of consulting
>> "worktrees/<id>/locked" manually, but I see that the entire "prune
>> worktrees" functionality in builting/worktree.c first needs to be
>> updated to the get_worktrees() API for that to happen.
>
> I thought about updating prune too. But it is in a bit special
> situation where it may need to consider invalid (or partly invalid)
> worktrees as well. So far worktree api is about valid worktrees only
> if I'm not mistaken and we probably should keep it that way, otherwise
> all callers may need to check "is this worktree valid" all over the
> place.

Yes and no. In addition to suggesting to Mike that 'struct worktree'
should have a 'locked' field, I also suggested a 'prunable' field
which would indicate whether a worktree was a candidate for pruning.
In fact, the field would be a 'const char *' where non-NULL would mean
prunable and give a reason (one of the reasons already determined by
builtin/worktree.c:prune_worktree()). Alternately it could be an enum.
Like the 'locked' (or 'lock_reason') field, 'prunable' (or
'prune_reason') is likely the sort of information which should be
presented by "git worktree list". And, as with 'locked', I think it
makes sense for the 'prunable' field to be populated automatically by
get_worktrees().

As for your concern about clients having to take care with valid vs.
invalid worktrees, get_worktrees() could be updated to return all
worktrees or only valid worktrees (for some definition of "valid").
"git worktree list" is an example of a client which would want to see
all worktrees assuming its updated to show 'locked' and 'prunable'
status.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 1/5] worktree.c: add find_worktree_by_path()
  2016-05-22 10:43                     ` [PATCH v2 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
  2016-05-23  1:41                       ` Eric Sunshine
@ 2016-05-23  4:11                       ` Eric Sunshine
  2016-05-30  9:56                         ` Duy Nguyen
  1 sibling, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  4:11 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Sun, May 22, 2016 at 6:43 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> So far we haven't needed to identify an existing worktree from command
> line. Future commands such as lock or move will need it. There are of
> course other options for identifying a worktree, for example by branch
> or even by internal id. They may be added later if proved useful.

Beyond the above methods for specifying a worktree, [1] adds
$(basename $path) as a possibility if not ambiguous. Excerpt:

    For git-worktree commands such as "lock", "mv", "remove", it
    likely will be nice to allow people to specify the linked
    worktree not only by path, but also by tag, and possibly even by
    $(basename $path) if not ambiguous.

[1]: http://article.gmane.org/gmane.comp.version-control.git/275528

> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH 4/5] worktree: add "lock" command
  2016-05-22 10:31                         ` Duy Nguyen
@ 2016-05-23  4:21                           ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  4:21 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Sun, May 22, 2016 at 6:31 AM, Duy Nguyen <pclouds@gmail.com> wrote:
> On Mon, May 16, 2016 at 7:09 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>>> +       old_reason = is_worktree_locked(wt);
>>> +       if (old_reason) {
>>> +               if (*old_reason)
>>> +                       die(_("already locked, reason: %s"), old_reason);
>>> +               die(_("already locked, no reason"));
>>> +       }
>>
>> As a convenience, would it make sense to allow a worktree to be
>> re-locked with a different reason rather than erroring out?
>
> For now I think the user can just unlock then relock, after examining
> the previous reason and maybe incorporating it to the new reason. Yes
> there is a race but for these operations I don't think it really
> matters. If it's done often enough, we can lift this restriction.

Agreed. It's easy to imagine someone wanting to change the lock
message to correct a typo, but that's likely a rare occurrence, and
other conceivable reasons for wanting to change the message are
probably even more unusual, thus not worth worrying about now.

>>> +       write_file(git_common_path("worktrees/%s/locked", wt->id),
>>> +                  "%s", reason);
>>> +       return 0;
>>> +}
>>
>> This is a tangent, but it would be nice for the "git worktree list"
>> command to show whether a worktree is locked, and the reason (if
>> available), both in pretty and porcelain formats. (That was another
>> reason why I suggested to Mike, back when he was adding the "list"
>> command, that 'struct worktree' should have a 'locked' field and
>> get_worktrees() should populate it automatically.)
>
> Yes. And a good reason for get_worktrees() to read lock status
> automatically too. I will not do it right away though or at least
> until after I'm done with move/remove and the shared config file
> problem.

For the record (for future readers of this thread), [1] itemized
various useful bits of information that would be handy as fields in
'struct worktree' and populated automatically by get_worktrees(), all
of which (I think) could be nice to have in the output of "git
worktree list". Excerpt:

    For git-worktree commands such as "lock", "mv", "remove", it
    likely will be nice to allow people to specify the linked
    worktree not only by path, but also by tag, and possibly even by
    $(basename $path) if not ambiguous. Therefore, to support such
    usage, at minimum, I think you also want to show the worktree's
    tag (d->d_name) in addition to the path.

    Other information which would be nice to display for each
    worktree (possibly controlled by a --verbose flag):

       * the checked out branch or detached head
       * whether it is locked
            - the lock reason (if available)
            - and whether the worktree is currently accessible
        * whether it can be pruned
            - and the prune reason if so

    The prune reason could be obtained by factoring out the
    reason-determination code from worktree.c:prune_worktree() to
    make it re-usable.

[1]: http://article.gmane.org/gmane.comp.version-control.git/275528

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 6/6] worktree: simplify prefixing paths
  2016-05-22 23:32                       ` Eric Sunshine
@ 2016-05-23  4:31                         ` Eric Sunshine
  2016-05-23  9:26                           ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  4:31 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: Git List, Junio C Hamano

On Sun, May 22, 2016 at 7:32 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sun, May 22, 2016 at 5:33 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> @@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
>>         if (ac < 2)
>>                 usage_with_options(worktree_usage, options);
>> +       if (!prefix)
>> +               prefix = "";
>
> Considering that all the other existing git-worktree subcommands
> already handle NULL prefix acceptably, it makes me a bit uncomfortable
> that this "fix", which could be local to add(), leaks into all the
> other subcommands, thus making the assumption that they (or other new
> subcommands) will never care about the distinction between NULL and
> "".
>
> Not a big objection; just a minor niggle, probably not worth a re-roll.

Okay, I see that you're taking advantage of the prefix="" in patch
5/6, as well, so it's not benefitting only add().

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 4/5] worktree: add "lock" command
  2016-05-22 10:43                     ` [PATCH v2 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-05-23  4:36                       ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  4:36 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Sun, May 22, 2016 at 6:43 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
> @@ -150,7 +161,8 @@ instead.
>
>  To prevent a $GIT_DIR/worktrees entry from being pruned (which
>  can be useful in some situations, such as when the
> -entry's working tree is stored on a portable device), add a file named
> +entry's working tree is stored on a portable device), use the
> +`git worktree lock` comamnd, which adds a file named

s/comamnd/command/

>  'locked' to the entry's directory. The file contains the reason in
>  plain text. For example, if a linked working tree's `.git` file points
>  to `/path/main/.git/worktrees/test-next` then a file named
> diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
> @@ -0,0 +1,54 @@
> +test_expect_success 'lock linked worktree from another worktree' '
> +       rm .git/worktrees/source/locked &&
> +       git worktree add elsewhere &&
> +       (
> +               cd elsewhere &&
> +               git worktree lock --reason hahaha ../source

Could use "git -C elsewhere worktree lock ..." and drop the subshell,
but not worth a re-roll.

> +       ) &&
> +       echo hahaha >expected &&
> +       test_cmp expected .git/worktrees/source/locked
> +'
> +
> +test_expect_success 'lock worktree twice (from the locked worktree)' '
> +       (
> +               cd source &&
> +               test_must_fail git worktree lock .

Likewise, -C.

> +       ) &&
> +       echo hahaha >expected &&
> +       test_cmp expected .git/worktrees/source/locked
> +'

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 5/5] worktree: add "unlock" command
  2016-05-22 10:43                     ` [PATCH v2 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-05-23  4:39                       ` Eric Sunshine
  0 siblings, 0 replies; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  4:39 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Sun, May 22, 2016 at 6:43 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> diff --git a/builtin/worktree.c b/builtin/worktree.c
> @@ -500,6 +501,37 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
> +static int unlock_worktree(int ac, const char **av, const char *prefix)
> +{
> +       [...]
> +       wt = find_worktree_by_path(worktrees, dst.buf);
> +       strbuf_release(&dst);
> +       if (!wt)
> +               die(_("'%s' is not a working directory"), av[0]);
> +       if (is_main_worktree(wt))
> +               die(_("'%s' is a main working directory"), av[0]);
> +       if (!is_worktree_locked(wt))
> +               die(_("not locked"));

Could be _("'%s' is not locked"), but not worth a re-roll.

> +       ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
> +       free_worktrees(worktrees);
> +       return ret;
> +}

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 0/5] worktree lock/unlock
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                       ` (4 preceding siblings ...)
  2016-05-22 10:43                     ` [PATCH v2 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-05-23  4:51                     ` Eric Sunshine
  2016-05-23  9:23                       ` Duy Nguyen
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
  6 siblings, 1 reply; 162+ messages in thread
From: Eric Sunshine @ 2016-05-23  4:51 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Sun, May 22, 2016 at 6:43 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> This should address all of Eric's comments (thanks!). An extra change
> I made is free_worktrees() at the end of {,un}lock_worktree() to avoid
> leaking. This series depends on nd/worktree-cleanup-post-head-protection.

Thanks, this addresses all my comments from the previous round (aside
from the suggestion to add a 'locked' field to 'struct worktree' and
populate it automatically, which you elected to defer for the
present).

I re-read the entire series and, aside from the typo in the
documentation update of patch 4/5, everything looks fine, and the
series is:

    Reviewed-by: Eric Sunshine <sunshine@sunshineco.com>

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 0/5] worktree lock/unlock
  2016-05-23  4:51                     ` [PATCH v2 0/5] worktree lock/unlock Eric Sunshine
@ 2016-05-23  9:23                       ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-05-23  9:23 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Mon, May 23, 2016 at 11:51 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sun, May 22, 2016 at 6:43 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> This should address all of Eric's comments (thanks!). An extra change
>> I made is free_worktrees() at the end of {,un}lock_worktree() to avoid
>> leaking. This series depends on nd/worktree-cleanup-post-head-protection.
>
> Thanks, this addresses all my comments from the previous round (aside
> from the suggestion to add a 'locked' field to 'struct worktree' and
> populate it automatically, which you elected to defer for the
> present).

If there's another re-roll (likely so), I'm tempted to do that too as
it's quite clear now that "locked" belongs in struct worktree.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 6/6] worktree: simplify prefixing paths
  2016-05-23  4:31                         ` Eric Sunshine
@ 2016-05-23  9:26                           ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-05-23  9:26 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List, Junio C Hamano

On Mon, May 23, 2016 at 11:31 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sun, May 22, 2016 at 7:32 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
>> On Sun, May 22, 2016 at 5:33 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>>> @@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
>>>         if (ac < 2)
>>>                 usage_with_options(worktree_usage, options);
>>> +       if (!prefix)
>>> +               prefix = "";
>>
>> Considering that all the other existing git-worktree subcommands
>> already handle NULL prefix acceptably, it makes me a bit uncomfortable
>> that this "fix", which could be local to add(), leaks into all the
>> other subcommands, thus making the assumption that they (or other new
>> subcommands) will never care about the distinction between NULL and
>> "".
>>
>> Not a big objection; just a minor niggle, probably not worth a re-roll.
>
> Okay, I see that you're taking advantage of the prefix="" in patch
> 5/6, as well, so it's not benefitting only add().

Yep. 'add' so far is the only command taking paths. The next four,
lock, unlock, move and delete, all deal with paths and need to deal
with NULL prefix otherwise.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v2 1/5] worktree.c: add find_worktree_by_path()
  2016-05-23  4:11                       ` Eric Sunshine
@ 2016-05-30  9:56                         ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-05-30  9:56 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git List, Junio C Hamano, Reto Hablützel, Mike Rappazzo

On Mon, May 23, 2016 at 11:11 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sun, May 22, 2016 at 6:43 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> So far we haven't needed to identify an existing worktree from command
>> line. Future commands such as lock or move will need it. There are of
>> course other options for identifying a worktree, for example by branch
>> or even by internal id. They may be added later if proved useful.
>
> Beyond the above methods for specifying a worktree, [1] adds
> $(basename $path) as a possibility if not ambiguous. Excerpt:
>
>     For git-worktree commands such as "lock", "mv", "remove", it
>     likely will be nice to allow people to specify the linked
>     worktree not only by path, but also by tag, and possibly even by
>     $(basename $path) if not ambiguous.

Thanks. This changes things. Turns out my "find_by_path" API  cannot
be easily extended to support identifying worktrees some other way. I
will need to pass the command line argument as-is, so that this
"find_worktree" (by any means) has enough info to decide (especially
about ambiguation).
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v3 0/6] worktree lock/unlock
  2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                       ` (5 preceding siblings ...)
  2016-05-23  4:51                     ` [PATCH v2 0/5] worktree lock/unlock Eric Sunshine
@ 2016-05-30 10:49                     ` Nguyễn Thái Ngọc Duy
  2016-05-30 10:49                       ` [PATCH v3 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
                                         ` (6 more replies)
  6 siblings, 7 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-30 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

v3 changes almost all patches so I didn't bother with interdiff

  [1/6] worktree.c: add find_worktree()

    This replaces the old find_wortree_by_path(). It now takes both prefix
    and command line argument so it can do some intelligent things with
    them if needed.

  [2/6] worktree.c: find_worktree() learns to identify worktrees by basename

    This is from Eric's suggestion. Here we only care about the "argument"
    argument and ignore the "prefix" argument from 1/6

  [3/6] worktree.c: add is_main_worktree()
 
    This is the only unchanged patch since v2!

  [4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees()

    This used to be is_worktree_locked()

  [5/6] worktree: add "lock" command

    Besides Eric's comments in v2, the syntax line "worktree lock <path>"
    is changed to "worktree lock <worktree>", where <worktree> could be
    either a path, or a base name, or something more in future. I'm aware
    that we don't like showing "worktrees" in user documents, favoring
    working trees. But I think <worktree> is ok in this case.

  [6/6] worktree: add "unlock" command

    Pretty much addressing comments from v2 and some more changes because
    of the new 1/6 and 4/6.

 Documentation/git-worktree.txt         | 33 +++++++++++++----
 builtin/worktree.c                     | 66 ++++++++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 ++-
 t/t2028-worktree-move.sh (new +x)      | 62 ++++++++++++++++++++++++++++++++
 worktree.c                             | 53 +++++++++++++++++++++++++++
 worktree.h                             | 14 ++++++++
 6 files changed, 226 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

-- 
2.8.2.524.g6ff3d78

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v3 1/6] worktree.c: add find_worktree()
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
@ 2016-05-30 10:49                       ` Nguyễn Thái Ngọc Duy
  2016-05-30 10:49                       ` [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename Nguyễn Thái Ngọc Duy
                                         ` (5 subsequent siblings)
  6 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-30 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

So far we haven't needed to identify an existing worktree from command
line. Future commands such as lock or move will need it. The current
implementation identifies worktrees by path (*). In future, the function
could learn to identify by $(basename $path) or tags...

(*) We could probably go cheaper with comparing inode number (and
probably more reliable than paths when unicode enters the game). But not
all systems have good inode that so let's stick to something simple for
now.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 15 +++++++++++++++
 worktree.h |  8 ++++++++
 2 files changed, 23 insertions(+)

diff --git a/worktree.c b/worktree.c
index f4a4f38..0782e00 100644
--- a/worktree.c
+++ b/worktree.c
@@ -214,6 +214,21 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+struct worktree *find_worktree(struct worktree **list,
+			       const char *prefix,
+			       const char *arg)
+{
+	char *path;
+
+	arg = prefix_filename(prefix, strlen(prefix), arg);
+	path = xstrdup(real_path(arg));
+	for (; *list; list++)
+		if (!fspathcmp(path, real_path((*list)->path)))
+			break;
+	free(path);
+	return *list;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 1394909..7ad15da 100644
--- a/worktree.h
+++ b/worktree.h
@@ -29,6 +29,14 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
+/*
+ * Search a worktree that can be unambiguously identified by
+ * "arg". "prefix" must not be NULL.
+ */
+extern struct worktree *find_worktree(struct worktree **list,
+				      const char *prefix,
+				      const char *arg);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
  2016-05-30 10:49                       ` [PATCH v3 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-30 10:49                       ` Nguyễn Thái Ngọc Duy
  2016-05-31 17:51                         ` Junio C Hamano
  2016-05-30 10:49                       ` [PATCH v3 3/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
                                         ` (4 subsequent siblings)
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-30 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This allows the user to do something like "worktree lock foo" instead of
"worktree lock <path/to/foo>". With completion support it could be quite
convenient. While this base name search can be done in the same worktree
iteration loop, the code is split into a separate function for clarity.

Suggested-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/worktree.c b/worktree.c
index 0782e00..4dd7b77 100644
--- a/worktree.c
+++ b/worktree.c
@@ -214,12 +214,33 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+static struct worktree *find_worktree_by_basename(struct worktree **list,
+						  const char *base_name)
+{
+	struct worktree *found = NULL;
+	int nr_found = 0;
+
+	for (; *list && nr_found < 2; list++) {
+		char *path = xstrdup((*list)->path);
+		if (!fspathcmp(base_name, basename(path))) {
+			found = *list;
+			nr_found++;
+		}
+		free(path);
+	}
+	return nr_found == 1 ? found : NULL;
+}
+
 struct worktree *find_worktree(struct worktree **list,
 			       const char *prefix,
 			       const char *arg)
 {
+	struct worktree *wt;
 	char *path;
 
+	if ((wt = find_worktree_by_basename(list, arg)))
+		return wt;
+
 	arg = prefix_filename(prefix, strlen(prefix), arg);
 	path = xstrdup(real_path(arg));
 	for (; *list; list++)
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 3/6] worktree.c: add is_main_worktree()
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
  2016-05-30 10:49                       ` [PATCH v3 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
  2016-05-30 10:49                       ` [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename Nguyễn Thái Ngọc Duy
@ 2016-05-30 10:49                       ` Nguyễn Thái Ngọc Duy
  2016-05-31 17:52                         ` Junio C Hamano
  2016-05-30 10:49                       ` [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees() Nguyễn Thái Ngọc Duy
                                         ` (3 subsequent siblings)
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-30 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Main worktree _is_ different. You can lock a linked worktree but not the
main one, for example. Provide an API for checking that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 5 +++++
 worktree.h | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/worktree.c b/worktree.c
index 4dd7b77..6432eec 100644
--- a/worktree.c
+++ b/worktree.c
@@ -250,6 +250,11 @@ struct worktree *find_worktree(struct worktree **list,
 	return *list;
 }
 
+int is_main_worktree(const struct worktree *wt)
+{
+	return !wt->id;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 7ad15da..e1c4715 100644
--- a/worktree.h
+++ b/worktree.h
@@ -37,6 +37,11 @@ extern struct worktree *find_worktree(struct worktree **list,
 				      const char *prefix,
 				      const char *arg);
 
+/*
+ * Return true if the given worktree is the main one.
+ */
+extern int is_main_worktree(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees()
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
                                         ` (2 preceding siblings ...)
  2016-05-30 10:49                       ` [PATCH v3 3/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-30 10:49                       ` Nguyễn Thái Ngọc Duy
  2016-05-31 17:55                         ` Junio C Hamano
  2016-05-30 10:49                       ` [PATCH v3 5/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
                                         ` (2 subsequent siblings)
  6 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-30 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

We need this later to avoid double locking a worktree, or unlocking one
when it's not even locked.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 12 ++++++++++++
 worktree.h |  1 +
 2 files changed, 13 insertions(+)

diff --git a/worktree.c b/worktree.c
index 6432eec..c6a64b1 100644
--- a/worktree.c
+++ b/worktree.c
@@ -98,6 +98,7 @@ static struct worktree *get_main_worktree(void)
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
+	worktree->lock_reason = NULL;
 
 done:
 	strbuf_release(&path);
@@ -144,6 +145,17 @@ static struct worktree *get_linked_worktree(const char *id)
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
 
+	strbuf_reset(&path);
+	strbuf_addf(&path, "%s/worktrees/%s/locked", get_git_common_dir(), id);
+	if (file_exists(path.buf)) {
+		struct strbuf lock_reason = STRBUF_INIT;
+		if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
+			die_errno(_("failed to read '%s'"), path.buf);
+		strbuf_trim(&lock_reason);
+		worktree->lock_reason = strbuf_detach(&lock_reason, NULL);
+	} else
+		worktree->lock_reason = NULL;
+
 done:
 	strbuf_release(&path);
 	strbuf_release(&worktree_path);
diff --git a/worktree.h b/worktree.h
index e1c4715..9932710 100644
--- a/worktree.h
+++ b/worktree.h
@@ -5,6 +5,7 @@ struct worktree {
 	char *path;
 	char *id;
 	char *head_ref;
+	char *lock_reason;	/* NULL means not locked */
 	unsigned char head_sha1[20];
 	int is_detached;
 	int is_bare;
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 5/6] worktree: add "lock" command
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
                                         ` (3 preceding siblings ...)
  2016-05-30 10:49                       ` [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees() Nguyễn Thái Ngọc Duy
@ 2016-05-30 10:49                       ` Nguyễn Thái Ngọc Duy
  2016-05-31 18:10                         ` Junio C Hamano
  2016-05-31 18:28                         ` Junio C Hamano
  2016-05-30 10:49                       ` [PATCH v3 6/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
  6 siblings, 2 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-30 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         | 28 +++++++++++++++-----
 builtin/worktree.c                     | 38 +++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 +++-
 t/t2028-worktree-move.sh (new +x)      | 48 ++++++++++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 27feff6..6fca6cf 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
 'git worktree list' [--porcelain]
+'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
@@ -38,9 +39,8 @@ section "DETAILS" for more information.
 
 If a linked working tree is stored on a portable device or network share
 which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'locked' alongside the other
-administrative files, optionally containing a plain text reason that
-pruning should be suppressed. See section "DETAILS" for more information.
+being pruned by issuing the `git worktree lock` command, optionally
+specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
@@ -61,6 +61,14 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+lock::
+
+If a working tree is on a portable device or network share which
+is not always mounted, lock it to prevent its administrative
+files from being pruned automatically. This also prevents it from
+being moved or deleted. Optionally, specify a reason for the lock
+with `--reason`.
+
 prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
@@ -110,6 +118,15 @@ OPTIONS
 --expire <time>::
 	With `prune`, only expire unused working trees older than <time>.
 
+--reason <string>:
+	With `lock`, an explanation why the worktree is locked.
+
+<worktree>::
+	Working trees can be identified by path, either relative or
+	absolute. Alternatively, if the last component in the
+	worktree's path is unique among working trees, it can be used
+	as well.
+
 DETAILS
 -------
 Each linked working tree has a private sub-directory in the repository's
@@ -150,7 +167,8 @@ instead.
 
 To prevent a $GIT_DIR/worktrees entry from being pruned (which
 can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), add a file named
+entry's working tree is stored on a portable device), use the
+`git worktree lock` command, which adds a file named
 'locked' to the entry's directory. The file contains the reason in
 plain text. For example, if a linked working tree's `.git` file points
 to `/path/main/.git/worktrees/test-next` then a file named
@@ -226,8 +244,6 @@ performed manually, such as:
 - `remove` to remove a linked working tree and its administrative files (and
   warn if the working tree is dirty)
 - `mv` to move or rename a working tree and update its administrative files
-- `lock` to prevent automatic pruning of administrative files (for instance,
-  for a working tree on a portable device)
 
 GIT
 ---
diff --git a/builtin/worktree.c b/builtin/worktree.c
index f9dac37..84fe63b 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -14,6 +14,7 @@
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<branch>]"),
 	N_("git worktree list [<options>]"),
+	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
 	NULL
 };
@@ -459,6 +460,41 @@ static int list(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+	const char *reason = "", *old_reason;
+	struct option options[] = {
+		OPT_STRING(0, "reason", &reason, N_("string"),
+			   N_("reason for locking")),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees();
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+
+	old_reason = wt->lock_reason;
+	if (old_reason) {
+		if (*old_reason)
+			die(_("'%s' is already locked, reason: %s"),
+			    av[0], old_reason);
+		die(_("'%s' is already locked"), av[0]);
+	}
+
+	write_file(git_common_path("worktrees/%s/locked", wt->id),
+		   "%s", reason);
+	free_worktrees(worktrees);
+	return 0;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -475,5 +511,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return prune(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "list"))
 		return list(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "lock"))
+		return lock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 951a186..f88727d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list prune"
+	local subcommands="add list lock prune"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
@@ -2609,6 +2609,9 @@ _git_worktree ()
 		list,--*)
 			__gitcomp "--porcelain"
 			;;
+		lock,--*)
+			__gitcomp "--reason"
+			;;
 		prune,--*)
 			__gitcomp "--dry-run --expire --verbose"
 			;;
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
new file mode 100755
index 0000000..87afc2e
--- /dev/null
+++ b/t/t2028-worktree-move.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='test git worktree move, remove, lock and unlock'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init &&
+	git worktree add source &&
+	git worktree list --porcelain | grep "^worktree" >actual &&
+	cat <<-EOF >expected &&
+	worktree $TRASH_DIRECTORY
+	worktree $TRASH_DIRECTORY/source
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'lock main worktree' '
+	test_must_fail git worktree lock .
+'
+
+test_expect_success 'lock linked worktree' '
+	git worktree lock --reason hahaha source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock linked worktree from another worktree' '
+	rm .git/worktrees/source/locked &&
+	git worktree add elsewhere &&
+	git -C elsewhere worktree lock --reason hahaha ../source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice' '
+	test_must_fail git worktree lock source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice (from the locked worktree)' '
+	test_must_fail git -C source worktree lock . &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v3 6/6] worktree: add "unlock" command
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
                                         ` (4 preceding siblings ...)
  2016-05-30 10:49                       ` [PATCH v3 5/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-05-30 10:49                       ` Nguyễn Thái Ngọc Duy
  2016-05-31 18:12                         ` Junio C Hamano
  2016-05-31 18:31                         ` Junio C Hamano
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
  6 siblings, 2 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-05-30 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         |  5 +++++
 builtin/worktree.c                     | 28 ++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  2 +-
 t/t2028-worktree-move.sh               | 14 ++++++++++++++
 4 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 6fca6cf..6a5ff47 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree unlock' <worktree>
 
 DESCRIPTION
 -----------
@@ -73,6 +74,10 @@ prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
 
+unlock::
+
+Unlock a worktree, allowing it to be pruned, moved or deleted.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 84fe63b..5fe9323 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -16,6 +16,7 @@ static const char * const worktree_usage[] = {
 	N_("git worktree list [<options>]"),
 	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
+	N_("git worktree unlock <path>"),
 	NULL
 };
 
@@ -495,6 +496,31 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	int ret;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees();
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+	if (!wt->lock_reason)
+		die(_("'%s' is not locked"), av[0]);
+	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+	free_worktrees(worktrees);
+	return ret;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -513,5 +539,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return list(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "lock"))
 		return lock_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "unlock"))
+		return unlock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index f88727d..0e3841d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list lock prune"
+	local subcommands="add list lock prune unlock"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 87afc2e..68d3fe8 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -45,4 +45,18 @@ test_expect_success 'lock worktree twice (from the locked worktree)' '
 	test_cmp expected .git/worktrees/source/locked
 '
 
+test_expect_success 'unlock main worktree' '
+	test_must_fail git worktree unlock .
+'
+
+test_expect_success 'unlock linked worktree' '
+	git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
+test_expect_success 'unlock worktree twice' '
+	test_must_fail git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
 test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-05-30 10:49                       ` [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename Nguyễn Thái Ngọc Duy
@ 2016-05-31 17:51                         ` Junio C Hamano
  2016-06-01 13:22                           ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 17:51 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> This allows the user to do something like "worktree lock foo" instead of
> "worktree lock <path/to/foo>". With completion support it could be quite
> convenient. While this base name search can be done in the same worktree
> iteration loop, the code is split into a separate function for clarity.

Makes me wonder if you want to do this without calling basename(3)
function at all.  I do not think such a feature would cost that much
over what we see in this patch.

That is, wouldn't you rather see "worktree lock to/foo" work when
'foo' is ambiguous but 'to/foo' is not?

>
> Suggested-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  worktree.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
>
> diff --git a/worktree.c b/worktree.c
> index 0782e00..4dd7b77 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -214,12 +214,33 @@ const char *get_worktree_git_dir(const struct worktree *wt)
>  		return git_common_path("worktrees/%s", wt->id);
>  }
>  
> +static struct worktree *find_worktree_by_basename(struct worktree **list,
> +						  const char *base_name)
> +{
> +	struct worktree *found = NULL;
> +	int nr_found = 0;
> +
> +	for (; *list && nr_found < 2; list++) {
> +		char *path = xstrdup((*list)->path);
> +		if (!fspathcmp(base_name, basename(path))) {
> +			found = *list;
> +			nr_found++;
> +		}
> +		free(path);
> +	}
> +	return nr_found == 1 ? found : NULL;
> +}
> +
>  struct worktree *find_worktree(struct worktree **list,
>  			       const char *prefix,
>  			       const char *arg)
>  {
> +	struct worktree *wt;
>  	char *path;
>  
> +	if ((wt = find_worktree_by_basename(list, arg)))
> +		return wt;
> +
>  	arg = prefix_filename(prefix, strlen(prefix), arg);
>  	path = xstrdup(real_path(arg));
>  	for (; *list; list++)

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 3/6] worktree.c: add is_main_worktree()
  2016-05-30 10:49                       ` [PATCH v3 3/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
@ 2016-05-31 17:52                         ` Junio C Hamano
  0 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 17:52 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> Main worktree _is_ different. You can lock a linked worktree but not the
> main one, for example. Provide an API for checking that.

You haven't defined what it means to "lock" a worktree, so that
example does not sound convincing at all, until the reader sees what
happens later in the series.  I'll just take your word for it and
keep reading for now ;-)

>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  worktree.c | 5 +++++
>  worktree.h | 5 +++++
>  2 files changed, 10 insertions(+)
>
> diff --git a/worktree.c b/worktree.c
> index 4dd7b77..6432eec 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -250,6 +250,11 @@ struct worktree *find_worktree(struct worktree **list,
>  	return *list;
>  }
>  
> +int is_main_worktree(const struct worktree *wt)
> +{
> +	return !wt->id;
> +}
> +
>  int is_worktree_being_rebased(const struct worktree *wt,
>  			      const char *target)
>  {
> diff --git a/worktree.h b/worktree.h
> index 7ad15da..e1c4715 100644
> --- a/worktree.h
> +++ b/worktree.h
> @@ -37,6 +37,11 @@ extern struct worktree *find_worktree(struct worktree **list,
>  				      const char *prefix,
>  				      const char *arg);
>  
> +/*
> + * Return true if the given worktree is the main one.
> + */
> +extern int is_main_worktree(const struct worktree *wt);
> +
>  /*
>   * Free up the memory for worktree(s)
>   */

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees()
  2016-05-30 10:49                       ` [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees() Nguyễn Thái Ngọc Duy
@ 2016-05-31 17:55                         ` Junio C Hamano
  2016-06-01 13:02                           ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 17:55 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> We need this later to avoid double locking a worktree, or unlocking one
> when it's not even locked.

Shouldn't this be done lazily?

If a user is working in worktree B and is not doing anything funky,
she would not care why worktree A and C are locked, even though she
might care the fact that they are locked.

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  worktree.c | 12 ++++++++++++
>  worktree.h |  1 +
>  2 files changed, 13 insertions(+)
>
> diff --git a/worktree.c b/worktree.c
> index 6432eec..c6a64b1 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -98,6 +98,7 @@ static struct worktree *get_main_worktree(void)
>  	worktree->is_detached = is_detached;
>  	worktree->is_current = 0;
>  	add_head_info(&head_ref, worktree);
> +	worktree->lock_reason = NULL;
>  
>  done:
>  	strbuf_release(&path);
> @@ -144,6 +145,17 @@ static struct worktree *get_linked_worktree(const char *id)
>  	worktree->is_current = 0;
>  	add_head_info(&head_ref, worktree);
>  
> +	strbuf_reset(&path);
> +	strbuf_addf(&path, "%s/worktrees/%s/locked", get_git_common_dir(), id);
> +	if (file_exists(path.buf)) {
> +		struct strbuf lock_reason = STRBUF_INIT;
> +		if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
> +			die_errno(_("failed to read '%s'"), path.buf);
> +		strbuf_trim(&lock_reason);
> +		worktree->lock_reason = strbuf_detach(&lock_reason, NULL);
> +	} else
> +		worktree->lock_reason = NULL;
> +
>  done:
>  	strbuf_release(&path);
>  	strbuf_release(&worktree_path);
> diff --git a/worktree.h b/worktree.h
> index e1c4715..9932710 100644
> --- a/worktree.h
> +++ b/worktree.h
> @@ -5,6 +5,7 @@ struct worktree {
>  	char *path;
>  	char *id;
>  	char *head_ref;
> +	char *lock_reason;	/* NULL means not locked */
>  	unsigned char head_sha1[20];
>  	int is_detached;
>  	int is_bare;

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 5/6] worktree: add "lock" command
  2016-05-30 10:49                       ` [PATCH v3 5/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-05-31 18:10                         ` Junio C Hamano
  2016-05-31 18:28                         ` Junio C Hamano
  1 sibling, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 18:10 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> diff --git a/builtin/worktree.c b/builtin/worktree.c
> index f9dac37..84fe63b 100644
> --- a/builtin/worktree.c
> +++ b/builtin/worktree.c
> @@ -14,6 +14,7 @@
>  static const char * const worktree_usage[] = {
>  	N_("git worktree add [<options>] <path> [<branch>]"),
>  	N_("git worktree list [<options>]"),
> +	N_("git worktree lock [<options>] <path>"),
>  	N_("git worktree prune [<options>]"),
>  	NULL
>  };
> @@ -459,6 +460,41 @@ static int list(int ac, const char **av, const char *prefix)
>  	return 0;
>  }
>  
> +static int lock_worktree(int ac, const char **av, const char *prefix)
> +{
> +	const char *reason = "", *old_reason;
> +	struct option options[] = {
> +		OPT_STRING(0, "reason", &reason, N_("string"),
> +			   N_("reason for locking")),
> +		OPT_END()
> +	};
> +	struct worktree **worktrees, *wt;
> +
> +	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
> +	if (ac != 1)
> +		usage_with_options(worktree_usage, options);
> +
> +	worktrees = get_worktrees();
> +	wt = find_worktree(worktrees, prefix, av[0]);
> +	if (!wt)
> +		die(_("'%s' is not a working directory"), av[0]);
> +	if (is_main_worktree(wt))
> +		die(_("'%s' is a main working directory"), av[0]);
> +
> +	old_reason = wt->lock_reason;

This use pattern suggests that the reading of lock_reason should be
done lazily, doesn't it?  The other user of the "is it locked?"
information, which is builtin/worktree.c::prune_worktree(), does not
even use the get_worktrees() interface and instead it just checks if
the lock marker git_path("worktrees/*/locked") exists.

Perhaps you want one of these as a public interface:

	int worktree_is_locked(const char *id, const char **reason);
	int worktree_is_locked(struct worktree *wt, const char **reason);

where the caller can learn if a worktree is locked, and optionally why?

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 6/6] worktree: add "unlock" command
  2016-05-30 10:49                       ` [PATCH v3 6/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-05-31 18:12                         ` Junio C Hamano
  2016-06-01 13:10                           ` Duy Nguyen
  2016-05-31 18:31                         ` Junio C Hamano
  1 sibling, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 18:12 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> +	if (is_main_worktree(wt))
> +		die(_("'%s' is a main working directory"), av[0]);
> +	if (!wt->lock_reason)
> +		die(_("'%s' is not locked"), av[0]);

Exactly the same comment about the lack of need for lock_reason
field as 5/6 appiles here.  Also, as "lock" does not allow you to
lock the primary tree, do you even need is_main_worktree() check?

That is:

	if (!worktree_is_locked(wt, &reason))
        	die(_("'%s' is not locked), av[0]);

should be sufficient, no?

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 5/6] worktree: add "lock" command
  2016-05-30 10:49                       ` [PATCH v3 5/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
  2016-05-31 18:10                         ` Junio C Hamano
@ 2016-05-31 18:28                         ` Junio C Hamano
  1 sibling, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 18:28 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> @@ -11,6 +11,7 @@ SYNOPSIS
>  [verse]
>  'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
>  'git worktree list' [--porcelain]
> +'git worktree lock' [--reason <string>] <worktree>
>  'git worktree prune' [-n] [-v] [--expire <expire>]

This seems to be based on an unseen version that lists 'list' before
'prune' here and other places.  I'd appreciate it if you either make
sure a series that introduces a new feature applies cleanly to my
tree before sending it out, or clarify to what state the series is
designed to apply, when you send a patch series next time.

> @@ -110,6 +118,15 @@ OPTIONS
>  --expire <time>::
>  	With `prune`, only expire unused working trees older than <time>.
>  
> +--reason <string>:

Double-colon, I think (locally fixable).

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 6/6] worktree: add "unlock" command
  2016-05-30 10:49                       ` [PATCH v3 6/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
  2016-05-31 18:12                         ` Junio C Hamano
@ 2016-05-31 18:31                         ` Junio C Hamano
  2016-05-31 18:35                           ` Junio C Hamano
  1 sibling, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 18:31 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  Documentation/git-worktree.txt         |  5 +++++
>  builtin/worktree.c                     | 28 ++++++++++++++++++++++++++++
>  contrib/completion/git-completion.bash |  2 +-
>  t/t2028-worktree-move.sh               | 14 ++++++++++++++
>  4 files changed, 48 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
> index 6fca6cf..6a5ff47 100644
> --- a/Documentation/git-worktree.txt
> +++ b/Documentation/git-worktree.txt
> @@ -13,6 +13,7 @@ SYNOPSIS
>  'git worktree list' [--porcelain]
>  'git worktree lock' [--reason <string>] <worktree>
>  'git worktree prune' [-n] [-v] [--expire <expire>]
> +'git worktree unlock' <worktree>

Again, this seems to be built on an unseen version.  I think I fixed
these up (and also the "missing second colon in the doc"), but please
double check what I push out later today.

Thanks.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 6/6] worktree: add "unlock" command
  2016-05-31 18:31                         ` Junio C Hamano
@ 2016-05-31 18:35                           ` Junio C Hamano
  0 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-05-31 18:35 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Junio C Hamano <gitster@pobox.com> writes:

> Again, this seems to be built on an unseen version.  I think I fixed
> these up (and also the "missing second colon in the doc"), but please
> double check what I push out later today.

Ah, this comes on top of 0409e0b6 (worktree: simplify prefixing
paths, 2016-05-22).  I do not necessarily agree with 7b722d9 (I
think "add, list, prune followed by lock, unlock" would be a more
sensible order), but I'll leave them as-is.

Thanks.  Queued.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees()
  2016-05-31 17:55                         ` Junio C Hamano
@ 2016-06-01 13:02                           ` Duy Nguyen
  2016-06-01 15:23                             ` Junio C Hamano
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-06-01 13:02 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

On Wed, Jun 1, 2016 at 12:55 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
>> We need this later to avoid double locking a worktree, or unlocking one
>> when it's not even locked.
>
> Shouldn't this be done lazily?
>
> If a user is working in worktree B and is not doing anything funky,
> she would not care why worktree A and C are locked, even though she
> might care the fact that they are locked.

You and Eric will have to settle this. It was done lazily in v2, but
Eric convinced me this lock thing will be needed for many worktree
commands (list, lock, unlock, move, remove and maybe even prune) that
it makes more sense to make it a field in struct worktree instead. For
a dozen worktrees, it does not really matter. But get_worktrees() is
also being used outside "git worktree" command, some of those new
callers may get picky. Maybe a common ground is adding a flag to
get_worktrees() to select what fields to be filled?
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 6/6] worktree: add "unlock" command
  2016-05-31 18:12                         ` Junio C Hamano
@ 2016-06-01 13:10                           ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-06-01 13:10 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

On Wed, Jun 1, 2016 at 1:12 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
>> +     if (is_main_worktree(wt))
>> +             die(_("'%s' is a main working directory"), av[0]);
>> +     if (!wt->lock_reason)
>> +             die(_("'%s' is not locked"), av[0]);
>
> Exactly the same comment about the lack of need for lock_reason
> field as 5/6 appiles here.  Also, as "lock" does not allow you to
> lock the primary tree, do you even need is_main_worktree() check?
>
> That is:
>
>         if (!worktree_is_locked(wt, &reason))
>                 die(_("'%s' is not locked), av[0]);
>
> should be sufficient, no?

It crossed my mind but I went for separate checks anyway because it
gives better explanation.

Assigning either lock or unlock status to main worktree does not work
well either. If you assume worktree is always unlocked, you still need
to check main worktree in lock, move and remove as all of them would
only proceed if it's an unlocked linked worktree. If you assume the
opposite, you need is_main_worktree() check here and "worktree list"
may print the confusing "locked" status on main worktree (I assume we
may add that later).
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-05-31 17:51                         ` Junio C Hamano
@ 2016-06-01 13:22                           ` Duy Nguyen
  2016-06-01 18:44                             ` Junio C Hamano
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-06-01 13:22 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

On Wed, Jun 1, 2016 at 12:51 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
>> This allows the user to do something like "worktree lock foo" instead of
>> "worktree lock <path/to/foo>". With completion support it could be quite
>> convenient. While this base name search can be done in the same worktree
>> iteration loop, the code is split into a separate function for clarity.
>
> Makes me wonder if you want to do this without calling basename(3)
> function at all.  I do not think such a feature would cost that much
> over what we see in this patch.
>
> That is, wouldn't you rather see "worktree lock to/foo" work when
> 'foo' is ambiguous but 'to/foo' is not?

I don't know. I suppose if people have to make `basename $path` the
same because of some weird build settings, e.g. abc/git and def/git,
then this basename selection becomes useless.

I had similar thought though, if you only have a worktree named "foo"
then "fo" or even "f" should be unambiguous and can also identify a
worktree, similar to short sha-1. But I discarded that idea because of
the higher chances that typos can select a wrong worktree. We would
need to convert or match both '/' and '\' in "to/foo" case because of
Windows, so it's not much easier than basename().
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees()
  2016-06-01 13:02                           ` Duy Nguyen
@ 2016-06-01 15:23                             ` Junio C Hamano
  0 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-06-01 15:23 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

Duy Nguyen <pclouds@gmail.com> writes:

> On Wed, Jun 1, 2016 at 12:55 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>>
>>> We need this later to avoid double locking a worktree, or unlocking one
>>> when it's not even locked.
>>
>> Shouldn't this be done lazily?
>>
>> If a user is working in worktree B and is not doing anything funky,
>> she would not care why worktree A and C are locked, even though she
>> might care the fact that they are locked.
>
> You and Eric will have to settle this. It was done lazily in v2, but
> Eric convinced me this lock thing will be needed for many worktree
> commands (list, lock, unlock, move, remove and maybe even prune) that
> it makes more sense to make it a field in struct worktree instead. For
> a dozen worktrees, it does not really matter. But get_worktrees() is
> also being used outside "git worktree" command, some of those new
> callers may get picky. Maybe a common ground is adding a flag to
> get_worktrees() to select what fields to be filled?

NO.

When you tell callers that they have DIRECT access to fields, then
they need to keep track of what flag they (or their callers) passed
when get_worktrees() was called, which is a road to insanity.

By "lazily", I didn't mean "do not have a field there; instead every
time read from the file and return the value".  A lazy interface would
give a function to read the "is it locked and if so why" information
as the sole interface, whose implementation would _not_ do the reading
when a worktree structure is instantiated, the first call would do
the reading, and it is up to the implementation to cache the result
of that first call's work in the structure and answer the subsequent
calls from the cache, or do the same reading for subsequent calls.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-06-01 13:22                           ` Duy Nguyen
@ 2016-06-01 18:44                             ` Junio C Hamano
  2016-06-02  9:40                               ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-06-01 18:44 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

Duy Nguyen <pclouds@gmail.com> writes:

>> That is, wouldn't you rather see "worktree lock to/foo" work when
>> 'foo' is ambiguous but 'to/foo' is not?
>
> I don't know. I suppose if people have to make `basename $path` the
> same because of some weird build settings, e.g. abc/git and def/git,
> then this basename selection becomes useless.
>
> I had similar thought though, if you only have a worktree named "foo"
> then "fo" or even "f" should be unambiguous and can also identify a
> worktree, similar to short sha-1. But I discarded that idea because of
> the higher chances that typos can select a wrong worktree.

I do not think abbreviation of "foo" down to "fo" is sensible at
all.  I would instead suggest "path-component-wise tail match",
i.e. "to/foo" would match "path/to/foo", "path/TO/FOO" (on fold-case
filesystems), but not "pathto/foo".

> We would
> need to convert or match both '/' and '\' in "to/foo" case because of
> Windows, so it's not much easier than basename().

I never said "easier to implement".  But can this codepath get
backslashed paths in the first place?  I somehow thought that
normalization would happen a lot before the control reaches here.

You'll be calling into fspathcmp() anyway; shouldn't the function
know that '/' and '\' are equivalent on some platforms, or is it
legal to only call fspathcmp() on a single path component without
directory separator?

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-06-01 18:44                             ` Junio C Hamano
@ 2016-06-02  9:40                               ` Duy Nguyen
  2016-06-02 16:49                                 ` Junio C Hamano
  0 siblings, 1 reply; 162+ messages in thread
From: Duy Nguyen @ 2016-06-02  9:40 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

On Thu, Jun 2, 2016 at 1:44 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> We would
>> need to convert or match both '/' and '\' in "to/foo" case because of
>> Windows, so it's not much easier than basename().
>
> I never said "easier to implement".  But can this codepath get
> backslashed paths in the first place?  I somehow thought that
> normalization would happen a lot before the control reaches here.
>
> You'll be calling into fspathcmp() anyway; shouldn't the function
> know that '/' and '\' are equivalent on some platforms, or is it
> legal to only call fspathcmp() on a single path component without
> directory separator?

We still need to calculate the length to compare, which could be
problematic when utf-8 is involved, or some other encoding. If we
always split at '/' boundary though (e.g. "abc/def/ghi", "def/ghi" or
"ghi" but never "ef/ghi") then it should be ok.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-06-02  9:40                               ` Duy Nguyen
@ 2016-06-02 16:49                                 ` Junio C Hamano
  2016-06-03 11:11                                   ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Junio C Hamano @ 2016-06-02 16:49 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

Duy Nguyen <pclouds@gmail.com> writes:

> On Thu, Jun 2, 2016 at 1:44 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>> We would
>>> need to convert or match both '/' and '\' in "to/foo" case because of
>>> Windows, so it's not much easier than basename().
>>
>> I never said "easier to implement".  But can this codepath get
>> backslashed paths in the first place?  I somehow thought that
>> normalization would happen a lot before the control reaches here.
>>
>> You'll be calling into fspathcmp() anyway; shouldn't the function
>> know that '/' and '\' are equivalent on some platforms, or is it
>> legal to only call fspathcmp() on a single path component without
>> directory separator?
>
> We still need to calculate the length to compare, which could be
> problematic when utf-8 is involved, or some other encoding. 

Hmph.  I was unaware that fspathcmp() used here does more than
byte-for-byte processing, which would cause problems due to encoding
issues when you hand code the comparison.

+static struct worktree *find_worktree_by_basename(struct worktree **list,
+						  const char *base_name)
+{
+	struct worktree *found = NULL;
+	int nr_found = 0;
+
+	for (; *list && nr_found < 2; list++) {
+		char *path = xstrdup((*list)->path);
+		if (!fspathcmp(base_name, basename(path))) {
+			found = *list;
+			nr_found++;
+		}
+		free(path);
+	}
+	return nr_found == 1 ? found : NULL;
+}


> If we always split at '/' boundary though (e.g. "abc/def/ghi",
> "def/ghi" or "ghi" but never "ef/ghi") then it should be ok.

Does "basename()" used here know '/' and '\' can both be a directory
separator, or does worktree->path have a normalized representation
of the path, i.e. '/' is the only directory separator?

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-06-02 16:49                                 ` Junio C Hamano
@ 2016-06-03 11:11                                   ` Duy Nguyen
  2016-06-03 15:30                                     ` Junio C Hamano
  2016-06-05  7:15                                     ` Johannes Schindelin
  0 siblings, 2 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-06-03 11:11 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

On Thu, Jun 2, 2016 at 11:49 PM, Junio C Hamano <gitster@pobox.com> wrote:
> +static struct worktree *find_worktree_by_basename(struct worktree **list,
> +                                                 const char *base_name)
> +{
> +       struct worktree *found = NULL;
> +       int nr_found = 0;
> +
> +       for (; *list && nr_found < 2; list++) {
> +               char *path = xstrdup((*list)->path);
> +               if (!fspathcmp(base_name, basename(path))) {
> +                       found = *list;
> +                       nr_found++;
> +               }
> +               free(path);
> +       }
> +       return nr_found == 1 ? found : NULL;
> +}
>
>
>> If we always split at '/' boundary though (e.g. "abc/def/ghi",
>> "def/ghi" or "ghi" but never "ef/ghi") then it should be ok.
>
> Does "basename()" used here know '/' and '\' can both be a directory
> separator, or does worktree->path have a normalized representation
> of the path, i.e. '/' is the only directory separator?

basename() does (or I think so because Windows has its own version).
worktree->path always uses '/' but the command line option can come
with either '/' or '\'. Probably safest to accept both.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v4 0/6] worktree lock/unlock
  2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
                                         ` (5 preceding siblings ...)
  2016-05-30 10:49                       ` [PATCH v3 6/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-06-03 12:19                       ` Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
                                           ` (8 more replies)
  6 siblings, 9 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-03 12:19 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

v4

 - fixes some documentation bugs in v3

 - updates is_main_worktree commit message a bit

 - brings back is_worktree_locked() as API with value lazily obtained
   and cached in worktree->lock_reason and fixes a memory leak

 - extends basename search to path suffix search. I moved this patch
   to the bottom so that the relevant documentation part is included
   in the patch (previously it was part of the "add lock command"
   patch).

This series is based on nd/worktree-cleanup-post-head-protection

Nguyễn Thái Ngọc Duy (6):
  worktree.c: add find_worktree()
  worktree.c: add is_main_worktree()
  worktree.c: add is_worktree_locked()
  worktree: add "lock" command
  worktree: add "unlock" command
  worktree.c: find_worktree() search by path suffix

 Documentation/git-worktree.txt         | 36 +++++++++++++---
 builtin/worktree.c                     | 66 ++++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 ++-
 t/t2028-worktree-move.sh (new +x)      | 62 ++++++++++++++++++++++++++++
 worktree.c                             | 75 ++++++++++++++++++++++++++++++++++
 worktree.h                             | 20 +++++++++
 6 files changed, 257 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

-- 
2.8.2.524.g6ff3d78

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v4 1/6] worktree.c: add find_worktree()
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
@ 2016-06-03 12:19                         ` Nguyễn Thái Ngọc Duy
  2016-06-03 15:00                           ` Ramsay Jones
  2016-06-03 12:19                         ` [PATCH v4 2/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
                                           ` (7 subsequent siblings)
  8 siblings, 1 reply; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-03 12:19 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

So far we haven't needed to identify an existing worktree from command
line. Future commands such as lock or move will need it. The current
implementation identifies worktrees by path (*). In future, the function
could learn to identify by $(basename $path) or tags...

(*) We could probably go cheaper with comparing inode number (and
probably more reliable than paths when unicode enters the game). But not
all systems have good inode that so let's stick to something simple for
now.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 15 +++++++++++++++
 worktree.h |  8 ++++++++
 2 files changed, 23 insertions(+)

diff --git a/worktree.c b/worktree.c
index e2a94e0..554f566 100644
--- a/worktree.c
+++ b/worktree.c
@@ -214,6 +214,21 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+struct worktree *find_worktree(struct worktree **list,
+			       const char *prefix,
+			       const char *arg)
+{
+	char *path;
+
+	arg = prefix_filename(prefix, strlen(prefix), arg);
+	path = xstrdup(real_path(arg));
+	for (; *list; list++)
+		if (!fspathcmp(path, real_path((*list)->path)))
+			break;
+	free(path);
+	return *list;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 1394909..7ad15da 100644
--- a/worktree.h
+++ b/worktree.h
@@ -29,6 +29,14 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
+/*
+ * Search a worktree that can be unambiguously identified by
+ * "arg". "prefix" must not be NULL.
+ */
+extern struct worktree *find_worktree(struct worktree **list,
+				      const char *prefix,
+				      const char *arg);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v4 2/6] worktree.c: add is_main_worktree()
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
@ 2016-06-03 12:19                         ` Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 3/6] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
                                           ` (6 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-03 12:19 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Main worktree _is_ different. You can lock (*) a linked worktree but not
the main one, for example. Provide an API for checking that.

(*) Add the file $GIT_DIR/worktrees/xxx/locked to avoid worktree xxx
from being removed or moved.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 5 +++++
 worktree.h | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/worktree.c b/worktree.c
index 554f566..eb3aaaa 100644
--- a/worktree.c
+++ b/worktree.c
@@ -229,6 +229,11 @@ struct worktree *find_worktree(struct worktree **list,
 	return *list;
 }
 
+int is_main_worktree(const struct worktree *wt)
+{
+	return !wt->id;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 7ad15da..e1c4715 100644
--- a/worktree.h
+++ b/worktree.h
@@ -37,6 +37,11 @@ extern struct worktree *find_worktree(struct worktree **list,
 				      const char *prefix,
 				      const char *arg);
 
+/*
+ * Return true if the given worktree is the main one.
+ */
+extern int is_main_worktree(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v4 3/6] worktree.c: add is_worktree_locked()
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 2/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
@ 2016-06-03 12:19                         ` Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 4/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
                                           ` (5 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-03 12:19 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

We need this later to avoid double locking a worktree, or unlocking one
when it's not even locked.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 26 ++++++++++++++++++++++++++
 worktree.h |  7 +++++++
 2 files changed, 33 insertions(+)

diff --git a/worktree.c b/worktree.c
index eb3aaaa..ee592a4 100644
--- a/worktree.c
+++ b/worktree.c
@@ -5,6 +5,8 @@
 #include "dir.h"
 #include "wt-status.h"
 
+static const char *lock_field_uninitialized = "value is not important";
+
 void free_worktrees(struct worktree **worktrees)
 {
 	int i = 0;
@@ -13,6 +15,8 @@ void free_worktrees(struct worktree **worktrees)
 		free(worktrees[i]->path);
 		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
+		if (worktrees[i]->lock_reason != lock_field_uninitialized)
+			free(worktrees[i]->lock_reason);
 		free(worktrees[i]);
 	}
 	free (worktrees);
@@ -98,6 +102,7 @@ static struct worktree *get_main_worktree(void)
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
+	worktree->lock_reason = (char *)lock_field_uninitialized;
 
 done:
 	strbuf_release(&path);
@@ -143,6 +148,7 @@ static struct worktree *get_linked_worktree(const char *id)
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
+	worktree->lock_reason = (char *)lock_field_uninitialized;
 
 done:
 	strbuf_release(&path);
@@ -234,6 +240,26 @@ int is_main_worktree(const struct worktree *wt)
 	return !wt->id;
 }
 
+const char *is_worktree_locked(struct worktree *wt)
+{
+	if (wt->lock_reason == lock_field_uninitialized) {
+		struct strbuf path = STRBUF_INIT;
+
+		strbuf_addstr(&path, worktree_git_path(wt, "locked"));
+		if (file_exists(path.buf)) {
+			struct strbuf lock_reason = STRBUF_INIT;
+			if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
+				die_errno(_("failed to read '%s'"), path.buf);
+			strbuf_trim(&lock_reason);
+			wt->lock_reason = strbuf_detach(&lock_reason, NULL);
+		} else
+			wt->lock_reason = NULL;
+		strbuf_release(&path);
+	}
+
+	return wt->lock_reason;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index e1c4715..263b61d 100644
--- a/worktree.h
+++ b/worktree.h
@@ -5,6 +5,7 @@ struct worktree {
 	char *path;
 	char *id;
 	char *head_ref;
+	char *lock_reason;	/* internal use */
 	unsigned char head_sha1[20];
 	int is_detached;
 	int is_bare;
@@ -42,6 +43,12 @@ extern struct worktree *find_worktree(struct worktree **list,
  */
 extern int is_main_worktree(const struct worktree *wt);
 
+/*
+ * Return the reason string if the given worktree is locked or NULL
+ * otherwise.
+ */
+extern const char *is_worktree_locked(struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v4 4/6] worktree: add "lock" command
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                           ` (2 preceding siblings ...)
  2016-06-03 12:19                         ` [PATCH v4 3/6] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
@ 2016-06-03 12:19                         ` Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 5/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
                                           ` (4 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-03 12:19 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         | 26 +++++++++++++-----
 builtin/worktree.c                     | 38 +++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 +++-
 t/t2028-worktree-move.sh (new +x)      | 48 ++++++++++++++++++++++++++++++++++
 4 files changed, 110 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 7c4cfb0..d233d61 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
 'git worktree list' [--porcelain]
+'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
@@ -38,9 +39,8 @@ section "DETAILS" for more information.
 
 If a linked working tree is stored on a portable device or network share
 which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'locked' alongside the other
-administrative files, optionally containing a plain text reason that
-pruning should be suppressed. See section "DETAILS" for more information.
+being pruned by issuing the `git worktree lock` command, optionally
+specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
@@ -62,6 +62,14 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+lock::
+
+If a working tree is on a portable device or network share which
+is not always mounted, lock it to prevent its administrative
+files from being pruned automatically. This also prevents it from
+being moved or deleted. Optionally, specify a reason for the lock
+with `--reason`.
+
 prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
@@ -111,6 +119,13 @@ OPTIONS
 --expire <time>::
 	With `prune`, only expire unused working trees older than <time>.
 
+--reason <string>::
+	With `lock`, an explanation why the working tree is locked.
+
+<worktree>::
+	Working trees can be identified by path, either relative or
+	absolute.
+
 DETAILS
 -------
 Each linked working tree has a private sub-directory in the repository's
@@ -151,7 +166,8 @@ instead.
 
 To prevent a $GIT_DIR/worktrees entry from being pruned (which
 can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), add a file named
+entry's working tree is stored on a portable device), use the
+`git worktree lock` command, which adds a file named
 'locked' to the entry's directory. The file contains the reason in
 plain text. For example, if a linked working tree's `.git` file points
 to `/path/main/.git/worktrees/test-next` then a file named
@@ -227,8 +243,6 @@ performed manually, such as:
 - `remove` to remove a linked working tree and its administrative files (and
   warn if the working tree is dirty)
 - `mv` to move or rename a working tree and update its administrative files
-- `lock` to prevent automatic pruning of administrative files (for instance,
-  for a working tree on a portable device)
 
 GIT
 ---
diff --git a/builtin/worktree.c b/builtin/worktree.c
index e866844..b686c7f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -14,6 +14,7 @@
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<branch>]"),
 	N_("git worktree list [<options>]"),
+	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
 	NULL
 };
@@ -462,6 +463,41 @@ static int list(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+	const char *reason = "", *old_reason;
+	struct option options[] = {
+		OPT_STRING(0, "reason", &reason, N_("string"),
+			   N_("reason for locking")),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees();
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+
+	old_reason = is_worktree_locked(wt);
+	if (old_reason) {
+		if (*old_reason)
+			die(_("'%s' is already locked, reason: %s"),
+			    av[0], old_reason);
+		die(_("'%s' is already locked"), av[0]);
+	}
+
+	write_file(git_common_path("worktrees/%s/locked", wt->id),
+		   "%s", reason);
+	free_worktrees(worktrees);
+	return 0;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -478,5 +514,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return prune(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "list"))
 		return list(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "lock"))
+		return lock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index fb0f6e3..61ef18d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2598,7 +2598,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list prune"
+	local subcommands="add list lock prune"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
@@ -2610,6 +2610,9 @@ _git_worktree ()
 		list,--*)
 			__gitcomp "--porcelain"
 			;;
+		lock,--*)
+			__gitcomp "--reason"
+			;;
 		prune,--*)
 			__gitcomp "--dry-run --expire --verbose"
 			;;
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
new file mode 100755
index 0000000..87afc2e
--- /dev/null
+++ b/t/t2028-worktree-move.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='test git worktree move, remove, lock and unlock'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init &&
+	git worktree add source &&
+	git worktree list --porcelain | grep "^worktree" >actual &&
+	cat <<-EOF >expected &&
+	worktree $TRASH_DIRECTORY
+	worktree $TRASH_DIRECTORY/source
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'lock main worktree' '
+	test_must_fail git worktree lock .
+'
+
+test_expect_success 'lock linked worktree' '
+	git worktree lock --reason hahaha source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock linked worktree from another worktree' '
+	rm .git/worktrees/source/locked &&
+	git worktree add elsewhere &&
+	git -C elsewhere worktree lock --reason hahaha ../source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice' '
+	test_must_fail git worktree lock source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice (from the locked worktree)' '
+	test_must_fail git -C source worktree lock . &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v4 5/6] worktree: add "unlock" command
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                           ` (3 preceding siblings ...)
  2016-06-03 12:19                         ` [PATCH v4 4/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-06-03 12:19                         ` Nguyễn Thái Ngọc Duy
  2016-06-03 12:19                         ` [PATCH v4 6/6] worktree.c: find_worktree() search by path suffix Nguyễn Thái Ngọc Duy
                                           ` (3 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-03 12:19 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         |  5 +++++
 builtin/worktree.c                     | 28 ++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  2 +-
 t/t2028-worktree-move.sh               | 14 ++++++++++++++
 4 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index d233d61..317c7d2 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree unlock' <worktree>
 
 DESCRIPTION
 -----------
@@ -74,6 +75,10 @@ prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
 
+unlock::
+
+Unlock a working tree, allowing it to be pruned, moved or deleted.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index b686c7f..6d30c55 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -16,6 +16,7 @@ static const char * const worktree_usage[] = {
 	N_("git worktree list [<options>]"),
 	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
+	N_("git worktree unlock <path>"),
 	NULL
 };
 
@@ -498,6 +499,31 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	int ret;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees();
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working directory"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("'%s' is a main working directory"), av[0]);
+	if (!is_worktree_locked(wt))
+		die(_("'%s' is not locked"), av[0]);
+	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+	free_worktrees(worktrees);
+	return ret;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -516,5 +542,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return list(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "lock"))
 		return lock_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "unlock"))
+		return unlock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 61ef18d..d90e571 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2598,7 +2598,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list lock prune"
+	local subcommands="add list lock prune unlock"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 87afc2e..68d3fe8 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -45,4 +45,18 @@ test_expect_success 'lock worktree twice (from the locked worktree)' '
 	test_cmp expected .git/worktrees/source/locked
 '
 
+test_expect_success 'unlock main worktree' '
+	test_must_fail git worktree unlock .
+'
+
+test_expect_success 'unlock linked worktree' '
+	git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
+test_expect_success 'unlock worktree twice' '
+	test_must_fail git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
 test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v4 6/6] worktree.c: find_worktree() search by path suffix
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                           ` (4 preceding siblings ...)
  2016-06-03 12:19                         ` [PATCH v4 5/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-06-03 12:19                         ` Nguyễn Thái Ngọc Duy
  2016-06-04  5:14                         ` [PATCH v4 0/6] worktree lock/unlock Junio C Hamano
                                           ` (2 subsequent siblings)
  8 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-03 12:19 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

This allows the user to do something like "worktree lock foo" or
"worktree lock to/foo" instead of "worktree lock /long/path/to/foo" if
it's unambiguous. With completion support it could be quite convenient.

Suggested-by: Eric Sunshine <sunshine@sunshineco.com>
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt |  5 +++++
 worktree.c                     | 29 +++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 317c7d2..0aeb020 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -130,6 +130,11 @@ OPTIONS
 <worktree>::
 	Working trees can be identified by path, either relative or
 	absolute.
++
+If the last path components in the working tree's path is unique among
+working trees, it can be used to identify worktrees. For example if
+you only have to working trees at "/abc/def/ghi" and "/abc/def/ggg",
+then "ghi" or "def/ghi" is enough to point to the former working tree.
 
 DETAILS
 -------
diff --git a/worktree.c b/worktree.c
index ee592a4..fd5c768 100644
--- a/worktree.c
+++ b/worktree.c
@@ -220,12 +220,41 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+static struct worktree *find_worktree_by_suffix(struct worktree **list,
+						const char *suffix)
+{
+	struct worktree *found = NULL;
+	int nr_found = 0, suffixlen;
+
+	suffixlen = strlen(suffix);
+	if (!suffixlen)
+		return NULL;
+
+	for (; *list && nr_found < 2; list++) {
+		const char	*path	 = (*list)->path;
+		int		 pathlen = strlen(path);
+		int		 start	 = pathlen - suffixlen;
+
+		/* suffix must start at directory boundary */
+		if ((!start || (start > 0 && is_dir_sep(path[start - 1]))) &&
+		    !fspathcmp(suffix, path + start)) {
+			found = *list;
+			nr_found++;
+		}
+	}
+	return nr_found == 1 ? found : NULL;
+}
+
 struct worktree *find_worktree(struct worktree **list,
 			       const char *prefix,
 			       const char *arg)
 {
+	struct worktree *wt;
 	char *path;
 
+	if ((wt = find_worktree_by_suffix(list, arg)))
+		return wt;
+
 	arg = prefix_filename(prefix, strlen(prefix), arg);
 	path = xstrdup(real_path(arg));
 	for (; *list; list++)
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v4 1/6] worktree.c: add find_worktree()
  2016-06-03 12:19                         ` [PATCH v4 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
@ 2016-06-03 15:00                           ` Ramsay Jones
  2016-06-13 12:22                             ` Duy Nguyen
  0 siblings, 1 reply; 162+ messages in thread
From: Ramsay Jones @ 2016-06-03 15:00 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy, git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo



On 03/06/16 13:19, Nguyễn Thái Ngọc Duy wrote:
> So far we haven't needed to identify an existing worktree from command
> line. Future commands such as lock or move will need it. The current
> implementation identifies worktrees by path (*). In future, the function
> could learn to identify by $(basename $path) or tags...
> 
> (*) We could probably go cheaper with comparing inode number (and
> probably more reliable than paths when unicode enters the game). But not
> all systems have good inode that so let's stick to something simple for
> now.
> 
> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  worktree.c | 15 +++++++++++++++
>  worktree.h |  8 ++++++++
>  2 files changed, 23 insertions(+)
> 
> diff --git a/worktree.c b/worktree.c
> index e2a94e0..554f566 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -214,6 +214,21 @@ const char *get_worktree_git_dir(const struct worktree *wt)
>  		return git_common_path("worktrees/%s", wt->id);
>  }
>  
> +struct worktree *find_worktree(struct worktree **list,
> +			       const char *prefix,
> +			       const char *arg)
> +{
> +	char *path;
> +
> +	arg = prefix_filename(prefix, strlen(prefix), arg);
> +	path = xstrdup(real_path(arg));
> +	for (; *list; list++)
> +		if (!fspathcmp(path, real_path((*list)->path)))

The results of the call to real_path() should probably be cached
in the worktree structure, since real_path() is relatively expensive
(it calls chdir(), lstat(), readlink() etc.), so you don't want to
re-compute the same result time-after-time ...

> +			break;
> +	free(path);
> +	return *list;
> +}
> +

ATB,
Ramsay Jones

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-06-03 11:11                                   ` Duy Nguyen
@ 2016-06-03 15:30                                     ` Junio C Hamano
  2016-06-05  7:15                                     ` Johannes Schindelin
  1 sibling, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-06-03 15:30 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Git Mailing List, Eric Sunshine, Reto Hablützel, Mike Rappazzo

Duy Nguyen <pclouds@gmail.com> writes:

> basename() does (or I think so because Windows has its own version).
> worktree->path always uses '/' but the command line option can come
> with either '/' or '\'. Probably safest to accept both.

OK.  Going beyond basename() was merely "This might be an easy
nice-to-have enhancement", not "basename() is not sufficient because
of such and such issues", so the patch is fine as-is.

Thanks.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v4 0/6] worktree lock/unlock
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                           ` (5 preceding siblings ...)
  2016-06-03 12:19                         ` [PATCH v4 6/6] worktree.c: find_worktree() search by path suffix Nguyễn Thái Ngọc Duy
@ 2016-06-04  5:14                         ` Junio C Hamano
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
  2016-06-27  9:57                         ` [PATCH v4 0/6] worktree lock/unlock Torsten Bögershausen
  8 siblings, 0 replies; 162+ messages in thread
From: Junio C Hamano @ 2016-06-04  5:14 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Eric Sunshine, rethab.ch, rappazzo

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> v4
>
>  - fixes some documentation bugs in v3
>
>  - updates is_main_worktree commit message a bit
>
>  - brings back is_worktree_locked() as API with value lazily obtained
>    and cached in worktree->lock_reason and fixes a memory leak
>
>  - extends basename search to path suffix search. I moved this patch
>    to the bottom so that the relevant documentation part is included
>    in the patch (previously it was part of the "add lock command"
>    patch).
>
> This series is based on nd/worktree-cleanup-post-head-protection

Will replace the one in 'pu'.  A few things I noticed:

 * Documentation and messages talk about "working tree" and "working
   directory" when they refer to "worktree", which is not a normal
   "working tree".  I think this was done as an attempt to avoid
   being too precise and technical in phrasing, but I suspect that
   is a false reader-friendliness.  At least, "working directory" in
   messages should be "worktree", e.g.

	wt = find_worktree(...);
	if (!wt)
        	die("'%s' is not a working directory", av[0]);

   does not make much sense (imagine "git wt lock ." and being told
   that '.' is not a working directory).

 * You are probably trying to be frugal by not introducing a
   separate bit for is_lock_checked to struct worktre and instead
   using a sentinel value for lock_reason, but I think that is a
   false economy, after seeing that you have to cast away constness
   in number of places and having to be extra careful not to free
   that sentinel value.

Thanks.

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename
  2016-06-03 11:11                                   ` Duy Nguyen
  2016-06-03 15:30                                     ` Junio C Hamano
@ 2016-06-05  7:15                                     ` Johannes Schindelin
  1 sibling, 0 replies; 162+ messages in thread
From: Johannes Schindelin @ 2016-06-05  7:15 UTC (permalink / raw)
  To: Duy Nguyen
  Cc: Junio C Hamano, Git Mailing List, Eric Sunshine,
	Reto Hablützel, Mike Rappazzo

Hi,

On Fri, 3 Jun 2016, Duy Nguyen wrote:

> On Thu, Jun 2, 2016 at 11:49 PM, Junio C Hamano <gitster@pobox.com> wrote:
>
> > Does "basename()" used here know '/' and '\' can both be a directory
> > separator, or does worktree->path have a normalized representation of
> > the path, i.e. '/' is the only directory separator?
> 
> basename() does (or I think so because Windows has its own version).
> worktree->path always uses '/' but the command line option can come with
> either '/' or '\'. Probably safest to accept both.

It is very easy to see what the Windows version of basename() does by
looking at our very own test code:

	https://github.com/git/git/blob/v2.8.3/test-path-utils.c#L71-L110

Short answer: basename() knows that both '/' and '\' are directory
separators.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH v5 0/6] worktree lock/unlock
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                           ` (6 preceding siblings ...)
  2016-06-04  5:14                         ` [PATCH v4 0/6] worktree lock/unlock Junio C Hamano
@ 2016-06-13 12:18                         ` Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
                                             ` (5 more replies)
  2016-06-27  9:57                         ` [PATCH v4 0/6] worktree lock/unlock Torsten Bögershausen
  8 siblings, 6 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-13 12:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

v5 fixes some error messages mentioning "working directory" instead of
"working tree" and split the double use of "lock_reason" field in
"struct worktree". This series depends on
nd/worktree-cleanup-post-head-protection.

Diff from v4

-- 8< --
diff --git a/builtin/worktree.c b/builtin/worktree.c
index cb5026d..4877421 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -478,9 +478,9 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 	worktrees = get_worktrees();
 	wt = find_worktree(worktrees, prefix, av[0]);
 	if (!wt)
-		die(_("'%s' is not a working directory"), av[0]);
+		die(_("'%s' is not a working tree"), av[0]);
 	if (is_main_worktree(wt))
-		die(_("'%s' is a main working directory"), av[0]);
+		die(_("The main working tree cannot be locked or unlocked"));
 
 	old_reason = is_worktree_locked(wt);
 	if (old_reason) {
@@ -511,9 +511,9 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
 	worktrees = get_worktrees();
 	wt = find_worktree(worktrees, prefix, av[0]);
 	if (!wt)
-		die(_("'%s' is not a working directory"), av[0]);
+		die(_("'%s' is not a working tree"), av[0]);
 	if (is_main_worktree(wt))
-		die(_("'%s' is a main working directory"), av[0]);
+		die(_("The main working tree cannot be locked or unlocked"));
 	if (!is_worktree_locked(wt))
 		die(_("'%s' is not locked"), av[0]);
 	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
diff --git a/worktree.c b/worktree.c
index b16262b..2107c06 100644
--- a/worktree.c
+++ b/worktree.c
@@ -5,8 +5,6 @@
 #include "dir.h"
 #include "wt-status.h"
 
-static const char *lock_field_uninitialized = "value is not important";
-
 void free_worktrees(struct worktree **worktrees)
 {
 	int i = 0;
@@ -15,8 +13,7 @@ void free_worktrees(struct worktree **worktrees)
 		free(worktrees[i]->path);
 		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
-		if (worktrees[i]->lock_reason != lock_field_uninitialized)
-			free(worktrees[i]->lock_reason);
+		free(worktrees[i]->lock_reason);
 		free(worktrees[i]);
 	}
 	free (worktrees);
@@ -102,7 +99,8 @@ static struct worktree *get_main_worktree(void)
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
-	worktree->lock_reason = (char *)lock_field_uninitialized;
+	worktree->lock_reason = NULL;
+	worktree->lock_reason_valid = 0;
 
 done:
 	strbuf_release(&path);
@@ -148,7 +146,8 @@ static struct worktree *get_linked_worktree(const char *id)
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
-	worktree->lock_reason = (char *)lock_field_uninitialized;
+	worktree->lock_reason = NULL;
+	worktree->lock_reason_valid = 0;
 
 done:
 	strbuf_release(&path);
@@ -271,7 +270,9 @@ int is_main_worktree(const struct worktree *wt)
 
 const char *is_worktree_locked(struct worktree *wt)
 {
-	if (wt->lock_reason == lock_field_uninitialized) {
+	assert(!is_main_worktree(wt));
+
+	if (!wt->lock_reason_valid) {
 		struct strbuf path = STRBUF_INIT;
 
 		strbuf_addstr(&path, worktree_git_path(wt, "locked"));
@@ -283,6 +284,7 @@ const char *is_worktree_locked(struct worktree *wt)
 			wt->lock_reason = strbuf_detach(&lock_reason, NULL);
 		} else
 			wt->lock_reason = NULL;
+		wt->lock_reason_valid = 1;
 		strbuf_release(&path);
 	}
 
diff --git a/worktree.h b/worktree.h
index 263b61d..90e1311 100644
--- a/worktree.h
+++ b/worktree.h
@@ -10,6 +10,7 @@ struct worktree {
 	int is_detached;
 	int is_bare;
 	int is_current;
+	int lock_reason_valid;
 };
 
 /* Functions for acting on the information about worktrees. */
-- >8 --

Nguyễn Thái Ngọc Duy (6):
  worktree.c: add find_worktree()
  worktree.c: add is_main_worktree()
  worktree.c: add is_worktree_locked()
  worktree: add "lock" command
  worktree: add "unlock" command
  worktree.c: find_worktree() search by path suffix

 Documentation/git-worktree.txt         | 36 +++++++++++++---
 builtin/worktree.c                     | 66 +++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 ++-
 t/t2028-worktree-move.sh (new +x)      | 62 +++++++++++++++++++++++++++
 worktree.c                             | 77 ++++++++++++++++++++++++++++++++++
 worktree.h                             | 21 ++++++++++
 6 files changed, 260 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v5 1/6] worktree.c: add find_worktree()
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
@ 2016-06-13 12:18                           ` Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 2/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
                                             ` (4 subsequent siblings)
  5 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-13 12:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

So far we haven't needed to identify an existing worktree from command
line. Future commands such as lock or move will need it. The current
implementation identifies worktrees by path (*). In future, the function
could learn to identify by $(basename $path) or tags...

(*) We could probably go cheaper with comparing inode number (and
probably more reliable than paths when unicode enters the game). But not
all systems have good inode that so let's stick to something simple for
now.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 15 +++++++++++++++
 worktree.h |  8 ++++++++
 2 files changed, 23 insertions(+)

diff --git a/worktree.c b/worktree.c
index f4a4f38..0782e00 100644
--- a/worktree.c
+++ b/worktree.c
@@ -214,6 +214,21 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+struct worktree *find_worktree(struct worktree **list,
+			       const char *prefix,
+			       const char *arg)
+{
+	char *path;
+
+	arg = prefix_filename(prefix, strlen(prefix), arg);
+	path = xstrdup(real_path(arg));
+	for (; *list; list++)
+		if (!fspathcmp(path, real_path((*list)->path)))
+			break;
+	free(path);
+	return *list;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 1394909..7ad15da 100644
--- a/worktree.h
+++ b/worktree.h
@@ -29,6 +29,14 @@ extern struct worktree **get_worktrees(void);
  */
 extern const char *get_worktree_git_dir(const struct worktree *wt);
 
+/*
+ * Search a worktree that can be unambiguously identified by
+ * "arg". "prefix" must not be NULL.
+ */
+extern struct worktree *find_worktree(struct worktree **list,
+				      const char *prefix,
+				      const char *arg);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v5 2/6] worktree.c: add is_main_worktree()
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
@ 2016-06-13 12:18                           ` Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 3/6] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
                                             ` (3 subsequent siblings)
  5 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-13 12:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Main worktree _is_ different. You can lock (*) a linked worktree but not
the main one, for example. Provide an API for checking that.

(*) Add the file $GIT_DIR/worktrees/xxx/locked to avoid worktree xxx
from being removed or moved.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 5 +++++
 worktree.h | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/worktree.c b/worktree.c
index 0782e00..12a766a 100644
--- a/worktree.c
+++ b/worktree.c
@@ -229,6 +229,11 @@ struct worktree *find_worktree(struct worktree **list,
 	return *list;
 }
 
+int is_main_worktree(const struct worktree *wt)
+{
+	return !wt->id;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index 7ad15da..e1c4715 100644
--- a/worktree.h
+++ b/worktree.h
@@ -37,6 +37,11 @@ extern struct worktree *find_worktree(struct worktree **list,
 				      const char *prefix,
 				      const char *arg);
 
+/*
+ * Return true if the given worktree is the main one.
+ */
+extern int is_main_worktree(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v5 3/6] worktree.c: add is_worktree_locked()
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 2/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
@ 2016-06-13 12:18                           ` Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 4/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
                                             ` (2 subsequent siblings)
  5 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-13 12:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

We need this later to avoid double locking a worktree, or unlocking one
when it's not even locked.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 worktree.c | 28 ++++++++++++++++++++++++++++
 worktree.h |  8 ++++++++
 2 files changed, 36 insertions(+)

diff --git a/worktree.c b/worktree.c
index 12a766a..2bcfff3 100644
--- a/worktree.c
+++ b/worktree.c
@@ -13,6 +13,7 @@ void free_worktrees(struct worktree **worktrees)
 		free(worktrees[i]->path);
 		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
+		free(worktrees[i]->lock_reason);
 		free(worktrees[i]);
 	}
 	free (worktrees);
@@ -98,6 +99,8 @@ static struct worktree *get_main_worktree(void)
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
+	worktree->lock_reason = NULL;
+	worktree->lock_reason_valid = 0;
 
 done:
 	strbuf_release(&path);
@@ -143,6 +146,8 @@ static struct worktree *get_linked_worktree(const char *id)
 	worktree->is_detached = is_detached;
 	worktree->is_current = 0;
 	add_head_info(&head_ref, worktree);
+	worktree->lock_reason = NULL;
+	worktree->lock_reason_valid = 0;
 
 done:
 	strbuf_release(&path);
@@ -234,6 +239,29 @@ int is_main_worktree(const struct worktree *wt)
 	return !wt->id;
 }
 
+const char *is_worktree_locked(struct worktree *wt)
+{
+	assert(!is_main_worktree(wt));
+
+	if (!wt->lock_reason_valid) {
+		struct strbuf path = STRBUF_INIT;
+
+		strbuf_addstr(&path, worktree_git_path(wt, "locked"));
+		if (file_exists(path.buf)) {
+			struct strbuf lock_reason = STRBUF_INIT;
+			if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
+				die_errno(_("failed to read '%s'"), path.buf);
+			strbuf_trim(&lock_reason);
+			wt->lock_reason = strbuf_detach(&lock_reason, NULL);
+		} else
+			wt->lock_reason = NULL;
+		wt->lock_reason_valid = 1;
+		strbuf_release(&path);
+	}
+
+	return wt->lock_reason;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
 			      const char *target)
 {
diff --git a/worktree.h b/worktree.h
index e1c4715..90e1311 100644
--- a/worktree.h
+++ b/worktree.h
@@ -5,10 +5,12 @@ struct worktree {
 	char *path;
 	char *id;
 	char *head_ref;
+	char *lock_reason;	/* internal use */
 	unsigned char head_sha1[20];
 	int is_detached;
 	int is_bare;
 	int is_current;
+	int lock_reason_valid;
 };
 
 /* Functions for acting on the information about worktrees. */
@@ -42,6 +44,12 @@ extern struct worktree *find_worktree(struct worktree **list,
  */
 extern int is_main_worktree(const struct worktree *wt);
 
+/*
+ * Return the reason string if the given worktree is locked or NULL
+ * otherwise.
+ */
+extern const char *is_worktree_locked(struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v5 4/6] worktree: add "lock" command
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                                             ` (2 preceding siblings ...)
  2016-06-13 12:18                           ` [PATCH v5 3/6] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
@ 2016-06-13 12:18                           ` Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 5/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 6/6] worktree.c: find_worktree() search by path suffix Nguyễn Thái Ngọc Duy
  5 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-13 12:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         | 26 +++++++++++++-----
 builtin/worktree.c                     | 38 +++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  5 +++-
 t/t2028-worktree-move.sh (new +x)      | 48 ++++++++++++++++++++++++++++++++++
 4 files changed, 110 insertions(+), 7 deletions(-)
 create mode 100755 t/t2028-worktree-move.sh

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 27feff6..b49b25b 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
 'git worktree list' [--porcelain]
+'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
@@ -38,9 +39,8 @@ section "DETAILS" for more information.
 
 If a linked working tree is stored on a portable device or network share
 which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'locked' alongside the other
-administrative files, optionally containing a plain text reason that
-pruning should be suppressed. See section "DETAILS" for more information.
+being pruned by issuing the `git worktree lock` command, optionally
+specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
@@ -61,6 +61,14 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+lock::
+
+If a working tree is on a portable device or network share which
+is not always mounted, lock it to prevent its administrative
+files from being pruned automatically. This also prevents it from
+being moved or deleted. Optionally, specify a reason for the lock
+with `--reason`.
+
 prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
@@ -110,6 +118,13 @@ OPTIONS
 --expire <time>::
 	With `prune`, only expire unused working trees older than <time>.
 
+--reason <string>::
+	With `lock`, an explanation why the working tree is locked.
+
+<worktree>::
+	Working trees can be identified by path, either relative or
+	absolute.
+
 DETAILS
 -------
 Each linked working tree has a private sub-directory in the repository's
@@ -150,7 +165,8 @@ instead.
 
 To prevent a $GIT_DIR/worktrees entry from being pruned (which
 can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), add a file named
+entry's working tree is stored on a portable device), use the
+`git worktree lock` command, which adds a file named
 'locked' to the entry's directory. The file contains the reason in
 plain text. For example, if a linked working tree's `.git` file points
 to `/path/main/.git/worktrees/test-next` then a file named
@@ -226,8 +242,6 @@ performed manually, such as:
 - `remove` to remove a linked working tree and its administrative files (and
   warn if the working tree is dirty)
 - `mv` to move or rename a working tree and update its administrative files
-- `lock` to prevent automatic pruning of administrative files (for instance,
-  for a working tree on a portable device)
 
 GIT
 ---
diff --git a/builtin/worktree.c b/builtin/worktree.c
index f9dac37..3b9220f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -14,6 +14,7 @@
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<branch>]"),
 	N_("git worktree list [<options>]"),
+	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
 	NULL
 };
@@ -459,6 +460,41 @@ static int list(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+	const char *reason = "", *old_reason;
+	struct option options[] = {
+		OPT_STRING(0, "reason", &reason, N_("string"),
+			   N_("reason for locking")),
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees();
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working tree"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("The main working tree cannot be locked or unlocked"));
+
+	old_reason = is_worktree_locked(wt);
+	if (old_reason) {
+		if (*old_reason)
+			die(_("'%s' is already locked, reason: %s"),
+			    av[0], old_reason);
+		die(_("'%s' is already locked"), av[0]);
+	}
+
+	write_file(git_common_path("worktrees/%s/locked", wt->id),
+		   "%s", reason);
+	free_worktrees(worktrees);
+	return 0;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -475,5 +511,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return prune(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "list"))
 		return list(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "lock"))
+		return lock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 951a186..f88727d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list prune"
+	local subcommands="add list lock prune"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
@@ -2609,6 +2609,9 @@ _git_worktree ()
 		list,--*)
 			__gitcomp "--porcelain"
 			;;
+		lock,--*)
+			__gitcomp "--reason"
+			;;
 		prune,--*)
 			__gitcomp "--dry-run --expire --verbose"
 			;;
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
new file mode 100755
index 0000000..87afc2e
--- /dev/null
+++ b/t/t2028-worktree-move.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='test git worktree move, remove, lock and unlock'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init &&
+	git worktree add source &&
+	git worktree list --porcelain | grep "^worktree" >actual &&
+	cat <<-EOF >expected &&
+	worktree $TRASH_DIRECTORY
+	worktree $TRASH_DIRECTORY/source
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'lock main worktree' '
+	test_must_fail git worktree lock .
+'
+
+test_expect_success 'lock linked worktree' '
+	git worktree lock --reason hahaha source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock linked worktree from another worktree' '
+	rm .git/worktrees/source/locked &&
+	git worktree add elsewhere &&
+	git -C elsewhere worktree lock --reason hahaha ../source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice' '
+	test_must_fail git worktree lock source &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_expect_success 'lock worktree twice (from the locked worktree)' '
+	test_must_fail git -C source worktree lock . &&
+	echo hahaha >expected &&
+	test_cmp expected .git/worktrees/source/locked
+'
+
+test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v5 5/6] worktree: add "unlock" command
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                                             ` (3 preceding siblings ...)
  2016-06-13 12:18                           ` [PATCH v5 4/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
@ 2016-06-13 12:18                           ` Nguyễn Thái Ngọc Duy
  2016-06-13 12:18                           ` [PATCH v5 6/6] worktree.c: find_worktree() search by path suffix Nguyễn Thái Ngọc Duy
  5 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-13 12:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt         |  5 +++++
 builtin/worktree.c                     | 28 ++++++++++++++++++++++++++++
 contrib/completion/git-completion.bash |  2 +-
 t/t2028-worktree-move.sh               | 14 ++++++++++++++
 4 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index b49b25b..27330c5 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree unlock' <worktree>
 
 DESCRIPTION
 -----------
@@ -73,6 +74,10 @@ prune::
 
 Prune working tree information in $GIT_DIR/worktrees.
 
+unlock::
+
+Unlock a working tree, allowing it to be pruned, moved or deleted.
+
 OPTIONS
 -------
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 3b9220f..4877421 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -16,6 +16,7 @@ static const char * const worktree_usage[] = {
 	N_("git worktree list [<options>]"),
 	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree prune [<options>]"),
+	N_("git worktree unlock <path>"),
 	NULL
 };
 
@@ -495,6 +496,31 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
 	return 0;
 }
 
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct worktree **worktrees, *wt;
+	int ret;
+
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac != 1)
+		usage_with_options(worktree_usage, options);
+
+	worktrees = get_worktrees();
+	wt = find_worktree(worktrees, prefix, av[0]);
+	if (!wt)
+		die(_("'%s' is not a working tree"), av[0]);
+	if (is_main_worktree(wt))
+		die(_("The main working tree cannot be locked or unlocked"));
+	if (!is_worktree_locked(wt))
+		die(_("'%s' is not locked"), av[0]);
+	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+	free_worktrees(worktrees);
+	return ret;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -513,5 +539,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		return list(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "lock"))
 		return lock_worktree(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "unlock"))
+		return unlock_worktree(ac - 1, av + 1, prefix);
 	usage_with_options(worktree_usage, options);
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index f88727d..0e3841d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
 
 _git_worktree ()
 {
-	local subcommands="add list lock prune"
+	local subcommands="add list lock prune unlock"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 87afc2e..68d3fe8 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -45,4 +45,18 @@ test_expect_success 'lock worktree twice (from the locked worktree)' '
 	test_cmp expected .git/worktrees/source/locked
 '
 
+test_expect_success 'unlock main worktree' '
+	test_must_fail git worktree unlock .
+'
+
+test_expect_success 'unlock linked worktree' '
+	git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
+test_expect_success 'unlock worktree twice' '
+	test_must_fail git worktree unlock source &&
+	test_path_is_missing .git/worktrees/source/locked
+'
+
 test_done
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* [PATCH v5 6/6] worktree.c: find_worktree() search by path suffix
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
                                             ` (4 preceding siblings ...)
  2016-06-13 12:18                           ` [PATCH v5 5/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
@ 2016-06-13 12:18                           ` Nguyễn Thái Ngọc Duy
  5 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-13 12:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

This allows the user to do something like "worktree lock foo" or
"worktree lock to/foo" instead of "worktree lock /long/path/to/foo" if
it's unambiguous.

With completion support it could be quite convenient. While this base
name search can be done in the same worktree iteration loop, the code is
split into a separate function for clarity.

Suggested-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-worktree.txt |  5 +++++
 worktree.c                     | 29 +++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 27330c5..7850dee 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -129,6 +129,11 @@ OPTIONS
 <worktree>::
 	Working trees can be identified by path, either relative or
 	absolute.
++
+If the last path components in the working tree's path is unique among
+working trees, it can be used to identify worktrees. For example if
+you only have to working trees at "/abc/def/ghi" and "/abc/def/ggg",
+then "ghi" or "def/ghi" is enough to point to the former working tree.
 
 DETAILS
 -------
diff --git a/worktree.c b/worktree.c
index 2bcfff3..2107c06 100644
--- a/worktree.c
+++ b/worktree.c
@@ -219,12 +219,41 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+static struct worktree *find_worktree_by_suffix(struct worktree **list,
+						const char *suffix)
+{
+	struct worktree *found = NULL;
+	int nr_found = 0, suffixlen;
+
+	suffixlen = strlen(suffix);
+	if (!suffixlen)
+		return NULL;
+
+	for (; *list && nr_found < 2; list++) {
+		const char	*path	 = (*list)->path;
+		int		 pathlen = strlen(path);
+		int		 start	 = pathlen - suffixlen;
+
+		/* suffix must start at directory boundary */
+		if ((!start || (start > 0 && is_dir_sep(path[start - 1]))) &&
+		    !fspathcmp(suffix, path + start)) {
+			found = *list;
+			nr_found++;
+		}
+	}
+	return nr_found == 1 ? found : NULL;
+}
+
 struct worktree *find_worktree(struct worktree **list,
 			       const char *prefix,
 			       const char *arg)
 {
+	struct worktree *wt;
 	char *path;
 
+	if ((wt = find_worktree_by_suffix(list, arg)))
+		return wt;
+
 	arg = prefix_filename(prefix, strlen(prefix), arg);
 	path = xstrdup(real_path(arg));
 	for (; *list; list++)
-- 
2.8.2.524.g6ff3d78

^ permalink raw reply related	[flat|nested] 162+ messages in thread

* Re: [PATCH v4 1/6] worktree.c: add find_worktree()
  2016-06-03 15:00                           ` Ramsay Jones
@ 2016-06-13 12:22                             ` Duy Nguyen
  0 siblings, 0 replies; 162+ messages in thread
From: Duy Nguyen @ 2016-06-13 12:22 UTC (permalink / raw)
  To: Ramsay Jones
  Cc: Git Mailing List, Junio C Hamano, Eric Sunshine,
	Reto Hablützel, Mike Rappazzo

On Fri, Jun 3, 2016 at 10:00 PM, Ramsay Jones
<ramsay@ramsayjones.plus.com> wrote:
>
>
> On 03/06/16 13:19, Nguyễn Thái Ngọc Duy wrote:
>> So far we haven't needed to identify an existing worktree from command
>> line. Future commands such as lock or move will need it. The current
>> implementation identifies worktrees by path (*). In future, the function
>> could learn to identify by $(basename $path) or tags...
>>
>> (*) We could probably go cheaper with comparing inode number (and
>> probably more reliable than paths when unicode enters the game). But not
>> all systems have good inode that so let's stick to something simple for
>> now.
>>
>> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>>  worktree.c | 15 +++++++++++++++
>>  worktree.h |  8 ++++++++
>>  2 files changed, 23 insertions(+)
>>
>> diff --git a/worktree.c b/worktree.c
>> index e2a94e0..554f566 100644
>> --- a/worktree.c
>> +++ b/worktree.c
>> @@ -214,6 +214,21 @@ const char *get_worktree_git_dir(const struct worktree *wt)
>>               return git_common_path("worktrees/%s", wt->id);
>>  }
>>
>> +struct worktree *find_worktree(struct worktree **list,
>> +                            const char *prefix,
>> +                            const char *arg)
>> +{
>> +     char *path;
>> +
>> +     arg = prefix_filename(prefix, strlen(prefix), arg);
>> +     path = xstrdup(real_path(arg));
>> +     for (; *list; list++)
>> +             if (!fspathcmp(path, real_path((*list)->path)))
>
> The results of the call to real_path() should probably be cached
> in the worktree structure, since real_path() is relatively expensive
> (it calls chdir(), lstat(), readlink() etc.), so you don't want to
> re-compute the same result time-after-time ...

Urgh.. I missed this after sending out v5. Because find_worktree is
probably called once or twice per process, I don't think we need to
optimize this yet. If nr. worktrees goes up to hundreds then this is
one of many items we need to do to make worktree list fast.
-- 
Duy

^ permalink raw reply	[flat|nested] 162+ messages in thread

* Re: [PATCH v4 0/6] worktree lock/unlock
  2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
                                           ` (7 preceding siblings ...)
  2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
@ 2016-06-27  9:57                         ` Torsten Bögershausen
  2016-06-30 16:01                           ` [PATCH] fixup! worktree: add "lock" command Nguyễn Thái Ngọc Duy
  8 siblings, 1 reply; 162+ messages in thread
From: Torsten Bögershausen @ 2016-06-27  9:57 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy, git
  Cc: Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo

>On 03.06.16 14:19, Nguyễn Thái Ngọc Duy wrote:

Minor problem:
t2028 fails, when the test is run from a directory that is

a softlink.
(In my case 
/Users/tb/projects/git/git.pu
is a softlink to
/Users/tb/NoBackup/projects/git/git.pu/


[master (root-commit) 2519212] init
 Author: A U Thor <author@example.com>
 1 file changed, 1 insertion(+)
 create mode 100644 init.t
Preparing source (identifier source)
HEAD is now at 2519212 init
--- expected    2016-06-27 09:50:19.000000000 +0000
+++ actual      2016-06-27 09:50:19.000000000 +0000
@@ -1,2 +1,2 @@
-worktree /Users/tb/projects/git/git.pu/t/trash directory.t2028-worktree-move
-worktree /Users/tb/projects/git/git.pu/t/trash directory.t2028-worktree-move/source
+worktree /Users/tb/NoBackup/projects/git/git.pu/t/trash directory.t2028-worktree-move
+worktree /Users/tb/NoBackup/projects/git/git.pu/t/trash directory.t2028-worktree-move/source
not ok 1 - setup
#


^ permalink raw reply	[flat|nested] 162+ messages in thread

* [PATCH] fixup! worktree: add "lock" command
  2016-06-27  9:57                         ` [PATCH v4 0/6] worktree lock/unlock Torsten Bögershausen
@ 2016-06-30 16:01                           ` Nguyễn Thái Ngọc Duy
  0 siblings, 0 replies; 162+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-06-30 16:01 UTC (permalink / raw)
  To: git
  Cc: tboegi, Junio C Hamano, Eric Sunshine, rethab.ch, rappazzo,
	Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Torsten, this seems to fix the symlink problem for me. How many times
 have I got similar reports from you and still managed to forget ...

 t/t2028-worktree-move.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 68d3fe8..8298aaf 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -9,8 +9,8 @@ test_expect_success 'setup' '
 	git worktree add source &&
 	git worktree list --porcelain | grep "^worktree" >actual &&
 	cat <<-EOF >expected &&
-	worktree $TRASH_DIRECTORY
-	worktree $TRASH_DIRECTORY/source
+	worktree $(pwd)
+	worktree $(pwd)/source
 	EOF
 	test_cmp expected actual
 '
-- 
2.8.2.531.gd073806


^ permalink raw reply related	[flat|nested] 162+ messages in thread

end of thread, other threads:[~2016-06-30 16:03 UTC | newest]

Thread overview: 162+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-15 13:53 possible to checkout same branch in different worktree Reto Hablützel
2016-04-15 15:36 ` Junio C Hamano
2016-04-16  0:51   ` Duy Nguyen
2016-04-17 12:59     ` [PATCH 0/7] fix checking out a being-rebased branch Nguyễn Thái Ngọc Duy
2016-04-17 12:59       ` [PATCH 1/7] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
2016-04-17 12:59       ` [PATCH 2/7] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
2016-04-17 12:59       ` [PATCH 3/7] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
2016-04-17 12:59       ` [PATCH 4/7] worktree.c: add worktree_git_path_..._head() Nguyễn Thái Ngọc Duy
2016-04-17 12:59       ` [PATCH 5/7] wt-status.c: make wt_status_get_state() support worktree Nguyễn Thái Ngọc Duy
2016-04-17 12:59       ` [PATCH 6/7] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
2016-04-17 12:59       ` [PATCH 7/7] checkout: prevent checking out a branch being rebased in another worktree Nguyễn Thái Ngọc Duy
2016-04-18 17:42       ` [PATCH 0/7] fix checking out a being-rebased branch Junio C Hamano
2016-04-19  1:04         ` Duy Nguyen
2016-04-19 20:43           ` Junio C Hamano
2016-04-20 13:24       ` [PATCH v2 00/12] " Nguyễn Thái Ngọc Duy
2016-04-20 13:24         ` [PATCH v2 01/12] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
2016-04-20 18:11           ` Eric Sunshine
2016-04-21  0:28             ` Duy Nguyen
2016-04-20 13:24         ` [PATCH v2 02/12] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
2016-04-20 13:24         ` [PATCH v2 03/12] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
2016-04-20 17:05           ` Junio C Hamano
2016-04-21  7:02           ` Eric Sunshine
2016-04-20 13:24         ` [PATCH v2 04/12] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
2016-04-21  7:20           ` Eric Sunshine
2016-04-21  8:19             ` Duy Nguyen
2016-04-21  9:33               ` Duy Nguyen
2016-04-21 14:23                 ` Eric Sunshine
2016-04-21 15:13                   ` Jeff King
2016-04-21 15:37                     ` Junio C Hamano
2016-04-21 15:40                       ` Jeff King
2016-04-21 15:42                         ` Junio C Hamano
2016-04-20 13:24         ` [PATCH v2 05/12] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
2016-04-20 13:24         ` [PATCH v2 06/12] wt-status.c: split rebase detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
2016-04-20 13:48           ` Ramsay Jones
2016-04-20 13:54             ` Duy Nguyen
2016-04-20 13:24         ` [PATCH v2 07/12] wt-status.c: make wt_status_check_rebase() work on any worktree Nguyễn Thái Ngọc Duy
2016-04-20 13:24         ` [PATCH v2 08/12] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
2016-04-20 13:24         ` [PATCH v2 09/12] worktree.c: test if branch being rebased in another worktree Nguyễn Thái Ngọc Duy
2016-04-20 18:04           ` Junio C Hamano
2016-04-21  0:37             ` Duy Nguyen
2016-04-20 13:24         ` [PATCH v2 10/12] wt-status.c: split bisect detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
2016-04-20 13:24         ` [PATCH v2 11/12] worktree.c: test if branch being bisected in another worktree Nguyễn Thái Ngọc Duy
2016-04-20 13:24         ` [PATCH v2 12/12] branch: do not rename a branch under bisect or rebase Nguyễn Thái Ngọc Duy
2016-04-20 18:09         ` [PATCH v2 00/12] fix checking out a being-rebased branch Junio C Hamano
2016-04-22 13:01         ` [PATCH v3 00/13] nd/worktree-various-heads Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 01/13] dir.c: rename str(n)cmp_icase to fspath(n)cmp Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 02/13] path.c: add git_common_path() and strbuf_git_common_path() Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 03/13] worktree.c: store "id" instead of "git_dir" Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 04/13] worktree.c: make find_shared_symref() return struct worktree * Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 05/13] worktree.c: mark current worktree Nguyễn Thái Ngọc Duy
2016-05-06  7:51             ` Eric Sunshine
2016-05-06 10:21               ` Duy Nguyen
2016-05-10 14:14                 ` Duy Nguyen
2016-05-10 14:15                   ` [PATCH 1/7] completion: support git-worktree Nguyễn Thái Ngọc Duy
2016-05-10 14:15                     ` [PATCH 2/7] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
2016-05-11  6:16                       ` Eric Sunshine
2016-05-10 14:15                     ` [PATCH 3/7] git-worktree.txt: keep subcommand listing in alphabetical order Nguyễn Thái Ngọc Duy
2016-05-10 14:15                     ` [PATCH 4/7] worktree.c: use is_dot_or_dotdot() Nguyễn Thái Ngọc Duy
2016-05-10 14:15                     ` [PATCH 5/7] worktree.c: add clear_worktree() Nguyễn Thái Ngọc Duy
2016-05-11  6:36                       ` Eric Sunshine
2016-05-11  6:42                         ` Eric Sunshine
2016-05-22  8:58                         ` Duy Nguyen
2016-05-10 14:15                     ` [PATCH 6/7] worktree: avoid 0{40}, too many zeroes, hard to read Nguyễn Thái Ngọc Duy
2016-05-10 14:15                     ` [PATCH 7/7] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
2016-05-10 23:07                       ` Junio C Hamano
2016-05-11  0:46                         ` Duy Nguyen
2016-05-11  6:12                     ` [PATCH 1/7] completion: support git-worktree Eric Sunshine
2016-05-10 14:17                   ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
2016-05-10 14:17                     ` [PATCH 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
2016-05-13 16:32                       ` Eric Sunshine
2016-05-10 14:17                     ` [PATCH 3/5] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
2016-05-13 16:52                       ` Eric Sunshine
2016-05-22  9:53                         ` Duy Nguyen
2016-05-23  2:04                           ` Eric Sunshine
2016-05-10 14:17                     ` [PATCH 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
2016-05-16  0:09                       ` Eric Sunshine
2016-05-22 10:31                         ` Duy Nguyen
2016-05-23  4:21                           ` Eric Sunshine
2016-05-16  0:40                       ` Eric Sunshine
2016-05-10 14:17                     ` [PATCH 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
2016-05-16  0:42                       ` Eric Sunshine
2016-05-13 16:29                     ` [PATCH 1/5] worktree.c: add find_worktree_by_path() Eric Sunshine
2016-05-10 17:32                   ` [PATCH v3 05/13] worktree.c: mark current worktree Junio C Hamano
2016-05-10 23:03                   ` Junio C Hamano
2016-05-11  0:33                     ` Duy Nguyen
2016-05-22  9:33                   ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Nguyễn Thái Ngọc Duy
2016-05-22  9:33                     ` [PATCH v2 1/6] completion: support git-worktree Nguyễn Thái Ngọc Duy
2016-05-22  9:33                     ` [PATCH v2 2/6] worktree.c: rewrite mark_current_worktree() to avoid strbuf Nguyễn Thái Ngọc Duy
2016-05-22  9:33                     ` [PATCH v2 3/6] git-worktree.txt: keep subcommand listing in alphabetical order Nguyễn Thái Ngọc Duy
2016-05-22  9:33                     ` [PATCH v2 4/6] worktree.c: use is_dot_or_dotdot() Nguyễn Thái Ngọc Duy
2016-05-22  9:33                     ` [PATCH v2 5/6] worktree: avoid 0{40}, too many zeroes, hard to read Nguyễn Thái Ngọc Duy
2016-05-22  9:33                     ` [PATCH v2 6/6] worktree: simplify prefixing paths Nguyễn Thái Ngọc Duy
2016-05-22 23:32                       ` Eric Sunshine
2016-05-23  4:31                         ` Eric Sunshine
2016-05-23  9:26                           ` Duy Nguyen
2016-05-23  1:09                     ` [PATCH v2 0/6] nd/worktree-cleanup-post-head-protection update Eric Sunshine
2016-05-22 10:43                   ` [PATCH v2 0/5] worktree lock/unlock Nguyễn Thái Ngọc Duy
2016-05-22 10:43                     ` [PATCH v2 1/5] worktree.c: add find_worktree_by_path() Nguyễn Thái Ngọc Duy
2016-05-23  1:41                       ` Eric Sunshine
2016-05-23  4:11                       ` Eric Sunshine
2016-05-30  9:56                         ` Duy Nguyen
2016-05-22 10:43                     ` [PATCH v2 2/5] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
2016-05-22 10:43                     ` [PATCH v2 3/5] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
2016-05-22 10:43                     ` [PATCH v2 4/5] worktree: add "lock" command Nguyễn Thái Ngọc Duy
2016-05-23  4:36                       ` Eric Sunshine
2016-05-22 10:43                     ` [PATCH v2 5/5] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
2016-05-23  4:39                       ` Eric Sunshine
2016-05-23  4:51                     ` [PATCH v2 0/5] worktree lock/unlock Eric Sunshine
2016-05-23  9:23                       ` Duy Nguyen
2016-05-30 10:49                     ` [PATCH v3 0/6] " Nguyễn Thái Ngọc Duy
2016-05-30 10:49                       ` [PATCH v3 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
2016-05-30 10:49                       ` [PATCH v3 2/6] worktree.c: find_worktree() learns to identify worktrees by basename Nguyễn Thái Ngọc Duy
2016-05-31 17:51                         ` Junio C Hamano
2016-06-01 13:22                           ` Duy Nguyen
2016-06-01 18:44                             ` Junio C Hamano
2016-06-02  9:40                               ` Duy Nguyen
2016-06-02 16:49                                 ` Junio C Hamano
2016-06-03 11:11                                   ` Duy Nguyen
2016-06-03 15:30                                     ` Junio C Hamano
2016-06-05  7:15                                     ` Johannes Schindelin
2016-05-30 10:49                       ` [PATCH v3 3/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
2016-05-31 17:52                         ` Junio C Hamano
2016-05-30 10:49                       ` [PATCH v3 4/6] worktree.c: retrieve lock status (and optionally reason) in get_worktrees() Nguyễn Thái Ngọc Duy
2016-05-31 17:55                         ` Junio C Hamano
2016-06-01 13:02                           ` Duy Nguyen
2016-06-01 15:23                             ` Junio C Hamano
2016-05-30 10:49                       ` [PATCH v3 5/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
2016-05-31 18:10                         ` Junio C Hamano
2016-05-31 18:28                         ` Junio C Hamano
2016-05-30 10:49                       ` [PATCH v3 6/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
2016-05-31 18:12                         ` Junio C Hamano
2016-06-01 13:10                           ` Duy Nguyen
2016-05-31 18:31                         ` Junio C Hamano
2016-05-31 18:35                           ` Junio C Hamano
2016-06-03 12:19                       ` [PATCH v4 0/6] worktree lock/unlock Nguyễn Thái Ngọc Duy
2016-06-03 12:19                         ` [PATCH v4 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
2016-06-03 15:00                           ` Ramsay Jones
2016-06-13 12:22                             ` Duy Nguyen
2016-06-03 12:19                         ` [PATCH v4 2/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
2016-06-03 12:19                         ` [PATCH v4 3/6] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
2016-06-03 12:19                         ` [PATCH v4 4/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
2016-06-03 12:19                         ` [PATCH v4 5/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
2016-06-03 12:19                         ` [PATCH v4 6/6] worktree.c: find_worktree() search by path suffix Nguyễn Thái Ngọc Duy
2016-06-04  5:14                         ` [PATCH v4 0/6] worktree lock/unlock Junio C Hamano
2016-06-13 12:18                         ` [PATCH v5 " Nguyễn Thái Ngọc Duy
2016-06-13 12:18                           ` [PATCH v5 1/6] worktree.c: add find_worktree() Nguyễn Thái Ngọc Duy
2016-06-13 12:18                           ` [PATCH v5 2/6] worktree.c: add is_main_worktree() Nguyễn Thái Ngọc Duy
2016-06-13 12:18                           ` [PATCH v5 3/6] worktree.c: add is_worktree_locked() Nguyễn Thái Ngọc Duy
2016-06-13 12:18                           ` [PATCH v5 4/6] worktree: add "lock" command Nguyễn Thái Ngọc Duy
2016-06-13 12:18                           ` [PATCH v5 5/6] worktree: add "unlock" command Nguyễn Thái Ngọc Duy
2016-06-13 12:18                           ` [PATCH v5 6/6] worktree.c: find_worktree() search by path suffix Nguyễn Thái Ngọc Duy
2016-06-27  9:57                         ` [PATCH v4 0/6] worktree lock/unlock Torsten Bögershausen
2016-06-30 16:01                           ` [PATCH] fixup! worktree: add "lock" command Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 06/13] path.c: refactor and add worktree_git_path() Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 07/13] wt-status.c: split rebase detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 08/13] wt-status.c: make wt_status_check_rebase() work on any worktree Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 09/13] worktree.c: avoid referencing to worktrees[i] multiple times Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 10/13] worktree.c: check whether branch is rebased in another worktree Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 11/13] wt-status.c: split bisect detection out of wt_status_get_state() Nguyễn Thái Ngọc Duy
2016-04-22 13:01           ` [PATCH v3 12/13] worktree.c: check whether branch is bisected in another worktree Nguyễn Thái Ngọc Duy
2016-05-11  5:45             ` Eric Sunshine
2016-04-22 13:01           ` [PATCH v3 13/13] branch: do not rename a branch under bisect or rebase Nguyễn Thái Ngọc Duy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.