4 #include "parse-options.h"
5 #include "argv-array.h"
6 #include "run-command.h"
8 static const char * const worktree_usage[] = {
9 N_("git worktree add [<options>] <path> <branch>"),
10 N_("git worktree prune [<options>]"),
16 static unsigned long expire;
18 static int prune_worktree(const char *id, struct strbuf *reason)
24 if (!is_directory(git_path("worktrees/%s", id))) {
25 strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
28 if (file_exists(git_path("worktrees/%s/locked", id)))
30 if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
31 strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
34 fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
36 strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
41 path = xmalloc(len + 1);
42 read_in_full(fd, path, len);
44 while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
47 strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
52 if (!file_exists(path)) {
56 * the repo is moved manually and has not been
59 if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
62 if (st.st_mtime <= expire) {
63 strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
73 static void prune_worktrees(void)
75 struct strbuf reason = STRBUF_INIT;
76 struct strbuf path = STRBUF_INIT;
77 DIR *dir = opendir(git_path("worktrees"));
82 while ((d = readdir(dir)) != NULL) {
83 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
85 strbuf_reset(&reason);
86 if (!prune_worktree(d->d_name, &reason))
88 if (show_only || verbose)
89 printf("%s\n", reason.buf);
93 strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
94 ret = remove_dir_recursively(&path, 0);
95 if (ret < 0 && errno == ENOTDIR)
96 ret = unlink(path.buf);
98 error(_("failed to remove: %s"), strerror(errno));
102 rmdir(git_path("worktrees"));
103 strbuf_release(&reason);
104 strbuf_release(&path);
107 static int prune(int ac, const char **av, const char *prefix)
109 struct option options[] = {
110 OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
111 OPT__VERBOSE(&verbose, N_("report pruned objects")),
112 OPT_EXPIRY_DATE(0, "expire", &expire,
113 N_("expire objects older than <time>")),
118 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
120 usage_with_options(worktree_usage, options);
125 static int add(int ac, const char **av, const char *prefix)
127 struct child_process c;
128 int force = 0, detach = 0;
129 const char *new_branch = NULL, *new_branch_force = NULL;
130 const char *path, *branch;
131 struct argv_array cmd = ARGV_ARRAY_INIT;
132 struct option options[] = {
133 OPT__FORCE(&force, N_("checkout <branch> even if already checked out in other worktree")),
134 OPT_STRING('b', NULL, &new_branch, N_("branch"),
135 N_("create a new branch")),
136 OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
137 N_("create or reset a branch")),
138 OPT_BOOL(0, "detach", &detach, N_("detach HEAD at named commit")),
142 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
143 if (new_branch && new_branch_force)
144 die(_("-b and -B are mutually exclusive"));
146 usage_with_options(worktree_usage, options);
148 path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
151 argv_array_push(&cmd, "checkout");
152 argv_array_pushl(&cmd, "--to", path, NULL);
154 argv_array_push(&cmd, "--ignore-other-worktrees");
156 argv_array_pushl(&cmd, "-b", new_branch, NULL);
157 if (new_branch_force)
158 argv_array_pushl(&cmd, "-B", new_branch_force, NULL);
160 argv_array_push(&cmd, "--detach");
161 argv_array_push(&cmd, branch);
163 memset(&c, 0, sizeof(c));
166 return run_command(&c);
169 int cmd_worktree(int ac, const char **av, const char *prefix)
171 struct option options[] = {
176 usage_with_options(worktree_usage, options);
177 if (!strcmp(av[1], "add"))
178 return add(ac - 1, av + 1, prefix);
179 if (!strcmp(av[1], "prune"))
180 return prune(ac - 1, av + 1, prefix);
181 usage_with_options(worktree_usage, options);