Skip to content
Permalink
df0b6cfbda
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
133 lines (124 sloc) 3.23 KB
#include "cache.h"
#include "builtin.h"
#include "dir.h"
#include "parse-options.h"
static const char * const worktree_usage[] = {
N_("git worktree prune [<options>]"),
NULL
};
static int show_only;
static int verbose;
static unsigned long expire;
static int prune_worktree(const char *id, struct strbuf *reason)
{
struct stat st;
char *path;
int fd, len;
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 = st.st_size;
path = xmalloc(len + 1);
read_in_full(fd, path, len);
close(fd);
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)) {
struct stat st_link;
free(path);
/*
* the repo is moved manually and has not been
* accessed since?
*/
if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
st_link.st_nlink > 1)
return 0;
if (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;
struct strbuf path = STRBUF_INIT;
DIR *dir = opendir(git_path("worktrees"));
struct dirent *d;
int ret;
if (!dir)
return;
while ((d = readdir(dir)) != NULL) {
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
strbuf_reset(&reason);
if (!prune_worktree(d->d_name, &reason))
continue;
if (show_only || verbose)
printf("%s\n", reason.buf);
if (show_only)
continue;
strbuf_reset(&path);
strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
ret = remove_dir_recursively(&path, 0);
if (ret < 0 && errno == ENOTDIR)
ret = unlink(path.buf);
if (ret)
error(_("failed to remove: %s"), strerror(errno));
}
closedir(dir);
if (!show_only)
rmdir(git_path("worktrees"));
strbuf_release(&reason);
strbuf_release(&path);
}
static int prune(int ac, const char **av, const char *prefix)
{
struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
OPT__VERBOSE(&verbose, N_("report pruned objects")),
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("expire objects older than <time>")),
OPT_END()
};
expire = ULONG_MAX;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
if (ac)
usage_with_options(worktree_usage, options);
prune_worktrees();
return 0;
}
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
OPT_END()
};
if (ac < 2)
usage_with_options(worktree_usage, options);
if (!strcmp(av[1], "prune"))
return prune(ac - 1, av + 1, prefix);
usage_with_options(worktree_usage, options);
}