@@ -68,11 +68,11 @@ static void delete_worktrees_dir_if_empty(void)
}
/*
- * Return NULL if worktree entry should be pruned (along with reason for
- * pruning), otherwise return the path of the worktree itself. Caller is
- * responsible for freeing return value.
+ * Return true if worktree entry should be pruned, along with the reason for
+ * pruning. Otherwise, return false and the worktree's path, or NULL if it
+ * cannot be determined. Caller is responsible for freeing returned path.
*/
-static char *worktree_disposition(const char *id, struct strbuf *reason)
+static int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath)
{
struct stat st;
char *path;
@@ -80,19 +80,22 @@ static char *worktree_disposition(const char *id, struct strbuf *reason)
size_t len;
ssize_t read_result;
+ *wtpath = NULL;
if (!is_directory(git_path("worktrees/%s", id))) {
strbuf_addstr(reason, _("not a valid directory"));
- return NULL;
+ 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 NULL;
+ return 1;
}
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
if (fd < 0) {
strbuf_addf(reason, _("unable to read gitdir file (%s)"),
strerror(errno));
- return NULL;
+ return 1;
}
len = xsize_t(st.st_size);
path = xmallocz(len);
@@ -103,7 +106,7 @@ static char *worktree_disposition(const char *id, struct strbuf *reason)
strerror(errno));
close(fd);
free(path);
- return NULL;
+ return 1;
}
close(fd);
@@ -112,29 +115,29 @@ static char *worktree_disposition(const char *id, struct strbuf *reason)
_("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
(uintmax_t)len, (uintmax_t)read_result);
free(path);
- return NULL;
+ return 1;
}
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
len--;
if (!len) {
strbuf_addstr(reason, _("invalid gitdir file"));
free(path);
- return NULL;
+ return 1;
}
path[len] = '\0';
if (!file_exists(path)) {
- if (file_exists(git_path("worktrees/%s/locked", id)))
- return path;
if (stat(git_path("worktrees/%s/index", id), &st) ||
st.st_mtime <= expire) {
strbuf_addstr(reason, _("gitdir file points to non-existent location"));
free(path);
- return NULL;
+ return 1;
} else {
- return path;
+ *wtpath = path;
+ return 0;
}
}
- return path;
+ *wtpath = path;
+ return 0;
}
static void prune_worktree(const char *id, const char *reason)
@@ -153,7 +156,12 @@ static int prune_cmp(const void *a, const void *b)
if ((c = fspathcmp(x->string, y->string)))
return c;
- /* paths same; main worktee (util==0) sorts above all others */
+ /*
+ * paths same; prune_dupes() removes all but the first worktree entry
+ * having the same path, so sort main worktree ('util' is NULL) above
+ * linked worktrees ('util' not NULL) since main worktree can't be
+ * removed
+ */
if (!x->util)
return -1;
if (!y->util)
@@ -176,7 +184,7 @@ static void prune_dups(struct string_list *l)
static void prune_worktrees(void)
{
struct strbuf reason = STRBUF_INIT;
- struct strbuf main = STRBUF_INIT;
+ struct strbuf main_path = STRBUF_INIT;
struct string_list kept = STRING_LIST_INIT_NODUP;
DIR *dir = opendir(git_path("worktrees"));
struct dirent *d;
@@ -187,19 +195,17 @@ static void prune_worktrees(void)
if (is_dot_or_dotdot(d->d_name))
continue;
strbuf_reset(&reason);
- path = worktree_disposition(d->d_name, &reason);
- if (path) {
+ if (should_prune_worktree(d->d_name, &reason, &path))
+ prune_worktree(d->d_name, reason.buf);
+ else if (path)
string_list_append(&kept, path)->util = xstrdup(d->d_name);
- continue;
- }
- prune_worktree(d->d_name, reason.buf);
}
closedir(dir);
- strbuf_add_absolute_path(&main, get_git_common_dir());
+ strbuf_add_absolute_path(&main_path, get_git_common_dir());
/* massage main worktree absolute path to match 'gitdir' content */
- strbuf_strip_suffix(&main, "/.");
- string_list_append(&kept, strbuf_detach(&main, 0));
+ strbuf_strip_suffix(&main_path, "/.");
+ string_list_append(&kept, strbuf_detach(&main_path, NULL));
prune_dups(&kept);
string_list_clear(&kept, 1);
@@ -69,21 +69,11 @@ test_expect_success 'prune directories with gitdir pointing to nowhere' '
'
test_expect_success 'not prune locked checkout' '
- 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 &&
+ test_when_finished rm -r .git/worktrees &&
mkdir -p .git/worktrees/ghi &&
- : >.git/worktrees/ghi/gitdir &&
: >.git/worktrees/ghi/locked &&
git worktree prune &&
- ! test -d .git/worktrees/ghi
+ test -d .git/worktrees/ghi
'
test_expect_success 'not prune recent checkouts' '
@@ -113,7 +113,7 @@ test_expect_success 'move locked worktree (force)' '
'
test_expect_success 'refuse to move worktree atop existing path' '
- > bobble &&
+ >bobble &&
git worktree add --detach beeble &&
test_must_fail git worktree move beeble bobble
'