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 <pjones@xxxxxxxxxx> --- 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