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 0/4] worktree: treat a branch being rebased or bisected as checked out
Date: Tue, 10 Nov 2026 16:49:08 +0000 [thread overview]
Message-ID: <4899b8ef-69f3-461a-8856-2101b81b03fc@gmail.com> (raw)
In-Reply-To: <20261102182930.3511-1-me@jaspermayone.com>
Hi Jasper
This is moving in the right direction, but I think we want to use a hash
to lookup which worktrees are checked out, rather than iterating over
all worktress for every ref that we list.
Thanks
Phillip
On 02/11/2026 18:29, Jasper Mayone wrote:
> While a worktree is rebasing or bisecting a branch, its HEAD is
> detached. "git worktree add" and "git branch -d" already treat the
> branch as checked out there, but "git worktree remove" deletes such a
> worktree when its files are clean, and %(worktreepath) and "git
> branch" do not show which worktree has the branch.
>
> Patch 1 makes "git worktree remove" refuse a worktree in the middle of
> an operation unless --force is given. Patch 2 documents how to read
> %(worktreepath) safely. Patch 3 makes %(worktreepath) name the
> worktree that is rebasing or bisecting a branch, and patch 4 keeps
> "git branch" from marking that branch with "+" in that worktree.
>
> Changes since v3 [1]:
>
> * Patch 1: also refuse a worktree that has CHERRY_PICK_HEAD or
> REVERT_HEAD, for a single cherry-pick or revert whose files are
> clean, with tests. The git-worktree documentation now lists the
> cases that need --force. The tests use a shared setup and
> subshells. Dropped the Helped-by trailer, as Phillip asked.
>
> * Patch 2 is new. It adds the notes that Phillip suggested to the
> documentation of %(worktreepath): the path can contain a newline,
> and only one worktree is shown for a branch that is checked out in
> more than one.
>
> * Patch 3: %(worktreepath) now uses a new branch.c function,
> branch_checked_out_worktree(), that reads the list that
> prepare_checked_out_branches() builds, in place of a copy of that
> code in ref-filter.c. ref-filter.c loses its own worktree map. The
> commit message explains why branches that "rebase --update-refs"
> will update are not included, and the change for the HEAD of a
> bare repository, which now has a test.
>
> * Patch 4 is new. "git branch" no longer marks a branch with "+" in
> the worktree that is rebasing or bisecting it, because the
> "(no branch, ...)" line already names it.
>
> [1] https://lore.kernel.org/git/20261030142137.54750-1-me@jaspermayone.com/
>
> Jasper Mayone (4):
> worktree remove: refuse a worktree in the middle of an operation
> doc: for-each-ref: say how to read %(worktreepath) safely
> ref-filter: name the worktree of a branch being rebased or bisected
> branch: do not mark the branch the current worktree is rebasing
>
> Documentation/git-branch.adoc | 6 +-
> Documentation/git-for-each-ref.adoc | 12 +++-
> Documentation/git-worktree.adoc | 17 +++--
> branch.c | 13 ++++
> branch.h | 9 +++
> builtin/branch.c | 103 +++++++++++++++++++++-------
> builtin/worktree.c | 37 +++++++++-
> ref-filter.c | 74 +-------------------
> t/t2403-worktree-move.sh | 94 +++++++++++++++++++++++++
> t/t3203-branch-output.sh | 49 +++++++++++++
> t/t6302-for-each-ref-filter.sh | 76 ++++++++++++++++++++
> 11 files changed, 386 insertions(+), 104 deletions(-)
>
> Range-diff against v3:
> 1: a79b69a0e4 ! 1: bca692b844 worktree remove: refuse a worktree in the middle of a rebase or bisect
> @@ Metadata
> Author: Jasper Mayone <me@jaspermayone.com>
>
> ## Commit message ##
> - worktree remove: refuse a worktree in the middle of a rebase or bisect
> + worktree remove: refuse a worktree in the middle of an operation
>
> - "git worktree remove" refuses a worktree with modified or untracked
> - 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.
> + "git worktree remove" refuses to remove a worktree with modified or
> + untracked files unless --force is given, but it removes a worktree
> + that is in the middle of a rebase, a "git am" session, a bisect, a
> + cherry-pick or a revert 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. Examples of such a clean worktree are a rebase
> + stopped at an "edit" or "break" line or after a failed "exec", a
> + cherry-pick sequence after a conflict was resolved and committed, and
> + a single cherry-pick or revert whose conflict was resolved to the
> + current contents, which leaves CHERRY_PICK_HEAD or REVERT_HEAD behind.
>
> Treat such a worktree as unclean: refuse to remove it unless --force
> is given, and name the operation in the message.
> @@ Commit message
> worktree, which is only done when the directory exists.
>
> Reported-by: D. Ben Knoble <ben.knoble@gmail.com>
> - Helped-by: Phillip Wood <phillip.wood123@gmail.com>
> Signed-off-by: Jasper Mayone <me@jaspermayone.com>
>
> ## Documentation/git-worktree.adoc ##
> @@ Documentation/git-worktree.adoc: the new working tree location.
> +
> `remove`::
>
> - Remove a worktree. Only clean worktrees (no untracked files and no
> +-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
> -+sequence is not clean either, because removing it loses that state. 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
> ++* 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.
> ++--
>
> `repair [<path>...]`::
>
> @@ Documentation/git-worktree.adoc: twice. If the destination is already assigned t
> 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,
> -+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>`::
> @@ builtin/worktree.c: static void check_clean_worktree(struct worktree *wt,
> }
>
> +/*
> -+ * 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.
> ++ * 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)
> @@ builtin/worktree.c: static void check_clean_worktree(struct worktree *wt,
> + 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);
> @@ t/t2403-worktree-move.sh: test_expect_success 'move worktree with relative 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 init &&
> -+ test_commit -C mid-rebase rebase-me &&
> ++ 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 &&
> @@ t/t2403-worktree-move.sh: test_expect_success 'move worktree with relative path
> +
> +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 &&
> ++ 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
> @@ t/t2403-worktree-move.sh: test_expect_success 'move worktree with relative path
> +
> +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 &&
> ++ 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 sequence' '
> ++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 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 &&
> ++ 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 or revert" 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
> -: ---------- > 2: 39eb92d441 doc: for-each-ref: say how to read %(worktreepath) safely
> 2: 0cd13347e1 ! 3: 990a19fd11 ref-filter: name the worktree of a branch being rebased or bisected
> @@ Commit message
>
> The rest of Git already treats that branch as checked out: "git
> worktree add" and "git branch -d" refuse it, because finishing the
> - rebase or bisect will check it out again. "git worktree list" shows
> - such a worktree as "(detached HEAD)", so %(worktreepath) is the only
> - place a script could find out which worktree is working on the
> - branch.
> + rebase or bisect will check it out again. But no command shows which
> + worktree is working on the branch: "git worktree list" shows such a
> + worktree as "(detached HEAD)", and %(worktreepath) is empty.
>
> - Also map the branch that a worktree is rebasing or bisecting to that
> - worktree, as prepare_checked_out_branches() in branch.c does.
> - Branches that "rebase --update-refs" will update are not mapped: that
> - worktree has not checked them out, it only moves them when the rebase
> - is done.
> + branch.c already knows the answer, as prepare_checked_out_branches()
> + records the branch that each worktree has checked out, is rebasing or
> + is bisecting. Add branch_checked_out_worktree() to look a branch up in
> + that list, and use it for %(worktreepath) in place of the map that
> + ref-filter.c builds from the HEAD of each worktree.
>
> - As a result "git branch" marks such a branch with "+", also when it
> - runs in the worktree that is rebasing or bisecting it, where it shows
> - both "* (no branch, rebasing topic)" and "+ topic". Document that.
> + The new function skips the branches that "rebase --update-refs" will
> + update. branch_checked_out() includes them, so that they cannot be
> + checked out or deleted during the rebase, but the worktree that runs
> + the rebase has not checked them out. %(worktreepath) therefore stays
> + empty for them, unlike branch_checked_out().
> +
> + This also changes the result for a bare repository: its HEAD names a
> + branch, and %(worktreepath) used to give the path of the bare
> + repository for that branch. "git branch" in a linked worktree marked
> + the branch with "+", although no worktree has it checked out and "git
> + worktree add" lets you check it out. prepare_checked_out_branches()
> + skips bare repositories, so %(worktreepath) is now empty for that
> + branch and "git branch" no longer marks it.
>
> Reported-by: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>
> Helped-by: Phillip Wood <phillip.wood123@gmail.com>
> @@ Documentation/git-branch.adoc: DESCRIPTION
> branches are listed; the current branch will be highlighted in green and
> marked with an asterisk. Any branches checked out in linked worktrees will
> -be highlighted in cyan and marked with a plus sign. Option `-r` causes the
> +-remote-tracking branches to be listed,
> +be highlighted in cyan and marked with a plus sign. A branch that a
> -+worktree is rebasing or bisecting counts as checked out there, so it is
> -+marked in the same way, also in the worktree that is rebasing or
> -+bisecting it. Option `-r` causes the
> - remote-tracking branches to be listed,
> ++worktree is rebasing or bisecting counts as checked out in that
> ++worktree, so the other worktrees mark it in the same way. Option `-r`
> ++causes the remote-tracking branches to be listed,
> and option `-a` shows both local and remote branches.
>
> + If a `<pattern>`
>
> ## Documentation/git-for-each-ref.adoc ##
> @@ Documentation/git-for-each-ref.adoc: but if used together the last option is selected.
> @@ Documentation/git-for-each-ref.adoc: but if used together the last option is sel
> - out, if it is checked out in any linked worktree. Empty string
> + out, if it is checked out in any linked worktree. A branch that
> + a worktree is rebasing or bisecting counts as checked out there,
> -+ although that worktree's `HEAD` is detached. Empty string
> - otherwise.
> -
> - `ahead-behind:<commit-ish>`::
> ++ although that worktree's `HEAD` is detached. A branch that
> ++ `git rebase --update-refs` will update does not. Empty string
> + otherwise. If the ref is checked out in more than one worktree,
> + only one of them is shown. The path can contain a newline, so
> + use one of the quoting options such as `--shell`, or follow
>
> - ## ref-filter.c ##
> -@@ ref-filter.c: static struct expand_data {
> -
> - struct ref_to_worktree_entry {
> - struct hashmap_entry ent;
> -- struct worktree *wt; /* key is wt->head_ref */
> -+ struct worktree *wt;
> -+ char refname[FLEX_ARRAY]; /* the key */
> - };
> -
> - static int ref_to_worktree_map_cmpfnc(const void *lookupdata UNUSED,
> -@@ ref-filter.c: static int ref_to_worktree_map_cmpfnc(const void *lookupdata UNUSED,
> - e = container_of(eptr, const struct ref_to_worktree_entry, ent);
> - k = container_of(kptr, const struct ref_to_worktree_entry, ent);
> -
> -- return strcmp(e->wt->head_ref,
> -- keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
> -+ return strcmp(e->refname,
> -+ keydata_aka_refname ? keydata_aka_refname : k->refname);
> + ## branch.c ##
> +@@ branch.c: const char *branch_checked_out(const char *refname)
> + return strmap_get(¤t_checked_out_branches, refname);
> }
>
> - static struct ref_to_worktree_map {
> ++const char *branch_checked_out_worktree(const char *refname)
> ++{
> ++ const char *path = NULL;
> ++
> ++ prepare_checked_out_branches();
> ++ for (size_t i = 0; i < checked_out_branches_nr; i++) {
> ++ if (checked_out_branches[i].kind != BRANCH_CHECKOUT_KIND_UPDATE_REF &&
> ++ !strcmp(refname, checked_out_branches[i].refname))
> ++ path = checked_out_branches[i].path;
> ++ }
> ++ return path;
> ++}
> ++
> + const char *branch_bisecting(const char *refname)
> + {
> + prepare_checked_out_branches();
> +
> + ## branch.h ##
> +@@ branch.h: void create_branches_recursively(struct repository *r, const char *name,
> + */
> + const char *branch_checked_out(const char *refname);
> +
> ++/*
> ++ * If a worktree has the branch 'refname' checked out, or is rebasing or
> ++ * bisecting it, return the path of that worktree, or of the last one if
> ++ * there are more. Otherwise return NULL. Unlike branch_checked_out(),
> ++ * this ignores the branches that "git rebase --update-refs" will
> ++ * update, because no worktree has them checked out.
> ++ */
> ++const char *branch_checked_out_worktree(const char *refname);
> ++
> + /*
> + * If the branch at 'refname' is currently used for bisecting in a
> + * worktree, then return the path to that worktree.
> +
> + ## ref-filter.c ##
> +@@
> + #define DISABLE_SIGN_COMPARE_WARNINGS
> +
> + #include "git-compat-util.h"
> ++#include "branch.h"
> + #include "environment.h"
> + #include "gettext.h"
> + #include "config.h"
> +@@ ref-filter.c: static struct expand_data {
> + struct object_info info;
> + } oi, oi_deref;
> +
> +-struct ref_to_worktree_entry {
> +- struct hashmap_entry ent;
> +- struct worktree *wt; /* key is wt->head_ref */
> +-};
> +-
> +-static int ref_to_worktree_map_cmpfnc(const void *lookupdata UNUSED,
> +- const struct hashmap_entry *eptr,
> +- const struct hashmap_entry *kptr,
> +- const void *keydata_aka_refname)
> +-{
> +- const struct ref_to_worktree_entry *e, *k;
> +-
> +- e = container_of(eptr, const struct ref_to_worktree_entry, ent);
> +- k = container_of(kptr, const struct ref_to_worktree_entry, ent);
> +-
> +- return strcmp(e->wt->head_ref,
> +- keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
> +-}
> +-
> +-static struct ref_to_worktree_map {
> +- struct hashmap map;
> +- struct worktree **worktrees;
> +-} ref_to_worktree_map;
> +-
> + /*
> + * The enum atom_type is used as the index of valid_atom array.
> + * In the atom parsing stage, it will be passed to used_atom.atom_type
> @@ ref-filter.c: static int get_object(struct ref_array_item *ref, int deref,
> return ret;
> }
>
> -+static void add_worktree_entry(struct hashmap *map, struct worktree *wt,
> -+ const char *refname)
> -+{
> -+ struct ref_to_worktree_entry *entry;
> -+
> -+ FLEX_ALLOC_STR(entry, refname, refname);
> -+ entry->wt = wt;
> -+ hashmap_entry_init(&entry->ent, strhash(refname));
> -+ hashmap_add(map, &entry->ent);
> -+}
> -+
> -+/*
> -+ * Map each branch to the worktree that has it checked out. A worktree
> -+ * that is rebasing or bisecting a branch has it checked out, too,
> -+ * even though its HEAD may be detached, as for
> -+ * prepare_checked_out_branches() in branch.c.
> -+ */
> - static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
> - {
> +-static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
> +-{
> - int i;
> -+ struct strbuf ref = STRBUF_INIT;
> -
> +-
> - for (i = 0; worktrees[i]; i++) {
> - if (worktrees[i]->head_ref) {
> - struct ref_to_worktree_entry *entry;
> @@ ref-filter.c: static int get_object(struct ref_array_item *ref, int deref,
> - entry->wt = worktrees[i];
> - hashmap_entry_init(&entry->ent,
> - strhash(worktrees[i]->head_ref));
> -+ for (size_t i = 0; worktrees[i]; i++) {
> -+ struct worktree *wt = worktrees[i];
> -+ struct wt_status_state state = { 0 };
> -
> +-
> - hashmap_add(map, &entry->ent);
> -+ if (wt->is_bare)
> -+ continue;
> -+
> -+ if (wt->head_ref)
> -+ add_worktree_entry(map, wt, wt->head_ref);
> -+
> -+ if (wt_status_check_rebase(wt, &state) &&
> -+ (state.rebase_in_progress ||
> -+ state.rebase_interactive_in_progress) &&
> -+ state.branch) {
> -+ strbuf_reset(&ref);
> -+ strbuf_addf(&ref, "refs/heads/%s", state.branch);
> -+ add_worktree_entry(map, wt, ref.buf);
> - }
> -+ wt_status_state_free_buffers(&state);
> -+
> -+ if (wt_status_check_bisect(wt, &state) &&
> -+ state.bisecting_from) {
> -+ strbuf_reset(&ref);
> -+ strbuf_addf(&ref, "refs/heads/%s", state.bisecting_from);
> -+ add_worktree_entry(map, wt, ref.buf);
> -+ }
> -+ wt_status_state_free_buffers(&state);
> - }
> -+ strbuf_release(&ref);
> +- }
> +- }
> +-}
> +-
> +-static void lazy_init_worktree_map(void)
> +-{
> +- if (ref_to_worktree_map.worktrees)
> +- return;
> +-
> +- ref_to_worktree_map.worktrees = get_worktrees(the_repository);
> +- hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
> +- populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
> +-}
> +-
> + static char *get_worktree_path(const struct ref_array_item *ref)
> + {
> +- struct hashmap_entry entry, *e;
> +- struct ref_to_worktree_entry *lookup_result;
> ++ const char *path = branch_checked_out_worktree(ref->refname);
> +
> +- lazy_init_worktree_map();
> +-
> +- hashmap_entry_init(&entry, strhash(ref->refname));
> +- e = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
> +-
> +- if (!e)
> +- return xstrdup("");
> +-
> +- lookup_result = container_of(e, struct ref_to_worktree_entry, ent);
> +-
> +- return xstrdup(lookup_result->wt->path);
> ++ return xstrdup(path ? path : "");
> + }
> +
> + /*
> +@@ ref-filter.c: void ref_array_clear(struct ref_array *array)
> + FREE_AND_NULL(used_atom);
> + used_atom_cnt = 0;
> +
> +- if (ref_to_worktree_map.worktrees) {
> +- hashmap_clear_and_free(&(ref_to_worktree_map.map),
> +- struct ref_to_worktree_entry, ent);
> +- free_worktrees(ref_to_worktree_map.worktrees);
> +- ref_to_worktree_map.worktrees = NULL;
> +- }
> +-
> + FREE_AND_NULL(array->counts);
> }
>
> - static void lazy_init_worktree_map(void)
>
> ## t/t3203-branch-output.sh ##
> @@ t/t3203-branch-output.sh: test_expect_success 'worktree colors correct' '
> @@ t/t3203-branch-output.sh: test_expect_success 'worktree colors correct' '
> +test_expect_success 'branch being rebased in a worktree is marked' '
> + test_when_finished "git -C rebase_dir rebase --abort; rm -rf rebase_dir; git worktree prune; git branch -D rebasing" &&
> + git worktree add -b rebasing rebase_dir branch-two &&
> -+ git -C rebase_dir commit --allow-empty -m rebase-me &&
> -+ test_must_fail git -C rebase_dir rebase -f -x false HEAD~1 &&
> -+ test_must_fail git -C rebase_dir symbolic-ref -q HEAD &&
> ++ (
> ++ cd rebase_dir &&
> ++ git commit --allow-empty -m rebase-me &&
> ++ test_must_fail git rebase -f -x false HEAD~1 &&
> ++ test_must_fail git symbolic-ref -q HEAD
> ++ ) &&
> + git branch --list rebasing >actual &&
> + echo "+ rebasing" >expect &&
> + test_cmp expect actual
> +'
> +
> -+test_expect_success 'branch being rebased is marked in its own worktree' '
> -+ test_when_finished "git -C rebase_dir rebase --abort; rm -rf rebase_dir; git worktree prune; git branch -D rebasing" &&
> -+ git worktree add -b rebasing rebase_dir branch-two &&
> -+ git -C rebase_dir commit --allow-empty -m rebase-me &&
> -+ test_must_fail git -C rebase_dir rebase -f -x false HEAD~1 &&
> -+ git -C rebase_dir branch --list rebasing >actual &&
> -+ echo "+ rebasing" >expect &&
> -+ test_cmp expect actual &&
> -+ git -C rebase_dir branch >actual &&
> -+ test_grep "^\* (no branch, rebasing rebasing)" actual
> -+'
> -+
> +test_expect_success 'branch being bisected in a worktree is marked' '
> + test_when_finished "git -C bisect_dir bisect reset; rm -rf bisect_dir; git worktree prune; git branch -D bisecting" &&
> + git worktree add -b bisecting bisect_dir branch-two &&
> -+ git -C bisect_dir commit --allow-empty -m bisect-one &&
> -+ git -C bisect_dir commit --allow-empty -m bisect-two &&
> -+ git -C bisect_dir bisect start HEAD HEAD~2 &&
> -+ test_must_fail git -C bisect_dir symbolic-ref -q HEAD &&
> ++ (
> ++ cd bisect_dir &&
> ++ git commit --allow-empty -m bisect-one &&
> ++ git commit --allow-empty -m bisect-two &&
> ++ git bisect start HEAD HEAD~2 &&
> ++ test_must_fail git symbolic-ref -q HEAD
> ++ ) &&
> + git branch --list bisecting >actual &&
> + echo "+ bisecting" >expect &&
> + test_cmp expect actual
> @@ t/t6302-for-each-ref-filter.sh: test_expect_success 'validate worktree atom' '
> + git for-each-ref --format="%(worktreepath)" refs/heads/ >actual &&
> + test_grep ! detached-rebase actual
> +'
> ++
> ++test_expect_success 'worktree atom ignores the HEAD of a bare repository' '
> ++ test_when_finished "rm -rf bare.git bare-wt" &&
> ++ git clone --bare . bare.git &&
> ++ git -C bare.git symbolic-ref HEAD refs/heads/main &&
> ++ git -C bare.git worktree add --detach ../bare-wt main &&
> ++ git -C bare.git for-each-ref --format="%(worktreepath)" refs/heads/main >actual &&
> ++ echo >expect &&
> ++ test_cmp expect actual &&
> ++ git -C bare-wt branch --list main >actual &&
> ++ echo " main" >expect &&
> ++ test_cmp expect actual
> ++'
> +
> test_expect_success 'start after with empty value' '
> cat >expect <<-\EOF &&
> -: ---------- > 4: 56e29c448f branch: do not mark the branch the current worktree is rebasing
next prev parent reply other threads:[~2026-11-10 16:49 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
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 ` Phillip Wood [this message]
2026-11-11 15:28 ` [PATCH v5 0/5] worktree: treat a branch being rebased or bisected as checked out 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=4899b8ef-69f3-461a-8856-2101b81b03fc@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