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
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html