worktree: new place for "git prune --worktrees"
Commit 23af91d
(prune: strategies for linked checkouts - 2014-11-30)
adds "--worktrees" to "git prune" without realizing that "git prune" is
for object database only. This patch moves the same functionality to a
new command "git worktree".
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
This commit is contained in:

committed by
Junio C Hamano

parent
e1c1ab9d25
commit
df0b6cfbda
133
builtin/worktree.c
Normal file
133
builtin/worktree.c
Normal file
@ -0,0 +1,133 @@
|
||||
#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);
|
||||
}
|
Reference in New Issue
Block a user