Permalink
Cannot retrieve contributors at this time
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?
git/builtin/worktree.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
133 lines (124 sloc)
3.23 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |