am: improve author-script error reporting
[gitweb.git] / builtin / worktree.c
index 46c93771e8119336c764b5926df24dc0666fce56..c4abbde2b87e00944eb86fe2393daf477da976b6 100644 (file)
@@ -62,6 +62,11 @@ static int delete_git_dir(const char *id)
        return ret;
 }
 
+static void delete_worktrees_dir_if_empty(void)
+{
+       rmdir(git_path("worktrees")); /* ignore failed removal */
+}
+
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
        struct stat st;
@@ -149,7 +154,7 @@ static void prune_worktrees(void)
        }
        closedir(dir);
        if (!show_only)
-               rmdir(git_path("worktrees"));
+               delete_worktrees_dir_if_empty();
        strbuf_release(&reason);
 }
 
@@ -221,8 +226,39 @@ static const char *worktree_basename(const char *path, int *olen)
 
 static void validate_worktree_add(const char *path, const struct add_opts *opts)
 {
+       struct worktree **worktrees;
+       struct worktree *wt;
+       int locked;
+
        if (file_exists(path) && !is_empty_dir(path))
                die(_("'%s' already exists"), path);
+
+       worktrees = get_worktrees(0);
+       /*
+        * find_worktree()'s suffix matching may undesirably find the main
+        * rather than a linked worktree (for instance, when the basenames
+        * of the main worktree and the one being created are the same).
+        * We're only interested in linked worktrees, so skip the main
+        * worktree with +1.
+        */
+       wt = find_worktree(worktrees + 1, NULL, path);
+       if (!wt)
+               goto done;
+
+       locked = !!is_worktree_locked(wt);
+       if ((!locked && opts->force) || (locked && opts->force > 1)) {
+               if (delete_git_dir(wt->id))
+                   die(_("unable to re-add worktree '%s'"), path);
+               goto done;
+       }
+
+       if (locked)
+               die(_("'%s' is a missing but locked worktree;\nuse 'add -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), path);
+       else
+               die(_("'%s' is a missing but already registered worktree;\nuse 'add -f' to override, or 'prune' or 'remove' to clear"), path);
+
+done:
+       free_worktrees(worktrees);
 }
 
 static int add_worktree(const char *path, const char *refname,
@@ -709,13 +745,17 @@ static void validate_no_submodules(const struct worktree *wt)
 
 static int move_worktree(int ac, const char **av, const char *prefix)
 {
+       int force = 0;
        struct option options[] = {
+               OPT__FORCE(&force,
+                        N_("force move even if worktree is dirty or locked"),
+                        PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
        struct worktree **worktrees, *wt;
        struct strbuf dst = STRBUF_INIT;
        struct strbuf errmsg = STRBUF_INIT;
-       const char *reason;
+       const char *reason = NULL;
        char *path;
 
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
@@ -746,12 +786,13 @@ static int move_worktree(int ac, const char **av, const char *prefix)
 
        validate_no_submodules(wt);
 
-       reason = is_worktree_locked(wt);
+       if (force < 2)
+               reason = is_worktree_locked(wt);
        if (reason) {
                if (*reason)
-                       die(_("cannot move a locked working tree, lock reason: %s"),
+                       die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"),
                            reason);
-               die(_("cannot move a locked working tree"));
+               die(_("cannot move a locked working tree;\nuse 'move -f -f' to override or unlock first"));
        }
        if (validate_worktree(wt, &errmsg, 0))
                die(_("validation failed, cannot move working tree: %s"),
@@ -839,13 +880,13 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
        int force = 0;
        struct option options[] = {
                OPT__FORCE(&force,
-                        N_("force removing even if the worktree is dirty"),
+                        N_("force removal even if worktree is dirty or locked"),
                         PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
        struct worktree **worktrees, *wt;
        struct strbuf errmsg = STRBUF_INIT;
-       const char *reason;
+       const char *reason = NULL;
        int ret = 0;
 
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
@@ -858,12 +899,13 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
                die(_("'%s' is not a working tree"), av[0]);
        if (is_main_worktree(wt))
                die(_("'%s' is a main working tree"), av[0]);
-       reason = is_worktree_locked(wt);
+       if (force < 2)
+               reason = is_worktree_locked(wt);
        if (reason) {
                if (*reason)
-                       die(_("cannot remove a locked working tree, lock reason: %s"),
+                       die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"),
                            reason);
-               die(_("cannot remove a locked working tree"));
+               die(_("cannot remove a locked working tree;\nuse 'remove -f -f' to override or unlock first"));
        }
        if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
                die(_("validation failed, cannot remove working tree: %s"),
@@ -881,6 +923,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
         * from here.
         */
        ret |= delete_git_dir(wt->id);
+       delete_worktrees_dir_if_empty();
 
        free_worktrees(worktrees);
        return ret;