The .git/worktrees/<id>/locked file created by "git worktree lock" is intended to prevent a missing worktree -- which might reside on a removable device or network share -- from being pruned. It is not meant to prevent a corrupt worktree from being pruned, yet it short-circuits almost all "git worktree prune" corruption checks. This can make it impossible[1] to prune a worktree which becomes corrupt after the lock is placed since "git worktree prune" won't prune it, and it may not even be possible to unlock it with "git worktree unlock", depending upon the nature of the corruption. Therefore, delay the check for .git/worktrees/<id>/locked until after all forms of corruption have been checked so that it behaves as originally intended (to wit: preventing pruning of a missing worktree only). [1]: Impossible, that is, without manually mucking around with .git/worktrees/<id>/ administrative files. Signed-off-by: Eric Sunshine <sunshine@xxxxxxxxxxxxxx> --- builtin/worktree.c | 4 ++-- t/t2401-worktree-prune.sh | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index 9b15f19fc5..f7351413af 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -79,8 +79,6 @@ static int prune_worktree(const char *id, struct strbuf *reason) strbuf_addstr(reason, _("not a valid directory")); return 1; } - if (file_exists(git_path("worktrees/%s/locked", id))) - return 0; if (stat(git_path("worktrees/%s/gitdir", id), &st)) { strbuf_addstr(reason, _("gitdir file does not exist")); return 1; @@ -121,6 +119,8 @@ static int prune_worktree(const char *id, struct strbuf *reason) path[len] = '\0'; if (!file_exists(path)) { free(path); + if (file_exists(git_path("worktrees/%s/locked", id))) + return 0; if (stat(git_path("worktrees/%s/index", id), &st) || st.st_mtime <= expire) { strbuf_addstr(reason, _("gitdir file points to non-existent location")); diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh index b7d6d5d45a..9be8e97d66 100755 --- a/t/t2401-worktree-prune.sh +++ b/t/t2401-worktree-prune.sh @@ -69,13 +69,23 @@ test_expect_success 'prune directories with gitdir pointing to nowhere' ' ' test_expect_success 'not prune locked checkout' ' - test_when_finished rm -r .git/worktrees && - mkdir -p .git/worktrees/ghi && + test_when_finished rm -fr .git/worktrees ghi && + git worktree add ghi && : >.git/worktrees/ghi/locked && + rm -r ghi && git worktree prune && test -d .git/worktrees/ghi ' +test_expect_success 'prune corrupt despite lock' ' + test_when_finished rm -fr .git/worktrees ghi && + mkdir -p .git/worktrees/ghi && + : >.git/worktrees/ghi/gitdir && + : >.git/worktrees/ghi/locked && + git worktree prune && + ! test -d .git/worktrees/ghi +' + test_expect_success 'not prune recent checkouts' ' test_when_finished rm -r .git/worktrees && git worktree add jlm HEAD && -- 2.27.0.290.gba653c62da