blob: 9fe9e442c7a085169acf7dd49fe87ebc2ccdcb38 [file] [log] [blame]
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +07001#include "cache.h"
Thomas Gummerer4e853332017-11-26 19:43:54 +00002#include "checkout.h"
Brandon Williamsb2141fc2017-06-14 11:07:36 -07003#include "config.h"
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +07004#include "builtin.h"
5#include "dir.h"
6#include "parse-options.h"
Eric Sunshinefc563612015-07-06 13:30:50 -04007#include "argv-array.h"
Eric Sunshinef7c9dac2015-07-17 19:00:13 -04008#include "branch.h"
9#include "refs.h"
Eric Sunshinefc563612015-07-06 13:30:50 -040010#include "run-command.h"
Eric Sunshineb979d952015-07-06 13:30:55 -040011#include "sigchain.h"
Nguyễn Thái Ngọc Duy00a6d4d2019-01-05 12:08:40 +070012#include "submodule.h"
Michael Rappazzobb9c03b2015-10-08 13:01:05 -040013#include "utf8.h"
14#include "worktree.h"
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070015
16static const char * const worktree_usage[] = {
Junio C Hamanob780e442018-01-17 10:32:32 -080017 N_("git worktree add [<options>] <path> [<commit-ish>]"),
Michael Rappazzobb9c03b2015-10-08 13:01:05 -040018 N_("git worktree list [<options>]"),
Nguyễn Thái Ngọc Duy58142c02016-06-13 19:18:24 +070019 N_("git worktree lock [<options>] <path>"),
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +070020 N_("git worktree move <worktree> <new-path>"),
Nguyễn Thái Ngọc Duy7b722d92016-05-22 16:33:53 +070021 N_("git worktree prune [<options>]"),
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +070022 N_("git worktree remove [<options>] <worktree>"),
Nguyễn Thái Ngọc Duy6d308622016-06-13 19:18:25 +070023 N_("git worktree unlock <path>"),
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070024 NULL
25};
26
Eric Sunshine5dd6e232015-07-17 19:00:07 -040027struct add_opts {
28 int force;
29 int detach;
Elia Pinto371979c2018-08-15 20:56:30 +000030 int quiet;
Ray Zhangef2a0ac2016-03-29 10:11:01 +000031 int checkout;
Nguyễn Thái Ngọc Duy507e6e92017-04-12 20:58:05 +070032 int keep_locked;
Eric Sunshine5dd6e232015-07-17 19:00:07 -040033};
34
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070035static int show_only;
36static int verbose;
Thomas Gummerere92445a2017-11-29 20:04:51 +000037static int guess_remote;
Johannes Schindelindddbad72017-04-26 21:29:31 +020038static timestamp_t expire;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070039
Thomas Gummerere92445a2017-11-29 20:04:51 +000040static int git_worktree_config(const char *var, const char *value, void *cb)
41{
42 if (!strcmp(var, "worktree.guessremote")) {
43 guess_remote = git_config_bool(var, value);
44 return 0;
45 }
46
47 return git_default_config(var, value, cb);
48}
49
Eric Sunshine602aaed2018-08-28 17:20:20 -040050static int delete_git_dir(const char *id)
Eric Sunshinee5353be2018-08-28 17:20:19 -040051{
52 struct strbuf sb = STRBUF_INIT;
Eric Sunshine602aaed2018-08-28 17:20:20 -040053 int ret;
Eric Sunshinee5353be2018-08-28 17:20:19 -040054
Eric Sunshine602aaed2018-08-28 17:20:20 -040055 strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
56 ret = remove_dir_recursively(&sb, 0);
57 if (ret < 0 && errno == ENOTDIR)
58 ret = unlink(sb.buf);
59 if (ret)
Eric Sunshinee5353be2018-08-28 17:20:19 -040060 error_errno(_("failed to delete '%s'"), sb.buf);
Eric Sunshinee5353be2018-08-28 17:20:19 -040061 strbuf_release(&sb);
62 return ret;
63}
64
Eric Sunshine3a540432018-08-28 17:20:26 -040065static void delete_worktrees_dir_if_empty(void)
66{
67 rmdir(git_path("worktrees")); /* ignore failed removal */
68}
69
Eric Sunshine4a3ce472020-06-10 02:30:46 -040070/*
71 * Return true if worktree entry should be pruned, along with the reason for
72 * pruning. Otherwise, return false and the worktree's path, or NULL if it
73 * cannot be determined. Caller is responsible for freeing returned path.
74 */
75static int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath)
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070076{
77 struct stat st;
78 char *path;
Jeff King228740b2017-09-27 02:02:21 -040079 int fd;
80 size_t len;
Jeff King8a1a8d22017-09-27 02:02:27 -040081 ssize_t read_result;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070082
Eric Sunshine4a3ce472020-06-10 02:30:46 -040083 *wtpath = NULL;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070084 if (!is_directory(git_path("worktrees/%s", id))) {
Eric Sunshinec9b77f22020-06-08 02:23:49 -040085 strbuf_addstr(reason, _("not a valid directory"));
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070086 return 1;
87 }
88 if (file_exists(git_path("worktrees/%s/locked", id)))
89 return 0;
90 if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
Eric Sunshinec9b77f22020-06-08 02:23:49 -040091 strbuf_addstr(reason, _("gitdir file does not exist"));
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070092 return 1;
93 }
94 fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
95 if (fd < 0) {
Eric Sunshinec9b77f22020-06-08 02:23:49 -040096 strbuf_addf(reason, _("unable to read gitdir file (%s)"),
97 strerror(errno));
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +070098 return 1;
99 }
Jeff King228740b2017-09-27 02:02:21 -0400100 len = xsize_t(st.st_size);
Jeff King3733e692016-02-22 17:44:28 -0500101 path = xmallocz(len);
Jeff King8a1a8d22017-09-27 02:02:27 -0400102
103 read_result = read_in_full(fd, path, len);
104 if (read_result < 0) {
Eric Sunshinec9b77f22020-06-08 02:23:49 -0400105 strbuf_addf(reason, _("unable to read gitdir file (%s)"),
106 strerror(errno));
Jeff King8a1a8d22017-09-27 02:02:27 -0400107 close(fd);
108 free(path);
109 return 1;
110 }
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700111 close(fd);
Jeff King8a1a8d22017-09-27 02:02:27 -0400112
113 if (read_result != len) {
114 strbuf_addf(reason,
Eric Sunshinec9b77f22020-06-08 02:23:49 -0400115 _("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
116 (uintmax_t)len, (uintmax_t)read_result);
Jeff King8a1a8d22017-09-27 02:02:27 -0400117 free(path);
118 return 1;
119 }
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700120 while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
121 len--;
122 if (!len) {
Eric Sunshinec9b77f22020-06-08 02:23:49 -0400123 strbuf_addstr(reason, _("invalid gitdir file"));
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700124 free(path);
125 return 1;
126 }
127 path[len] = '\0';
128 if (!file_exists(path)) {
Nguyễn Thái Ngọc Duy327864a2018-03-15 17:44:12 +0100129 if (stat(git_path("worktrees/%s/index", id), &st) ||
130 st.st_mtime <= expire) {
Eric Sunshinec9b77f22020-06-08 02:23:49 -0400131 strbuf_addstr(reason, _("gitdir file points to non-existent location"));
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400132 free(path);
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700133 return 1;
134 } else {
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400135 *wtpath = path;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700136 return 0;
137 }
138 }
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400139 *wtpath = path;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700140 return 0;
141}
142
Eric Sunshinedd9609a2020-06-10 02:30:45 -0400143static void prune_worktree(const char *id, const char *reason)
144{
145 if (show_only || verbose)
146 printf_ln(_("Removing %s/%s: %s"), "worktrees", id, reason);
147 if (!show_only)
148 delete_git_dir(id);
149}
150
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400151static int prune_cmp(const void *a, const void *b)
152{
153 const struct string_list_item *x = a;
154 const struct string_list_item *y = b;
155 int c;
156
157 if ((c = fspathcmp(x->string, y->string)))
158 return c;
Eric Sunshine916133e2020-06-10 02:30:47 -0400159 /*
160 * paths same; prune_dupes() removes all but the first worktree entry
161 * having the same path, so sort main worktree ('util' is NULL) above
162 * linked worktrees ('util' not NULL) since main worktree can't be
163 * removed
164 */
165 if (!x->util)
166 return -1;
167 if (!y->util)
168 return 1;
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400169 /* paths same; sort by .git/worktrees/<id> */
170 return strcmp(x->util, y->util);
171}
172
173static void prune_dups(struct string_list *l)
174{
175 int i;
176
177 QSORT(l->items, l->nr, prune_cmp);
178 for (i = 1; i < l->nr; i++) {
179 if (!fspathcmp(l->items[i].string, l->items[i - 1].string))
180 prune_worktree(l->items[i].util, "duplicate entry");
181 }
182}
183
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700184static void prune_worktrees(void)
185{
186 struct strbuf reason = STRBUF_INIT;
Eric Sunshine916133e2020-06-10 02:30:47 -0400187 struct strbuf main_path = STRBUF_INIT;
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400188 struct string_list kept = STRING_LIST_INIT_NODUP;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700189 DIR *dir = opendir(git_path("worktrees"));
190 struct dirent *d;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700191 if (!dir)
192 return;
193 while ((d = readdir(dir)) != NULL) {
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400194 char *path;
Nguyễn Thái Ngọc Duyafb9e302016-05-22 16:33:54 +0700195 if (is_dot_or_dotdot(d->d_name))
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700196 continue;
197 strbuf_reset(&reason);
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400198 if (should_prune_worktree(d->d_name, &reason, &path))
199 prune_worktree(d->d_name, reason.buf);
200 else if (path)
201 string_list_append(&kept, path)->util = xstrdup(d->d_name);
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700202 }
203 closedir(dir);
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400204
Eric Sunshine916133e2020-06-10 02:30:47 -0400205 strbuf_add_absolute_path(&main_path, get_git_common_dir());
206 /* massage main worktree absolute path to match 'gitdir' content */
207 strbuf_strip_suffix(&main_path, "/.");
208 string_list_append(&kept, strbuf_detach(&main_path, NULL));
Eric Sunshine4a3ce472020-06-10 02:30:46 -0400209 prune_dups(&kept);
210 string_list_clear(&kept, 1);
211
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700212 if (!show_only)
Eric Sunshine3a540432018-08-28 17:20:26 -0400213 delete_worktrees_dir_if_empty();
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700214 strbuf_release(&reason);
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700215}
216
217static int prune(int ac, const char **av, const char *prefix)
218{
219 struct option options[] = {
220 OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
Patrick Steinhardt2488dca2017-02-06 14:13:59 +0100221 OPT__VERBOSE(&verbose, N_("report pruned working trees")),
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700222 OPT_EXPIRY_DATE(0, "expire", &expire,
Patrick Steinhardt2488dca2017-02-06 14:13:59 +0100223 N_("expire working trees older than <time>")),
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700224 OPT_END()
225 };
226
Johannes Schindelindddbad72017-04-26 21:29:31 +0200227 expire = TIME_MAX;
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +0700228 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
229 if (ac)
230 usage_with_options(worktree_usage, options);
231 prune_worktrees();
232 return 0;
233}
234
Eric Sunshineb979d952015-07-06 13:30:55 -0400235static char *junk_work_tree;
236static char *junk_git_dir;
237static int is_junk;
238static pid_t junk_pid;
239
240static void remove_junk(void)
241{
242 struct strbuf sb = STRBUF_INIT;
243 if (!is_junk || getpid() != junk_pid)
244 return;
245 if (junk_git_dir) {
246 strbuf_addstr(&sb, junk_git_dir);
247 remove_dir_recursively(&sb, 0);
248 strbuf_reset(&sb);
249 }
250 if (junk_work_tree) {
251 strbuf_addstr(&sb, junk_work_tree);
252 remove_dir_recursively(&sb, 0);
253 }
254 strbuf_release(&sb);
255}
256
257static void remove_junk_on_signal(int signo)
258{
259 remove_junk();
260 sigchain_pop(signo);
261 raise(signo);
262}
263
Eric Sunshinef5682b22015-07-06 13:30:57 -0400264static const char *worktree_basename(const char *path, int *olen)
265{
266 const char *name;
267 int len;
268
269 len = strlen(path);
270 while (len && is_dir_sep(path[len - 1]))
271 len--;
272
273 for (name = path + len - 1; name > path; name--)
274 if (is_dir_sep(*name)) {
275 name++;
276 break;
277 }
278
279 *olen = len;
280 return name;
281}
282
Eric Sunshine45059e62018-08-28 17:20:21 -0400283static void validate_worktree_add(const char *path, const struct add_opts *opts)
284{
Eric Sunshinecb56f552018-08-28 17:20:22 -0400285 struct worktree **worktrees;
286 struct worktree *wt;
287 int locked;
288
Eric Sunshine45059e62018-08-28 17:20:21 -0400289 if (file_exists(path) && !is_empty_dir(path))
290 die(_("'%s' already exists"), path);
Eric Sunshinecb56f552018-08-28 17:20:22 -0400291
292 worktrees = get_worktrees(0);
Eric Sunshinebb69b3b2020-02-24 04:08:48 -0500293 wt = find_worktree_by_path(worktrees, path);
Eric Sunshinecb56f552018-08-28 17:20:22 -0400294 if (!wt)
295 goto done;
296
Nickolai Belakovskid236f122018-10-29 23:24:09 -0700297 locked = !!worktree_lock_reason(wt);
Eric Sunshinee19831c2018-08-28 17:20:23 -0400298 if ((!locked && opts->force) || (locked && opts->force > 1)) {
299 if (delete_git_dir(wt->id))
300 die(_("unable to re-add worktree '%s'"), path);
301 goto done;
302 }
303
Eric Sunshinecb56f552018-08-28 17:20:22 -0400304 if (locked)
Eric Sunshinee19831c2018-08-28 17:20:23 -0400305 die(_("'%s' is a missing but locked worktree;\nuse 'add -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), path);
Eric Sunshinecb56f552018-08-28 17:20:22 -0400306 else
Eric Sunshinee19831c2018-08-28 17:20:23 -0400307 die(_("'%s' is a missing but already registered worktree;\nuse 'add -f' to override, or 'prune' or 'remove' to clear"), path);
Eric Sunshinecb56f552018-08-28 17:20:22 -0400308
309done:
310 free_worktrees(worktrees);
Eric Sunshine45059e62018-08-28 17:20:21 -0400311}
312
Eric Sunshine80a05482015-07-17 19:00:12 -0400313static int add_worktree(const char *path, const char *refname,
Eric Sunshine5dd6e232015-07-17 19:00:07 -0400314 const struct add_opts *opts)
Eric Sunshineb979d952015-07-06 13:30:55 -0400315{
316 struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
317 struct strbuf sb = STRBUF_INIT;
318 const char *name;
René Scharfe542aa252016-08-05 22:38:44 +0200319 struct child_process cp = CHILD_PROCESS_INIT;
Eric Sunshineae2a3822015-07-17 19:00:11 -0400320 struct argv_array child_env = ARGV_ARRAY_INIT;
Michal Suchanek7af01f22019-02-20 17:16:48 +0100321 unsigned int counter = 0;
322 int len, ret;
Eric Sunshinef7c9dac2015-07-17 19:00:13 -0400323 struct strbuf symref = STRBUF_INIT;
324 struct commit *commit = NULL;
Eric Sunshineade546b2017-12-07 16:20:17 -0500325 int is_branch = 0;
Nguyễn Thái Ngọc Duy1de16ae2019-03-08 16:28:34 +0700326 struct strbuf sb_name = STRBUF_INIT;
Eric Sunshineb979d952015-07-06 13:30:55 -0400327
Eric Sunshine45059e62018-08-28 17:20:21 -0400328 validate_worktree_add(path, opts);
Eric Sunshineb979d952015-07-06 13:30:55 -0400329
Eric Sunshinef7c9dac2015-07-17 19:00:13 -0400330 /* is 'refname' a branch or commit? */
Nguyễn Thái Ngọc Duy0ebf4a22016-02-15 20:35:32 +0700331 if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
Eric Sunshineade546b2017-12-07 16:20:17 -0500332 ref_exists(symref.buf)) {
333 is_branch = 1;
Eric Sunshinef7c9dac2015-07-17 19:00:13 -0400334 if (!opts->force)
Nguyễn Thái Ngọc Duy8d9fdd72016-04-22 20:01:33 +0700335 die_if_checked_out(symref.buf, 0);
Eric Sunshinef7c9dac2015-07-17 19:00:13 -0400336 }
Eric Sunshineade546b2017-12-07 16:20:17 -0500337 commit = lookup_commit_reference_by_name(refname);
338 if (!commit)
339 die(_("invalid reference: %s"), refname);
Eric Sunshinef7c9dac2015-07-17 19:00:13 -0400340
Eric Sunshinef5682b22015-07-06 13:30:57 -0400341 name = worktree_basename(path, &len);
Nguyễn Thái Ngọc Duy1de16ae2019-03-08 16:28:34 +0700342 strbuf_add(&sb, name, path + len - name);
343 sanitize_refname_component(sb.buf, &sb_name);
344 if (!sb_name.len)
345 BUG("How come '%s' becomes empty after sanitization?", sb.buf);
346 strbuf_reset(&sb);
347 name = sb_name.buf;
348 git_path_buf(&sb_repo, "worktrees/%s", name);
Eric Sunshineb979d952015-07-06 13:30:55 -0400349 len = sb_repo.len;
350 if (safe_create_leading_directories_const(sb_repo.buf))
351 die_errno(_("could not create leading directories of '%s'"),
352 sb_repo.buf);
Michal Suchanek7af01f22019-02-20 17:16:48 +0100353
354 while (mkdir(sb_repo.buf, 0777)) {
Eric Sunshineb979d952015-07-06 13:30:55 -0400355 counter++;
Michal Suchanek7af01f22019-02-20 17:16:48 +0100356 if ((errno != EEXIST) || !counter /* overflow */)
357 die_errno(_("could not create directory of '%s'"),
358 sb_repo.buf);
Eric Sunshineb979d952015-07-06 13:30:55 -0400359 strbuf_setlen(&sb_repo, len);
360 strbuf_addf(&sb_repo, "%d", counter);
361 }
362 name = strrchr(sb_repo.buf, '/') + 1;
363
364 junk_pid = getpid();
365 atexit(remove_junk);
366 sigchain_push_common(remove_junk_on_signal);
367
Eric Sunshineb979d952015-07-06 13:30:55 -0400368 junk_git_dir = xstrdup(sb_repo.buf);
369 is_junk = 1;
370
371 /*
372 * lock the incomplete repo so prune won't delete it, unlock
373 * after the preparation is over.
374 */
375 strbuf_addf(&sb, "%s/locked", sb_repo.buf);
Nguyễn Thái Ngọc Duy507e6e92017-04-12 20:58:05 +0700376 if (!opts->keep_locked)
377 write_file(sb.buf, "initializing");
378 else
379 write_file(sb.buf, "added with --lock");
Eric Sunshineb979d952015-07-06 13:30:55 -0400380
381 strbuf_addf(&sb_git, "%s/.git", path);
382 if (safe_create_leading_directories_const(sb_git.buf))
383 die_errno(_("could not create leading directories of '%s'"),
384 sb_git.buf);
385 junk_work_tree = xstrdup(path);
386
387 strbuf_reset(&sb);
388 strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
Junio C Hamano1f76a102015-08-24 13:20:39 -0700389 write_file(sb.buf, "%s", real_path(sb_git.buf));
390 write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
Eric Sunshineb979d952015-07-06 13:30:55 -0400391 real_path(get_git_common_dir()), name);
392 /*
393 * This is to keep resolve_ref() happy. We need a valid HEAD
Eric Sunshineed197a62015-07-17 19:00:15 -0400394 * or is_git_directory() will reject the directory. Any value which
395 * looks like an object ID will do since it will be immediately
396 * replaced by the symbolic-ref or update-ref invocation in the new
397 * worktree.
Eric Sunshineb979d952015-07-06 13:30:55 -0400398 */
Eric Sunshineb979d952015-07-06 13:30:55 -0400399 strbuf_reset(&sb);
400 strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
brian m. carlsonf6ca67d2019-08-18 20:04:19 +0000401 write_file(sb.buf, "%s", oid_to_hex(&null_oid));
Eric Sunshineb979d952015-07-06 13:30:55 -0400402 strbuf_reset(&sb);
403 strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
Junio C Hamano1f76a102015-08-24 13:20:39 -0700404 write_file(sb.buf, "../..");
Eric Sunshineb979d952015-07-06 13:30:55 -0400405
Eric Sunshineae2a3822015-07-17 19:00:11 -0400406 argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
407 argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
Eric Sunshineb979d952015-07-06 13:30:55 -0400408 cp.git_cmd = 1;
Eric Sunshine7f44e3d2015-07-17 19:00:14 -0400409
Eric Sunshineade546b2017-12-07 16:20:17 -0500410 if (!is_branch)
Eric Sunshine7f44e3d2015-07-17 19:00:14 -0400411 argv_array_pushl(&cp.args, "update-ref", "HEAD",
brian m. carlsonf2fd0762015-11-10 02:22:28 +0000412 oid_to_hex(&commit->object.oid), NULL);
Elia Pinto371979c2018-08-15 20:56:30 +0000413 else {
Eric Sunshine7f44e3d2015-07-17 19:00:14 -0400414 argv_array_pushl(&cp.args, "symbolic-ref", "HEAD",
415 symref.buf, NULL);
Elia Pinto371979c2018-08-15 20:56:30 +0000416 if (opts->quiet)
417 argv_array_push(&cp.args, "--quiet");
418 }
419
Eric Sunshine7f44e3d2015-07-17 19:00:14 -0400420 cp.env = child_env.argv;
421 ret = run_command(&cp);
422 if (ret)
423 goto done;
424
Ray Zhangef2a0ac2016-03-29 10:11:01 +0000425 if (opts->checkout) {
426 cp.argv = NULL;
427 argv_array_clear(&cp.args);
Philippe Blain4782cf22019-10-27 17:16:25 +0000428 argv_array_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
Elia Pinto371979c2018-08-15 20:56:30 +0000429 if (opts->quiet)
430 argv_array_push(&cp.args, "--quiet");
Ray Zhangef2a0ac2016-03-29 10:11:01 +0000431 cp.env = child_env.argv;
432 ret = run_command(&cp);
433 if (ret)
434 goto done;
Eric Sunshineb979d952015-07-06 13:30:55 -0400435 }
Ray Zhangef2a0ac2016-03-29 10:11:01 +0000436
437 is_junk = 0;
Ævar Arnfjörð Bjarmason88ce3ef2017-06-15 23:15:49 +0000438 FREE_AND_NULL(junk_work_tree);
439 FREE_AND_NULL(junk_git_dir);
Ray Zhangef2a0ac2016-03-29 10:11:01 +0000440
Eric Sunshine7f44e3d2015-07-17 19:00:14 -0400441done:
Nguyễn Thái Ngọc Duy507e6e92017-04-12 20:58:05 +0700442 if (ret || !opts->keep_locked) {
443 strbuf_reset(&sb);
444 strbuf_addf(&sb, "%s/locked", sb_repo.buf);
445 unlink_or_warn(sb.buf);
446 }
Eric Sunshineade546b2017-12-07 16:20:17 -0500447
448 /*
449 * Hook failure does not warrant worktree deletion, so run hook after
450 * is_junk is cleared, but do return appropriate code when hook fails.
451 */
Eric Sunshinea4bf1e32018-02-15 14:18:41 -0500452 if (!ret && opts->checkout) {
453 const char *hook = find_hook("post-checkout");
454 if (hook) {
455 const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
456 cp.git_cmd = 0;
457 cp.no_stdin = 1;
458 cp.stdout_to_stderr = 1;
459 cp.dir = path;
460 cp.env = env;
461 cp.argv = NULL;
Jeff Hostetler62062862019-02-22 14:25:06 -0800462 cp.trace2_hook_name = "post-checkout";
Eric Sunshinea4bf1e32018-02-15 14:18:41 -0500463 argv_array_pushl(&cp.args, absolute_path(hook),
464 oid_to_hex(&null_oid),
465 oid_to_hex(&commit->object.oid),
466 "1", NULL);
467 ret = run_command(&cp);
468 }
469 }
Eric Sunshineade546b2017-12-07 16:20:17 -0500470
Eric Sunshineae2a3822015-07-17 19:00:11 -0400471 argv_array_clear(&child_env);
Eric Sunshineb979d952015-07-06 13:30:55 -0400472 strbuf_release(&sb);
Eric Sunshinef7c9dac2015-07-17 19:00:13 -0400473 strbuf_release(&symref);
Eric Sunshineb979d952015-07-06 13:30:55 -0400474 strbuf_release(&sb_repo);
475 strbuf_release(&sb_git);
Nguyễn Thái Ngọc Duy1de16ae2019-03-08 16:28:34 +0700476 strbuf_release(&sb_name);
Eric Sunshineb979d952015-07-06 13:30:55 -0400477 return ret;
478}
479
Thomas Gummerer2c270022018-04-24 22:56:33 +0100480static void print_preparing_worktree_line(int detach,
481 const char *branch,
482 const char *new_branch,
483 int force_new_branch)
484{
485 if (force_new_branch) {
486 struct commit *commit = lookup_commit_reference_by_name(new_branch);
487 if (!commit)
488 printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
489 else
490 printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"),
491 new_branch,
Junio C Hamano10174da2018-05-23 14:38:18 +0900492 find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
Thomas Gummerer2c270022018-04-24 22:56:33 +0100493 } else if (new_branch) {
494 printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
495 } else {
496 struct strbuf s = STRBUF_INIT;
497 if (!detach && !strbuf_check_branch_ref(&s, branch) &&
498 ref_exists(s.buf))
499 printf_ln(_("Preparing worktree (checking out '%s')"),
500 branch);
501 else {
502 struct commit *commit = lookup_commit_reference_by_name(branch);
503 if (!commit)
504 die(_("invalid reference: %s"), branch);
505 printf_ln(_("Preparing worktree (detached HEAD %s)"),
Junio C Hamano10174da2018-05-23 14:38:18 +0900506 find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
Thomas Gummerer2c270022018-04-24 22:56:33 +0100507 }
508 strbuf_release(&s);
509 }
510}
511
Thomas Gummerer6427f872018-04-24 22:56:34 +0100512static const char *dwim_branch(const char *path, const char **new_branch)
513{
514 int n;
515 const char *s = worktree_basename(path, &n);
Thomas Gummererf60a7b72018-04-24 22:56:35 +0100516 const char *branchname = xstrndup(s, n);
517 struct strbuf ref = STRBUF_INIT;
518
519 UNLEAK(branchname);
520 if (!strbuf_check_branch_ref(&ref, branchname) &&
521 ref_exists(ref.buf)) {
522 strbuf_release(&ref);
523 return branchname;
524 }
525
526 *new_branch = branchname;
Thomas Gummerer6427f872018-04-24 22:56:34 +0100527 if (guess_remote) {
528 struct object_id oid;
529 const char *remote =
Ævar Arnfjörð Bjarmason3c87aa92018-06-05 14:40:46 +0000530 unique_tracking_name(*new_branch, &oid, NULL);
Thomas Gummerer6427f872018-04-24 22:56:34 +0100531 return remote;
532 }
533 return NULL;
534}
535
Eric Sunshinefc563612015-07-06 13:30:50 -0400536static int add(int ac, const char **av, const char *prefix)
537{
Eric Sunshine5dd6e232015-07-17 19:00:07 -0400538 struct add_opts opts;
539 const char *new_branch_force = NULL;
Jeff Kinge4da43b2017-03-20 21:28:49 -0400540 char *path;
541 const char *branch;
Thomas Gummererd861d342018-04-24 22:56:32 +0100542 const char *new_branch = NULL;
Thomas Gummerere284e892017-11-26 19:43:53 +0000543 const char *opt_track = NULL;
Eric Sunshinefc563612015-07-06 13:30:50 -0400544 struct option options[] = {
Nguyễn Thái Ngọc Duy12247812018-02-09 18:01:42 +0700545 OPT__FORCE(&opts.force,
546 N_("checkout <branch> even if already checked out in other worktree"),
Nguyễn Thái Ngọc Duyfc3d4e02018-02-09 18:02:20 +0700547 PARSE_OPT_NOCOMPLETE),
Thomas Gummererd861d342018-04-24 22:56:32 +0100548 OPT_STRING('b', NULL, &new_branch, N_("branch"),
Eric Sunshinecbdf60f2015-07-06 13:30:53 -0400549 N_("create a new branch")),
550 OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
551 N_("create or reset a branch")),
Eric Sunshine5dd6e232015-07-17 19:00:07 -0400552 OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
Ray Zhangef2a0ac2016-03-29 10:11:01 +0000553 OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
Nguyễn Thái Ngọc Duy507e6e92017-04-12 20:58:05 +0700554 OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
Elia Pinto371979c2018-08-15 20:56:30 +0000555 OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
Thomas Gummerere284e892017-11-26 19:43:53 +0000556 OPT_PASSTHRU(0, "track", &opt_track, NULL,
557 N_("set up tracking mode (see git-branch(1))"),
558 PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
Thomas Gummerer71d66822017-11-29 20:04:50 +0000559 OPT_BOOL(0, "guess-remote", &guess_remote,
560 N_("try to match the new branch name with a remote-tracking branch")),
Eric Sunshinefc563612015-07-06 13:30:50 -0400561 OPT_END()
562 };
563
Eric Sunshine5dd6e232015-07-17 19:00:07 -0400564 memset(&opts, 0, sizeof(opts));
Ray Zhangef2a0ac2016-03-29 10:11:01 +0000565 opts.checkout = 1;
Eric Sunshinefc563612015-07-06 13:30:50 -0400566 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
Thomas Gummererd861d342018-04-24 22:56:32 +0100567 if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
Eric Sunshineab0b2c52015-07-17 19:00:08 -0400568 die(_("-b, -B, and --detach are mutually exclusive"));
Eric Sunshine0f4af3b2015-07-06 13:30:58 -0400569 if (ac < 1 || ac > 2)
570 usage_with_options(worktree_usage, options);
Eric Sunshinefc563612015-07-06 13:30:50 -0400571
Jeff King116fb642017-03-20 21:22:28 -0400572 path = prefix_filename(prefix, av[0]);
Eric Sunshine0f4af3b2015-07-06 13:30:58 -0400573 branch = ac < 2 ? "HEAD" : av[1];
Eric Sunshinefc563612015-07-06 13:30:50 -0400574
Jordan DE GEA1a450e22016-05-27 15:17:08 +0200575 if (!strcmp(branch, "-"))
576 branch = "@{-1}";
577
Thomas Gummererd861d342018-04-24 22:56:32 +0100578 if (new_branch_force) {
Nguyễn Thái Ngọc Duybeb6f242016-02-15 20:35:33 +0700579 struct strbuf symref = STRBUF_INIT;
580
Thomas Gummererd861d342018-04-24 22:56:32 +0100581 new_branch = new_branch_force;
Eric Sunshineeef005d2015-07-17 19:00:06 -0400582
Nguyễn Thái Ngọc Duybeb6f242016-02-15 20:35:33 +0700583 if (!opts.force &&
Thomas Gummererd861d342018-04-24 22:56:32 +0100584 !strbuf_check_branch_ref(&symref, new_branch) &&
Nguyễn Thái Ngọc Duybeb6f242016-02-15 20:35:33 +0700585 ref_exists(symref.buf))
Nguyễn Thái Ngọc Duy8d9fdd72016-04-22 20:01:33 +0700586 die_if_checked_out(symref.buf, 0);
Nguyễn Thái Ngọc Duybeb6f242016-02-15 20:35:33 +0700587 strbuf_release(&symref);
588 }
589
Thomas Gummererd861d342018-04-24 22:56:32 +0100590 if (ac < 2 && !new_branch && !opts.detach) {
Thomas Gummerer6427f872018-04-24 22:56:34 +0100591 const char *s = dwim_branch(path, &new_branch);
592 if (s)
593 branch = s;
Eric Sunshine1eb07d82015-07-06 13:30:59 -0400594 }
595
Thomas Gummererd861d342018-04-24 22:56:32 +0100596 if (ac == 2 && !new_branch && !opts.detach) {
Thomas Gummerer4e853332017-11-26 19:43:54 +0000597 struct object_id oid;
598 struct commit *commit;
599 const char *remote;
600
601 commit = lookup_commit_reference_by_name(branch);
602 if (!commit) {
Ævar Arnfjörð Bjarmason3c87aa92018-06-05 14:40:46 +0000603 remote = unique_tracking_name(branch, &oid, NULL);
Thomas Gummerer4e853332017-11-26 19:43:54 +0000604 if (remote) {
Thomas Gummererd861d342018-04-24 22:56:32 +0100605 new_branch = branch;
Thomas Gummerer4e853332017-11-26 19:43:54 +0000606 branch = remote;
607 }
608 }
609 }
Elia Pinto371979c2018-08-15 20:56:30 +0000610 if (!opts.quiet)
611 print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
Thomas Gummerer2c270022018-04-24 22:56:33 +0100612
Thomas Gummererd861d342018-04-24 22:56:32 +0100613 if (new_branch) {
René Scharfe542aa252016-08-05 22:38:44 +0200614 struct child_process cp = CHILD_PROCESS_INIT;
Eric Sunshinec2842432015-07-17 19:00:10 -0400615 cp.git_cmd = 1;
616 argv_array_push(&cp.args, "branch");
Thomas Gummererd861d342018-04-24 22:56:32 +0100617 if (new_branch_force)
Eric Sunshinec2842432015-07-17 19:00:10 -0400618 argv_array_push(&cp.args, "--force");
Elia Pinto371979c2018-08-15 20:56:30 +0000619 if (opts.quiet)
620 argv_array_push(&cp.args, "--quiet");
Thomas Gummererd861d342018-04-24 22:56:32 +0100621 argv_array_push(&cp.args, new_branch);
Eric Sunshinec2842432015-07-17 19:00:10 -0400622 argv_array_push(&cp.args, branch);
Thomas Gummerere284e892017-11-26 19:43:53 +0000623 if (opt_track)
624 argv_array_push(&cp.args, opt_track);
Eric Sunshinec2842432015-07-17 19:00:10 -0400625 if (run_command(&cp))
626 return -1;
Thomas Gummererd861d342018-04-24 22:56:32 +0100627 branch = new_branch;
Thomas Gummerere284e892017-11-26 19:43:53 +0000628 } else if (opt_track) {
629 die(_("--[no-]track can only be used if a new branch is created"));
Eric Sunshinec2842432015-07-17 19:00:10 -0400630 }
Eric Sunshinefc563612015-07-06 13:30:50 -0400631
Jeff King0e5bba52017-09-08 02:38:41 -0400632 UNLEAK(path);
633 UNLEAK(opts);
Eric Sunshine80a05482015-07-17 19:00:12 -0400634 return add_worktree(path, branch, &opts);
Eric Sunshinefc563612015-07-06 13:30:50 -0400635}
636
Michael Rappazzobb9c03b2015-10-08 13:01:05 -0400637static void show_worktree_porcelain(struct worktree *wt)
638{
639 printf("worktree %s\n", wt->path);
640 if (wt->is_bare)
641 printf("bare\n");
642 else {
brian m. carlson0f051542017-10-15 22:07:08 +0000643 printf("HEAD %s\n", oid_to_hex(&wt->head_oid));
Michael Rappazzobb9c03b2015-10-08 13:01:05 -0400644 if (wt->is_detached)
645 printf("detached\n");
Nguyễn Thái Ngọc Duya2345632016-11-28 16:36:54 +0700646 else if (wt->head_ref)
Michael Rappazzobb9c03b2015-10-08 13:01:05 -0400647 printf("branch %s\n", wt->head_ref);
648 }
649 printf("\n");
650}
651
652static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
653{
654 struct strbuf sb = STRBUF_INIT;
655 int cur_path_len = strlen(wt->path);
656 int path_adj = cur_path_len - utf8_strwidth(wt->path);
657
658 strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
659 if (wt->is_bare)
660 strbuf_addstr(&sb, "(bare)");
661 else {
662 strbuf_addf(&sb, "%-*s ", abbrev_len,
brian m. carlsonaab95832018-03-12 02:27:30 +0000663 find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
Nguyễn Thái Ngọc Duy96f09e22016-11-28 16:36:53 +0700664 if (wt->is_detached)
Michael Rappazzobb9c03b2015-10-08 13:01:05 -0400665 strbuf_addstr(&sb, "(detached HEAD)");
Johannes Schindelin2e11f582017-05-04 15:59:13 +0200666 else if (wt->head_ref) {
667 char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
668 strbuf_addf(&sb, "[%s]", ref);
669 free(ref);
670 } else
Nguyễn Thái Ngọc Duya2345632016-11-28 16:36:54 +0700671 strbuf_addstr(&sb, "(error)");
Michael Rappazzobb9c03b2015-10-08 13:01:05 -0400672 }
673 printf("%s\n", sb.buf);
674
675 strbuf_release(&sb);
676}
677
678static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
679{
680 int i;
681
682 for (i = 0; wt[i]; i++) {
683 int sha1_len;
684 int path_len = strlen(wt[i]->path);
685
686 if (path_len > *maxlen)
687 *maxlen = path_len;
brian m. carlsonaab95832018-03-12 02:27:30 +0000688 sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
Michael Rappazzobb9c03b2015-10-08 13:01:05 -0400689 if (sha1_len > *abbrev)
690 *abbrev = sha1_len;
691 }
692}
693
694static int list(int ac, const char **av, const char *prefix)
695{
696 int porcelain = 0;
697
698 struct option options[] = {
699 OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
700 OPT_END()
701 };
702
703 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
704 if (ac)
705 usage_with_options(worktree_usage, options);
706 else {
Nguyễn Thái Ngọc Duy4df1d4d2016-11-28 16:36:56 +0700707 struct worktree **worktrees = get_worktrees(GWT_SORT_LINKED);
Michael Rappazzobb9c03b2015-10-08 13:01:05 -0400708 int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
709
710 if (!porcelain)
711 measure_widths(worktrees, &abbrev, &path_maxlen);
712
713 for (i = 0; worktrees[i]; i++) {
714 if (porcelain)
715 show_worktree_porcelain(worktrees[i]);
716 else
717 show_worktree(worktrees[i], path_maxlen, abbrev);
718 }
719 free_worktrees(worktrees);
720 }
721 return 0;
722}
723
Nguyễn Thái Ngọc Duy58142c02016-06-13 19:18:24 +0700724static int lock_worktree(int ac, const char **av, const char *prefix)
725{
726 const char *reason = "", *old_reason;
727 struct option options[] = {
728 OPT_STRING(0, "reason", &reason, N_("string"),
729 N_("reason for locking")),
730 OPT_END()
731 };
732 struct worktree **worktrees, *wt;
733
734 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
735 if (ac != 1)
736 usage_with_options(worktree_usage, options);
737
Nguyễn Thái Ngọc Duy4fff1ef2016-11-28 16:36:55 +0700738 worktrees = get_worktrees(0);
Nguyễn Thái Ngọc Duy58142c02016-06-13 19:18:24 +0700739 wt = find_worktree(worktrees, prefix, av[0]);
740 if (!wt)
741 die(_("'%s' is not a working tree"), av[0]);
742 if (is_main_worktree(wt))
743 die(_("The main working tree cannot be locked or unlocked"));
744
Nickolai Belakovskid236f122018-10-29 23:24:09 -0700745 old_reason = worktree_lock_reason(wt);
Nguyễn Thái Ngọc Duy58142c02016-06-13 19:18:24 +0700746 if (old_reason) {
747 if (*old_reason)
748 die(_("'%s' is already locked, reason: %s"),
749 av[0], old_reason);
750 die(_("'%s' is already locked"), av[0]);
751 }
752
753 write_file(git_common_path("worktrees/%s/locked", wt->id),
754 "%s", reason);
755 free_worktrees(worktrees);
756 return 0;
757}
758
Nguyễn Thái Ngọc Duy6d308622016-06-13 19:18:25 +0700759static int unlock_worktree(int ac, const char **av, const char *prefix)
760{
761 struct option options[] = {
762 OPT_END()
763 };
764 struct worktree **worktrees, *wt;
765 int ret;
766
767 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
768 if (ac != 1)
769 usage_with_options(worktree_usage, options);
770
Nguyễn Thái Ngọc Duy4fff1ef2016-11-28 16:36:55 +0700771 worktrees = get_worktrees(0);
Nguyễn Thái Ngọc Duy6d308622016-06-13 19:18:25 +0700772 wt = find_worktree(worktrees, prefix, av[0]);
773 if (!wt)
774 die(_("'%s' is not a working tree"), av[0]);
775 if (is_main_worktree(wt))
776 die(_("The main working tree cannot be locked or unlocked"));
Nickolai Belakovskid236f122018-10-29 23:24:09 -0700777 if (!worktree_lock_reason(wt))
Nguyễn Thái Ngọc Duy6d308622016-06-13 19:18:25 +0700778 die(_("'%s' is not locked"), av[0]);
779 ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
780 free_worktrees(worktrees);
781 return ret;
782}
783
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700784static void validate_no_submodules(const struct worktree *wt)
785{
786 struct index_state istate = { NULL };
Nguyễn Thái Ngọc Duy00a6d4d2019-01-05 12:08:40 +0700787 struct strbuf path = STRBUF_INIT;
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700788 int i, found_submodules = 0;
789
Nguyễn Thái Ngọc Duy00a6d4d2019-01-05 12:08:40 +0700790 if (is_directory(worktree_git_path(wt, "modules"))) {
791 /*
792 * There could be false positives, e.g. the "modules"
793 * directory exists but is empty. But it's a rare case and
794 * this simpler check is probably good enough for now.
795 */
796 found_submodules = 1;
797 } else if (read_index_from(&istate, worktree_git_path(wt, "index"),
798 get_worktree_git_dir(wt)) > 0) {
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700799 for (i = 0; i < istate.cache_nr; i++) {
800 struct cache_entry *ce = istate.cache[i];
Nguyễn Thái Ngọc Duy00a6d4d2019-01-05 12:08:40 +0700801 int err;
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700802
Nguyễn Thái Ngọc Duy00a6d4d2019-01-05 12:08:40 +0700803 if (!S_ISGITLINK(ce->ce_mode))
804 continue;
805
806 strbuf_reset(&path);
807 strbuf_addf(&path, "%s/%s", wt->path, ce->name);
808 if (!is_submodule_populated_gently(path.buf, &err))
809 continue;
810
811 found_submodules = 1;
812 break;
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700813 }
814 }
815 discard_index(&istate);
Nguyễn Thái Ngọc Duy00a6d4d2019-01-05 12:08:40 +0700816 strbuf_release(&path);
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700817
818 if (found_submodules)
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700819 die(_("working trees containing submodules cannot be moved or removed"));
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700820}
821
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700822static int move_worktree(int ac, const char **av, const char *prefix)
823{
Eric Sunshine68a6b3a2018-08-28 17:20:24 -0400824 int force = 0;
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700825 struct option options[] = {
Eric Sunshine68a6b3a2018-08-28 17:20:24 -0400826 OPT__FORCE(&force,
827 N_("force move even if worktree is dirty or locked"),
828 PARSE_OPT_NOCOMPLETE),
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700829 OPT_END()
830 };
831 struct worktree **worktrees, *wt;
832 struct strbuf dst = STRBUF_INIT;
833 struct strbuf errmsg = STRBUF_INIT;
Eric Sunshine68a6b3a2018-08-28 17:20:24 -0400834 const char *reason = NULL;
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700835 char *path;
836
837 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
838 if (ac != 2)
839 usage_with_options(worktree_usage, options);
840
841 path = prefix_filename(prefix, av[1]);
842 strbuf_addstr(&dst, path);
843 free(path);
844
845 worktrees = get_worktrees(0);
846 wt = find_worktree(worktrees, prefix, av[0]);
847 if (!wt)
848 die(_("'%s' is not a working tree"), av[0]);
849 if (is_main_worktree(wt))
850 die(_("'%s' is a main working tree"), av[0]);
Nguyễn Thái Ngọc Duyc64a8d22018-02-12 16:49:37 +0700851 if (is_directory(dst.buf)) {
852 const char *sep = find_last_dir_sep(wt->path);
853
854 if (!sep)
855 die(_("could not figure out destination name from '%s'"),
856 wt->path);
857 strbuf_trim_trailing_dir_sep(&dst);
858 strbuf_addstr(&dst, sep);
859 }
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700860 if (file_exists(dst.buf))
Nguyễn Thái Ngọc Duyc64a8d22018-02-12 16:49:37 +0700861 die(_("target '%s' already exists"), dst.buf);
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700862
Nguyễn Thái Ngọc Duy78d986b2018-02-12 16:49:38 +0700863 validate_no_submodules(wt);
864
Eric Sunshine68a6b3a2018-08-28 17:20:24 -0400865 if (force < 2)
Nickolai Belakovskid236f122018-10-29 23:24:09 -0700866 reason = worktree_lock_reason(wt);
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700867 if (reason) {
868 if (*reason)
Eric Sunshine68a6b3a2018-08-28 17:20:24 -0400869 die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"),
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700870 reason);
Eric Sunshine68a6b3a2018-08-28 17:20:24 -0400871 die(_("cannot move a locked working tree;\nuse 'move -f -f' to override or unlock first"));
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700872 }
Nguyễn Thái Ngọc Duyee6763a2018-02-12 16:49:40 +0700873 if (validate_worktree(wt, &errmsg, 0))
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +0700874 die(_("validation failed, cannot move working tree: %s"),
875 errmsg.buf);
876 strbuf_release(&errmsg);
877
878 if (rename(wt->path, dst.buf) == -1)
879 die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf);
880
881 update_worktree_location(wt, dst.buf);
882
883 strbuf_release(&dst);
884 free_worktrees(worktrees);
885 return 0;
886}
887
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700888/*
889 * Note, "git status --porcelain" is used to determine if it's safe to
890 * delete a whole worktree. "git status" does not ignore user
891 * configuration, so if a normal "git status" shows "clean" for the
892 * user, then it's ok to remove it.
893 *
894 * This assumption may be a bad one. We may want to ignore
895 * (potentially bad) user settings and only delete a worktree when
896 * it's absolutely safe to do so from _our_ point of view because we
897 * know better.
898 */
899static void check_clean_worktree(struct worktree *wt,
900 const char *original_path)
901{
902 struct argv_array child_env = ARGV_ARRAY_INIT;
903 struct child_process cp;
904 char buf[1];
905 int ret;
906
907 /*
908 * Until we sort this out, all submodules are "dirty" and
909 * will abort this function.
910 */
911 validate_no_submodules(wt);
912
913 argv_array_pushf(&child_env, "%s=%s/.git",
914 GIT_DIR_ENVIRONMENT, wt->path);
915 argv_array_pushf(&child_env, "%s=%s",
916 GIT_WORK_TREE_ENVIRONMENT, wt->path);
917 memset(&cp, 0, sizeof(cp));
918 argv_array_pushl(&cp.args, "status",
919 "--porcelain", "--ignore-submodules=none",
920 NULL);
921 cp.env = child_env.argv;
922 cp.git_cmd = 1;
923 cp.dir = wt->path;
924 cp.out = -1;
925 ret = start_command(&cp);
926 if (ret)
927 die_errno(_("failed to run 'git status' on '%s'"),
928 original_path);
929 ret = xread(cp.out, buf, sizeof(buf));
930 if (ret)
SZEDER Gábor507e5472019-08-13 20:02:44 +0200931 die(_("'%s' contains modified or untracked files, use --force to delete it"),
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700932 original_path);
933 close(cp.out);
934 ret = finish_command(&cp);
935 if (ret)
936 die_errno(_("failed to run 'git status' on '%s', code %d"),
937 original_path, ret);
938}
939
940static int delete_git_work_tree(struct worktree *wt)
941{
942 struct strbuf sb = STRBUF_INIT;
943 int ret = 0;
944
945 strbuf_addstr(&sb, wt->path);
946 if (remove_dir_recursively(&sb, 0)) {
947 error_errno(_("failed to delete '%s'"), sb.buf);
948 ret = -1;
949 }
950 strbuf_release(&sb);
951 return ret;
952}
953
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700954static int remove_worktree(int ac, const char **av, const char *prefix)
955{
956 int force = 0;
957 struct option options[] = {
Stefan Bellerd228eea2018-04-17 11:19:39 -0700958 OPT__FORCE(&force,
Eric Sunshinef4143102018-08-28 17:20:25 -0400959 N_("force removal even if worktree is dirty or locked"),
Stefan Bellerd228eea2018-04-17 11:19:39 -0700960 PARSE_OPT_NOCOMPLETE),
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700961 OPT_END()
962 };
963 struct worktree **worktrees, *wt;
964 struct strbuf errmsg = STRBUF_INIT;
Eric Sunshinef4143102018-08-28 17:20:25 -0400965 const char *reason = NULL;
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700966 int ret = 0;
967
968 ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
969 if (ac != 1)
970 usage_with_options(worktree_usage, options);
971
972 worktrees = get_worktrees(0);
973 wt = find_worktree(worktrees, prefix, av[0]);
974 if (!wt)
975 die(_("'%s' is not a working tree"), av[0]);
976 if (is_main_worktree(wt))
977 die(_("'%s' is a main working tree"), av[0]);
Eric Sunshinef4143102018-08-28 17:20:25 -0400978 if (force < 2)
Nickolai Belakovskid236f122018-10-29 23:24:09 -0700979 reason = worktree_lock_reason(wt);
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700980 if (reason) {
981 if (*reason)
Eric Sunshinef4143102018-08-28 17:20:25 -0400982 die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"),
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700983 reason);
Eric Sunshinef4143102018-08-28 17:20:25 -0400984 die(_("cannot remove a locked working tree;\nuse 'remove -f -f' to override or unlock first"));
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700985 }
Nguyễn Thái Ngọc Duyee6763a2018-02-12 16:49:40 +0700986 if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700987 die(_("validation failed, cannot remove working tree: %s"),
988 errmsg.buf);
989 strbuf_release(&errmsg);
990
Nguyễn Thái Ngọc Duyee6763a2018-02-12 16:49:40 +0700991 if (file_exists(wt->path)) {
992 if (!force)
993 check_clean_worktree(wt, av[0]);
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700994
Nguyễn Thái Ngọc Duyee6763a2018-02-12 16:49:40 +0700995 ret |= delete_git_work_tree(wt);
996 }
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +0700997 /*
998 * continue on even if ret is non-zero, there's no going back
999 * from here.
1000 */
Eric Sunshine602aaed2018-08-28 17:20:20 -04001001 ret |= delete_git_dir(wt->id);
Eric Sunshine3a540432018-08-28 17:20:26 -04001002 delete_worktrees_dir_if_empty();
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +07001003
1004 free_worktrees(worktrees);
1005 return ret;
1006}
1007
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +07001008int cmd_worktree(int ac, const char **av, const char *prefix)
1009{
1010 struct option options[] = {
1011 OPT_END()
1012 };
1013
Thomas Gummerere92445a2017-11-29 20:04:51 +00001014 git_config(git_worktree_config, NULL);
Junio C Hamanod49028e2016-09-26 23:49:39 -07001015
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +07001016 if (ac < 2)
1017 usage_with_options(worktree_usage, options);
Nguyễn Thái Ngọc Duy0409e0b2016-05-22 16:33:56 +07001018 if (!prefix)
1019 prefix = "";
Eric Sunshinefc563612015-07-06 13:30:50 -04001020 if (!strcmp(av[1], "add"))
1021 return add(ac - 1, av + 1, prefix);
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +07001022 if (!strcmp(av[1], "prune"))
1023 return prune(ac - 1, av + 1, prefix);
Michael Rappazzobb9c03b2015-10-08 13:01:05 -04001024 if (!strcmp(av[1], "list"))
1025 return list(ac - 1, av + 1, prefix);
Nguyễn Thái Ngọc Duy58142c02016-06-13 19:18:24 +07001026 if (!strcmp(av[1], "lock"))
1027 return lock_worktree(ac - 1, av + 1, prefix);
Nguyễn Thái Ngọc Duy6d308622016-06-13 19:18:25 +07001028 if (!strcmp(av[1], "unlock"))
1029 return unlock_worktree(ac - 1, av + 1, prefix);
Nguyễn Thái Ngọc Duy9f792bb2018-02-12 16:49:36 +07001030 if (!strcmp(av[1], "move"))
1031 return move_worktree(ac - 1, av + 1, prefix);
Nguyễn Thái Ngọc Duycc733852018-02-12 16:49:39 +07001032 if (!strcmp(av[1], "remove"))
1033 return remove_worktree(ac - 1, av + 1, prefix);
Nguyễn Thái Ngọc Duydf0b6cf2015-06-29 19:51:18 +07001034 usage_with_options(worktree_usage, options);
1035}