Thread (37 messages) flat view 37 messages, 5 authors, 2018-03-05

Re: [PATCH 6/7] worktree remove: new command

From: Eric Sunshine <hidden>
Date: 2018-02-02 11:47:20

On Wed, Jan 24, 2018 at 4:53 AM, Nguyễn Thái Ngọc Duy [off-list ref] wrote:
quoted hunk ↗ jump to hunk
This command allows to delete a worktree. Like 'move' you cannot
remove the main worktree, or one with submodules inside [1].
[...]
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
diff --git a/builtin/worktree.c b/builtin/worktree.c
@@ -688,6 +689,132 @@ static int move_worktree(int ac, const char **av, const char *prefix)
+static void check_clean_worktree(struct worktree *wt,
+                                const char *original_path)
+{
+       [...]
+       validate_no_submodules(wt);
It's slightly strange seeing worktree validation in a function
checking whether the worktree is clean since submodule validation
isn't an issue of cleanliness. I'd have expected the caller to invoke
it instead:

    int remove_worktree(...) {
        ...
        if (!force) {
            validate_no_submodules(wt);
            check_clean_worktree(wt, av[0]);
        }
        ...
    }

On the other hand, I could imagine it being called here as appropriate
if submodule validation eventually also checks submodule cleanliness
as hinted by the commit message.
+       argv_array_pushf(&child_env, "%s=%s/.git",
+                        GIT_DIR_ENVIRONMENT, wt->path);
+       argv_array_pushf(&child_env, "%s=%s",
+                        GIT_WORK_TREE_ENVIRONMENT, wt->path);
+       memset(&cp, 0, sizeof(cp));
+       argv_array_pushl(&cp.args, "status",
+                        "--porcelain", "--ignore-submodules=none",
+                        NULL);
+       cp.env = child_env.argv;
+       cp.git_cmd = 1;
+       cp.dir = wt->path;
+       cp.out = -1;
+       ret = start_command(&cp);
+       if (ret)
+               die_errno(_("failed to run git-status on '%s'"),
+                         original_path);
Minor: I think there was some effort recently to remove "git-foo"
style mentions from documentation and error messages. Perhaps this
could be "failed to run 'git status' on '%s'". Ditto below.
+       ret = xread(cp.out, buf, sizeof(buf));
+       if (ret)
+               die(_("'%s' is dirty, use --force to delete it"),
+                   original_path);
+       close(cp.out);
+       ret = finish_command(&cp);
+       if (ret)
+               die_errno(_("failed to run git-status on '%s', code %d"),
+                         original_path, ret);
+}
+
+static int delete_git_work_tree(struct worktree *wt)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int ret = 0;
+
+       strbuf_addstr(&sb, wt->path);
+       if (remove_dir_recursively(&sb, 0)) {
+               error_errno(_("failed to delete '%s'"), sb.buf);
+               ret = -1;
+       }
+       strbuf_release(&sb);
+
+       return ret;
+}
Style nit: In the very similar delete_git_dir(), just below, there is
no blank line before 'return'.
+
+static int delete_git_dir(struct worktree *wt)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int ret = 0;
+
+       strbuf_addstr(&sb, git_common_path("worktrees/%s", wt->id));
+       if (remove_dir_recursively(&sb, 0)) {
+               error_errno(_("failed to delete '%s'"), sb.buf);
+               ret = -1;
+       }
+       strbuf_release(&sb);
+       return ret;
+}
+
+static int remove_worktree(int ac, const char **av, const char *prefix)
+{
+       [...]
+       if (reason) {
+               if (*reason)
+                       die(_("cannot remove a locked working tree, lock reason: %s"),
+                           reason);
+               die(_("cannot remove a locked working tree"));
+       }
+       if (validate_worktree(wt, &errmsg))
+               die(_("validation failed, cannot remove working tree:\n%s"),
+                   errmsg.buf);
Minor: Same concern as in 3/7 about the multi-line error message
making scripted handling of error message collection more difficult.
quoted hunk ↗ jump to hunk
+       strbuf_release(&errmsg);
+
+       if (!force)
+               check_clean_worktree(wt, av[0]);
+
+       ret |= delete_git_work_tree(wt);
+       /*
+        * continue on even if ret is non-zero, there's no going back
+        * from here.
+        */
+       ret |= delete_git_dir(wt);
+
+       free_worktrees(worktrees);
+       return ret;
+}
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
@@ -90,4 +90,30 @@ test_expect_success 'move main worktree' '
+test_expect_success 'remove locked worktree' '
+       git worktree lock destination &&
+       test_must_fail git worktree remove destination &&
+       git worktree unlock destination
+'
Perhaps place 'unlock' in test_when_finished()[1].
+test_expect_success 'remove worktree with dirty tracked file' '
+       echo dirty >>destination/init.t &&
+       test_must_fail git worktree remove destination
+'
+
+test_expect_success 'remove worktree with untracked file' '
+       git -C destination checkout init.t &&
Reversion of 'init.t' probably belongs in the preceding test which
modified it, wrapped in test_when_finished()[1].
+       : >destination/untracked &&
+       test_must_fail git worktree remove destination
+'
[1]: https://public-inbox.org/git/CAPig+cSV9_6j6Nkptma3BewKW8QQcem7gwFCb42VBW4Xe0Vr2w@mail.gmail.com/
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help