Add delete_worktrees_dir_if_empty() and prune_worktree() to the public
API, so they can be used from more places.  Also add a new function,
prune_worktree_if_missing(), which prunes unlocked worktrees if they
aren't present on the filesystem.

Signed-off-by: Peter Jones <pjo...@redhat.com>
---
 builtin/worktree.c | 73 +-------------------------------------
 worktree.c         | 88 ++++++++++++++++++++++++++++++++++++++++++++++
 worktree.h         | 19 ++++++++++
 3 files changed, 108 insertions(+), 72 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 86305cc1fe1..8ff37309be9 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -62,77 +62,6 @@ static int delete_git_dir(const char *id)
        return ret;
 }
 
-static void delete_worktrees_dir_if_empty(void)
-{
-       rmdir(git_path("worktrees")); /* ignore failed removal */
-}
-
-static int prune_worktree(const char *id, struct strbuf *reason)
-{
-       struct stat st;
-       char *path;
-       int fd;
-       size_t len;
-       ssize_t read_result;
-
-       if (!is_directory(git_path("worktrees/%s", id))) {
-               strbuf_addf(reason, _("Removing worktrees/%s: not a valid 
directory"), id);
-               return 1;
-       }
-       if (file_exists(git_path("worktrees/%s/locked", id)))
-               return 0;
-       if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
-               strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does 
not exist"), id);
-               return 1;
-       }
-       fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
-       if (fd < 0) {
-               strbuf_addf(reason, _("Removing worktrees/%s: unable to read 
gitdir file (%s)"),
-                           id, strerror(errno));
-               return 1;
-       }
-       len = xsize_t(st.st_size);
-       path = xmallocz(len);
-
-       read_result = read_in_full(fd, path, len);
-       if (read_result < 0) {
-               strbuf_addf(reason, _("Removing worktrees/%s: unable to read 
gitdir file (%s)"),
-                           id, strerror(errno));
-               close(fd);
-               free(path);
-               return 1;
-       }
-       close(fd);
-
-       if (read_result != len) {
-               strbuf_addf(reason,
-                           _("Removing worktrees/%s: short read (expected 
%"PRIuMAX" bytes, read %"PRIuMAX")"),
-                           id, (uintmax_t)len, (uintmax_t)read_result);
-               free(path);
-               return 1;
-       }
-       while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
-               len--;
-       if (!len) {
-               strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir 
file"), id);
-               free(path);
-               return 1;
-       }
-       path[len] = '\0';
-       if (!file_exists(path)) {
-               free(path);
-               if (stat(git_path("worktrees/%s/index", id), &st) ||
-                   st.st_mtime <= expire) {
-                       strbuf_addf(reason, _("Removing worktrees/%s: gitdir 
file points to non-existent location"), id);
-                       return 1;
-               } else {
-                       return 0;
-               }
-       }
-       free(path);
-       return 0;
-}
-
 static void prune_worktrees(void)
 {
        struct strbuf reason = STRBUF_INIT;
@@ -144,7 +73,7 @@ static void prune_worktrees(void)
                if (is_dot_or_dotdot(d->d_name))
                        continue;
                strbuf_reset(&reason);
-               if (!prune_worktree(d->d_name, &reason))
+               if (!prune_worktree(d->d_name, &reason, expire))
                        continue;
                if (show_only || verbose)
                        printf("%s\n", reason.buf);
diff --git a/worktree.c b/worktree.c
index 4924805c389..08454a4e65d 100644
--- a/worktree.c
+++ b/worktree.c
@@ -608,3 +608,91 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
        free_worktrees(worktrees);
        return ret;
 }
+
+void delete_worktrees_dir_if_empty(void)
+{
+       rmdir(git_path("worktrees")); /* ignore failed removal */
+}
+
+int prune_worktree(const char *id, struct strbuf *reason, timestamp_t expire)
+{
+       struct stat st;
+       char *path;
+       int fd;
+       size_t len;
+       ssize_t read_result;
+
+       if (!is_directory(git_path("worktrees/%s", id))) {
+               strbuf_addf(reason, _("Removing worktrees/%s: not a valid 
directory"), id);
+               return 1;
+       }
+       if (file_exists(git_path("worktrees/%s/locked", id)))
+               return 0;
+       if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
+               strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does 
not exist"), id);
+               return 1;
+       }
+       fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
+       if (fd < 0) {
+               strbuf_addf(reason, _("Removing worktrees/%s: unable to read 
gitdir file (%s)"),
+                           id, strerror(errno));
+               return 1;
+       }
+       len = xsize_t(st.st_size);
+       path = xmallocz(len);
+
+       read_result = read_in_full(fd, path, len);
+       if (read_result < 0) {
+               strbuf_addf(reason, _("Removing worktrees/%s: unable to read 
gitdir file (%s)"),
+                           id, strerror(errno));
+               close(fd);
+               free(path);
+               return 1;
+       }
+       close(fd);
+
+       if (read_result != len) {
+               strbuf_addf(reason,
+                           _("Removing worktrees/%s: short read (expected 
%"PRIuMAX" bytes, read %"PRIuMAX")"),
+                           id, (uintmax_t)len, (uintmax_t)read_result);
+               free(path);
+               return 1;
+       }
+       while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
+               len--;
+       if (!len) {
+               strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir 
file"), id);
+               free(path);
+               return 1;
+       }
+       path[len] = '\0';
+       if (!file_exists(path)) {
+               free(path);
+               if (stat(git_path("worktrees/%s/index", id), &st) ||
+                   st.st_mtime <= expire) {
+                       strbuf_addf(reason, _("Removing worktrees/%s: gitdir 
file points to non-existent location"), id);
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
+       free(path);
+       return 0;
+}
+
+int prune_worktree_if_missing(const struct worktree *wt)
+{
+       struct strbuf reason = STRBUF_INIT;
+       int ret;
+
+       if (is_worktree_locked(wt) ||
+           access(wt->path, F_OK) >= 0 ||
+           (errno != ENOENT && errno == ENOTDIR)) {
+               errno = EEXIST;
+               return -1;
+       }
+
+       strbuf_addf(&reason, _("Removing worktrees/%s: worktree directory is 
not present"), wt->id);
+       ret = prune_worktree(wt->id, &reason, TIME_MAX);
+       return ret;
+}
diff --git a/worktree.h b/worktree.h
index 5ff16c414b5..636bbb1c449 100644
--- a/worktree.h
+++ b/worktree.h
@@ -137,4 +137,23 @@ void strbuf_worktree_ref(const struct worktree *wt,
 const char *worktree_ref(const struct worktree *wt,
                         const char *refname);
 
+/*
+ * Clean up the 'worktrees' directory, if necessary.
+ */
+void delete_worktrees_dir_if_empty(void);
+
+/*
+ * Prune a worktree if it's older than expire.
+ * Returns 0 on success, < 0 on failure.
+ */
+int prune_worktree(const char *id, struct strbuf *reason, timestamp_t expire);
+
+/*
+ * Prune a worktree if it is not locked and is no longer present at the
+ * checked out location.
+ * Returns < 0 if the checkout is there, if the worktree is locked, or if
+ * pruning fails.
+ */
+int prune_worktree_if_missing(const struct worktree *wt);
+
 #endif
-- 
2.23.0

Reply via email to