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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html