Git development
 help / color / mirror / Atom feed
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 1/2] worktree remove: refuse a worktree in the middle of a rebase or bisect
Date: Mon, 2 Nov 2026 14:07:33 +0000	[thread overview]
Message-ID: <bf554c5f-066c-4e58-a583-0f95aedecf95@gmail.com> (raw)
In-Reply-To: <20261030142137.54750-2-me@jaspermayone.com>

Hi Jasper

On 30/10/2026 14:21, Jasper Mayone wrote:
> "git worktree remove" refuses a worktree with modified or untracked

"refuses to move" would be clearer

> files unless --force is given, but it removes a worktree that is in
> the middle of a rebase, a "git am" session, a bisect or a cherry-pick
> or revert sequence when its files happen to be clean. The state of
> that operation lives in the worktree's own $GIT_DIR, so it is lost
> with the worktree. A rebase stopped at an "edit" or "break" line, an
> "exec" that failed, or a cherry-pick sequence after a conflict was
> resolved and committed is clean in this sense and still has work in
> progress.

Cherry picking or reverting a single commit that contains no new changes 
also looks clean, but there is still a cherry-pick operation in progress 
as CHERRY_PICK_HEAD exists. One could argue that, because the user 
doesn't really lose any work, we don't lose much by deleting such a 
worktree but we're still deleting a worktree that contains an 
in-progress operation. Fixing that case simply requires us to see if 
CHERRY_PICK_HEAD, or REVERT_HEAD exist.

> Treat such a worktree as unclean: refuse to remove it unless --force
> is given, and name the operation in the message.
> 
> A worktree whose directory is already gone is still removed without
> --force, as before: the check is part of the check for a clean
> worktree, which is only done when the directory exists.

Sensible

> Reported-by: D. Ben Knoble <ben.knoble@gmail.com>
> Helped-by: Phillip Wood <phillip.wood123@gmail.com>

I'm not sure what I've done anything to help with this patch

> diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
> index e6e77252ab..da4c38f909 100644
> --- a/Documentation/git-worktree.adoc
> +++ b/Documentation/git-worktree.adoc
> @@ -142,9 +142,10 @@ 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.
> +modification in tracked files) can be removed. A worktree in the middle
> +of a rebase, a `git am` session, a bisect or a cherry-pick or revert

"bisect, cherry-pick or revert"

> +sequence is not clean either, 

"either" doesn't make sense, because you've not described the unclean 
case yet. This line needs folding as well.

> because removing it loses that state. Unclean worktrees or ones with submodules
> +can be removed with `--force`. The main worktree cannot be removed.

A list of the conditions that require --force might make this section 
clearer.
>   `repair [<path>...]`::
>   
> @@ -189,7 +190,9 @@ 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 an unclean worktree, or one in the middle of a
> +rebase, `git am` session, bisect, or cherry-pick or revert sequence,

too many "or"s as 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..ed08de1f43 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,32 @@ static void check_clean_worktree(struct worktree *wt,
>   			  original_path, ret);
>   }
>   
> +/*
> + * A rebase, "git am", bisect or cherry-pick/revert sequence 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 (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);
> +}

This looks correct, though I wish we had a standard function that 
returned an enum describing which operation was in progress, rather than 
having to open code this sort of thing.

>   static int delete_git_work_tree(struct worktree *wt)
>   {
>   	struct strbuf sb = STRBUF_INIT;
> @@ -1461,8 +1488,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]);

This triggers the new check when the worktree directory exists - good.

>   			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..4eb56a8911 100755
> --- a/t/t2403-worktree-move.sh
> +++ b/t/t2403-worktree-move.sh
> @@ -271,4 +271,63 @@ test_expect_success 'move worktree with relative path to absolute path' '
>   	test_cmp expect .git/worktrees/absolute/gitdir
>   '
>   
> +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 init &&
> +	test_commit -C mid-rebase rebase-me &&
> +	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 worktree add --detach mid-am init &&
> +	test_commit -C mid-am am-me &&
> +	git -C mid-am format-patch -1 --stdout >patch &&
> +	git -C mid-am reset --hard init &&
> +	echo conflict >mid-am/am-me.t &&
> +	git -C mid-am add am-me.t &&
> +	git -C mid-am commit -m conflict &&
> +	test_must_fail git -C mid-am am ../patch &&
> +	git -C mid-am reset --hard &&

If we're running a lot of consecutive commands in a worktree, it is 
clearer to use a subshell

	(
		cd mid-am &&
		...
	) &&

I wonder how hard it would be to extend the "setup" test to create a 
branch that has conflicts when it is applied with am, cherry-pick, or 
rebase, so that we don't have to set up the conflicts in each test.

Thanks

Phillip

> +	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 init &&
> +	test_commit -C mid-bisect bisect-one &&
> +	test_commit -C mid-bisect bisect-two &&
> +	git -C mid-bisect bisect start HEAD 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 sequence' '
> +	test_when_finished "rm -rf mid-pick; git worktree prune" &&
> +	git worktree add --detach mid-pick init &&
> +	git -C mid-pick switch -c pick-src &&
> +	test_commit -C mid-pick pick-one &&
> +	test_commit -C mid-pick pick-two &&
> +	git -C mid-pick switch --detach init &&
> +	echo conflict >mid-pick/pick-one.t &&
> +	git -C mid-pick add pick-one.t &&
> +	git -C mid-pick commit -m conflict &&
> +	test_must_fail git -C mid-pick cherry-pick pick-src~1 pick-src &&
> +	git -C mid-pick checkout --theirs pick-one.t &&
> +	git -C mid-pick add pick-one.t &&
> +	git -C mid-pick commit --no-edit &&
> +	test_path_is_dir .git/worktrees/mid-pick/sequencer &&
> +	test_must_fail git worktree remove mid-pick 2>err &&
> +	test_grep "in the middle of a cherry-pick or revert" err &&
> +	git worktree remove --force mid-pick
> +'
> +
>   test_done


  reply	other threads:[~2026-11-02 14:07 UTC|newest]

Thread overview: 33+ 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 [this message]
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-02 18:29             ` [PATCH v4 2/4] doc: for-each-ref: say how to read %(worktreepath) safely Jasper Mayone
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-02 18:29             ` [PATCH v4 4/4] branch: do not mark the branch the current worktree is rebasing Jasper Mayone
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=bf554c5f-066c-4e58-a583-0f95aedecf95@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