From: Phillip Wood <phillip.wood123@gmail.com>
To: Jasper Mayone <me@jaspermayone.com>, git@vger.kernel.org
Cc: gitster@pobox.com, sunshine@sunshineco.com,
kristofferhaugsbakk@fastmail.com, ben.knoble@gmail.com,
haraldnordgren@gmail.com
Subject: Re: [PATCH v4 1/4] worktree remove: refuse a worktree in the middle of an operation
Date: Tue, 10 Nov 2026 16:47:24 +0000 [thread overview]
Message-ID: <8667d7b4-ffb4-4285-be67-10a85730c359@gmail.com> (raw)
In-Reply-To: <20261102182930.3511-2-me@jaspermayone.com>
Hi Jasper
On 02/11/2026 18:29, Jasper Mayone wrote:
>
> diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
> index e6e77252ab..4a7dc47dd8 100644
> --- a/Documentation/git-worktree.adoc
> +++ b/Documentation/git-worktree.adoc
> @@ -141,10 +141,16 @@ the new working tree location.
>
> `remove`::
>
> -Remove a worktree. Only clean worktrees (no untracked files and no
> -modification in tracked files) can be removed. Unclean worktrees or ones
> -with submodules can be removed with `--force`. The main worktree cannot be
> -removed.
> +Remove a worktree. The main worktree cannot be removed. Without
> +`--force`, a worktree is not removed when:
> ++
> +--
> +* it has untracked files or modifications in tracked files,
> +* it has submodules, or
This is a list so each item should be a self contained sentence, we
should drop the comma above and ", or" here.
> +* it is in the middle of a rebase, a `git am` session, a bisect, a
> + cherry-pick or a revert, because removing it loses the state of that
> + operation.
Looking at the html rendering of this, it is slightly odd that "git am"
is in a different font to the rest of the command names. As we're
creating a list we could have a separate item for each command, I'm not
sure whether that would be clearer or not.
The rest of this patch looks good
Thanks
Phillip
> +--
>
> `repair [<path>...]`::
>
> @@ -189,7 +195,8 @@ twice. If the destination is already assigned to some other worktree but is
> missing (for instance, if _<new-path>_ was deleted manually), then `--force`
> allows the move to proceed; use `--force` twice if the destination is locked.
> +
> -`remove` refuses to remove an unclean worktree unless `--force` is used.
> +`remove` refuses to remove a worktree in the cases listed under
> +`remove` above unless `--force` is used.
> To remove a locked worktree, specify `--force` twice.
>
> `-b <new-branch>`::
> diff --git a/builtin/worktree.c b/builtin/worktree.c
> index 77ecd0f71f..7f53215cda 100644
> --- a/builtin/worktree.c
> +++ b/builtin/worktree.c
> @@ -26,6 +26,7 @@
> #include "submodule.h"
> #include "utf8.h"
> #include "worktree.h"
> +#include "wt-status.h"
> #include "quote.h"
>
> #define BUILTIN_WORKTREE_ADD_USAGE \
> @@ -1408,6 +1409,38 @@ static void check_clean_worktree(struct worktree *wt,
> original_path, ret);
> }
>
> +/*
> + * A rebase, "git am", bisect, cherry-pick or revert keeps its state in
> + * the worktree's own $GIT_DIR, and removing the worktree throws that
> + * away even when "git status" says the files are clean.
> + */
> +static void check_no_operation_in_progress(struct worktree *wt,
> + const char *original_path)
> +{
> + struct wt_status_state state = { 0 };
> +
> + if (wt_status_check_rebase(wt, &state)) {
> + if (state.am_in_progress)
> + die(_("'%s' is in the middle of an am session, use --force to delete it"),
> + original_path);
> + die(_("'%s' is in the middle of a rebase, use --force to delete it"),
> + original_path);
> + }
> + if (wt_status_check_bisect(wt, &state))
> + die(_("'%s' is in the middle of a bisect, use --force to delete it"),
> + original_path);
> + wt_status_state_free_buffers(&state);
> + if (refs_ref_exists(get_worktree_ref_store(wt), "CHERRY_PICK_HEAD"))
> + die(_("'%s' is in the middle of a cherry-pick, use --force to delete it"),
> + original_path);
> + if (refs_ref_exists(get_worktree_ref_store(wt), "REVERT_HEAD"))
> + die(_("'%s' is in the middle of a revert, use --force to delete it"),
> + original_path);
> + if (is_directory(worktree_git_path(wt, "sequencer")))
> + die(_("'%s' is in the middle of a cherry-pick or revert, use --force to delete it"),
> + original_path);
> +}
> +
> static int delete_git_work_tree(struct worktree *wt)
> {
> struct strbuf sb = STRBUF_INIT;
> @@ -1461,8 +1494,10 @@ static int remove_worktree(int ac, const char **av, const char *prefix,
> strbuf_release(&errmsg);
>
> if (file_exists(wt->path)) {
> - if (!force)
> + if (!force) {
> + check_no_operation_in_progress(wt, av[0]);
> check_clean_worktree(wt, av[0]);
> + }
>
> ret |= delete_git_work_tree(wt);
> }
> diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
> index 69768c1207..2e45cd77f9 100755
> --- a/t/t2403-worktree-move.sh
> +++ b/t/t2403-worktree-move.sh
> @@ -271,4 +271,98 @@ test_expect_success 'move worktree with relative path to absolute path' '
> test_cmp expect .git/worktrees/absolute/gitdir
> '
>
> +test_expect_success 'setup commits that conflict for am, cherry-pick and revert' '
> + git worktree add --detach conflict-setup init &&
> + (
> + cd conflict-setup &&
> + test_commit base conflict.t base &&
> + git switch --detach init &&
> + test_commit side conflict.t side &&
> + test_commit side-two
> + ) &&
> + git worktree remove conflict-setup
> +'
> +
> +test_expect_success 'remove worktree in the middle of a rebase' '
> + test_when_finished "rm -rf mid-rebase; git worktree prune" &&
> + git worktree add --detach mid-rebase side &&
> + test_must_fail git -C mid-rebase rebase -f -x false init &&
> + test_must_fail git worktree remove mid-rebase 2>err &&
> + test_grep "in the middle of a rebase" err &&
> + test_path_is_dir mid-rebase &&
> + git worktree remove --force mid-rebase &&
> + test_path_is_missing mid-rebase
> +'
> +
> +test_expect_success 'remove worktree in the middle of an am session' '
> + test_when_finished "rm -rf mid-am patch; git worktree prune" &&
> + git format-patch -1 --stdout side >patch &&
> + git worktree add --detach mid-am base &&
> + (
> + cd mid-am &&
> + test_must_fail git am ../patch &&
> + git reset --hard
> + ) &&
> + test_must_fail git worktree remove mid-am 2>err &&
> + test_grep "in the middle of an am session" err &&
> + git worktree remove --force mid-am
> +'
> +
> +test_expect_success 'remove worktree in the middle of a bisect' '
> + test_when_finished "rm -rf mid-bisect; git worktree prune" &&
> + git worktree add --detach mid-bisect side-two &&
> + git -C mid-bisect bisect start side-two init &&
> + test_must_fail git worktree remove mid-bisect 2>err &&
> + test_grep "in the middle of a bisect" err &&
> + git worktree remove --force mid-bisect
> +'
> +
> +test_expect_success 'remove worktree in the middle of a cherry-pick' '
> + test_when_finished "rm -rf mid-pick; git worktree prune" &&
> + git worktree add --detach mid-pick base &&
> + (
> + cd mid-pick &&
> + test_must_fail git cherry-pick side &&
> + git checkout --ours conflict.t &&
> + git add conflict.t &&
> + git rev-parse --verify CHERRY_PICK_HEAD
> + ) &&
> + test_path_is_missing .git/worktrees/mid-pick/sequencer &&
> + test_must_fail git worktree remove mid-pick 2>err &&
> + test_grep "in the middle of a cherry-pick," err &&
> + git worktree remove --force mid-pick
> +'
> +
> +test_expect_success 'remove worktree in the middle of a revert' '
> + test_when_finished "rm -rf mid-revert; git worktree prune" &&
> + git worktree add --detach mid-revert base &&
> + (
> + cd mid-revert &&
> + test_must_fail git revert side &&
> + git checkout --ours conflict.t &&
> + git add conflict.t &&
> + git rev-parse --verify REVERT_HEAD
> + ) &&
> + test_path_is_missing .git/worktrees/mid-revert/sequencer &&
> + test_must_fail git worktree remove mid-revert 2>err &&
> + test_grep "in the middle of a revert" err &&
> + git worktree remove --force mid-revert
> +'
> +
> +test_expect_success 'remove worktree in the middle of a cherry-pick sequence' '
> + test_when_finished "rm -rf mid-pick-seq; git worktree prune" &&
> + git worktree add --detach mid-pick-seq base &&
> + (
> + cd mid-pick-seq &&
> + test_must_fail git cherry-pick side side-two &&
> + git checkout --theirs conflict.t &&
> + git add conflict.t &&
> + git commit --no-edit
> + ) &&
> + test_path_is_dir .git/worktrees/mid-pick-seq/sequencer &&
> + test_must_fail git worktree remove mid-pick-seq 2>err &&
> + test_grep "in the middle of a cherry-pick or revert" err &&
> + git worktree remove --force mid-pick-seq
> +'
> +
> test_done
next prev parent reply other threads:[~2026-11-10 16:47 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 17:33 [RFC PATCH 0/1] worktree list: add --merged and --no-merged Jasper Mayone
2026-10-21 17:33 ` [RFC PATCH 1/1] " Jasper Mayone
2026-10-23 18:30 ` Junio C Hamano
2026-10-24 6:25 ` Eric Sunshine
2026-10-24 16:53 ` [PATCH v2] worktree list: add --branch to select worktrees by branch Jasper Mayone
2026-10-25 14:47 ` Phillip Wood
2026-10-26 11:02 ` Phillip Wood
2026-10-28 6:02 ` Eric Sunshine
2026-10-28 15:27 ` Junio C Hamano
2026-10-28 11:32 ` Kristoffer Haugsbakk
2026-10-29 4:46 ` Jasper Mayone
2026-10-29 5:01 ` Eric Sunshine
2026-10-29 11:14 ` Phillip Wood
2026-10-29 14:23 ` Ben Knoble
2026-10-29 16:10 ` Phillip Wood
2026-10-30 14:21 ` [PATCH 0/2] worktree: treat a branch being rebased or bisected as checked out Jasper Mayone
2026-10-30 14:21 ` [PATCH 1/2] worktree remove: refuse a worktree in the middle of a rebase or bisect Jasper Mayone
2026-11-02 14:07 ` Phillip Wood
2026-11-02 17:57 ` Jasper Mayone
2026-10-30 14:21 ` [PATCH 2/2] ref-filter: name the worktree of a branch being rebased or bisected Jasper Mayone
2026-11-02 14:06 ` Phillip Wood
2026-11-02 17:57 ` Jasper Mayone
2026-10-30 22:55 ` [PATCH 0/2] worktree: treat a branch being rebased or bisected as checked out Junio C Hamano
2026-10-30 23:06 ` Jasper Mayone
2026-11-02 18:29 ` [PATCH v4 0/4] " Jasper Mayone
2026-11-02 18:29 ` [PATCH v4 1/4] worktree remove: refuse a worktree in the middle of an operation Jasper Mayone
2026-11-10 16:47 ` Phillip Wood [this message]
2026-11-10 17:09 ` Ben Knoble
2026-11-02 18:29 ` [PATCH v4 2/4] doc: for-each-ref: say how to read %(worktreepath) safely Jasper Mayone
2026-11-09 12:36 ` D. Ben Knoble
2026-11-09 15:10 ` Phillip Wood
2026-11-09 17:27 ` Ben Knoble
2026-11-09 20:17 ` Junio C Hamano
2026-11-10 16:47 ` Phillip Wood
2026-11-02 18:29 ` [PATCH v4 3/4] ref-filter: name the worktree of a branch being rebased or bisected Jasper Mayone
2026-11-10 16:47 ` Phillip Wood
2026-11-02 18:29 ` [PATCH v4 4/4] branch: do not mark the branch the current worktree is rebasing Jasper Mayone
2026-11-10 16:45 ` Phillip Wood
2026-11-10 16:49 ` [PATCH v4 0/4] worktree: treat a branch being rebased or bisected as checked out Phillip Wood
2026-11-11 15:28 ` [PATCH v5 0/5] " Jasper Mayone
2026-11-11 15:28 ` [PATCH v5 1/5] worktree remove: refuse a worktree in the middle of an operation Jasper Mayone
2026-11-11 15:28 ` [PATCH v5 2/5] doc: for-each-ref: say how to read %(worktreepath) safely Jasper Mayone
2026-11-11 15:28 ` [PATCH v5 3/5] ref-filter: name the worktree of a branch being rebased or bisected Jasper Mayone
2026-11-11 15:28 ` [PATCH v5 4/5] ref-filter: add %(currentworktree) Jasper Mayone
2026-11-11 15:28 ` [PATCH v5 5/5] branch: do not mark the branch the current worktree is rebasing Jasper Mayone
2026-11-11 15:42 ` [PATCH v5 0/5] worktree: treat a branch being rebased or bisected as checked out Ben Knoble
2026-10-21 21:36 ` [RFC PATCH 0/1] worktree list: add --merged and --no-merged Harald Nordgren
2026-10-21 22:12 ` Junio C Hamano
2026-10-22 7:21 ` Harald Nordgren
2026-10-22 17:58 ` Kristoffer Haugsbakk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8667d7b4-ffb4-4285-be67-10a85730c359@gmail.com \
--to=phillip.wood123@gmail.com \
--cc=ben.knoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=haraldnordgren@gmail.com \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=me@jaspermayone.com \
--cc=phillip.wood@dunelm.org.uk \
--cc=sunshine@sunshineco.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox