git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 0/4] worktree: treat a branch being rebased or bisected as checked out

From
JMJasper Mayone <me@jaspermayone.com>
Date
Nov 2, 2026, 18:29 UTC
Message-ID
<20261102182930.3511-1-me@jaspermayone.com>
In-Reply-To
<20261030142137.54750-1-me@jaspermayone.com>

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(&current_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
-- 
2.56.0
Previous: Jasper MayoneNext: Jasper Mayone
Message 25 of 33 in “worktree list: add --merged and --no-merged”
  1. 0/1 worktree list: add --merged and --no-mergedJasper Mayone, Oct 21, 2026
  2. 1/1 worktree list: add --merged and --no-mergedJasper Mayone, Oct 21, 2026
  3. Junio C HamanoOct 23, 2026
  4. Eric SunshineOct 24, 2026
  5. worktree list: add --branch to select worktrees by branchJasper Mayone, Oct 24, 2026
  6. Phillip WoodOct 25, 2026
  7. Phillip WoodOct 26, 2026
  8. Eric SunshineOct 28, 2026
  9. Junio C HamanoOct 28, 2026
  10. Kristoffer HaugsbakkOct 28, 2026
  11. Jasper MayoneOct 29, 2026
  12. Eric SunshineOct 29, 2026
  13. Phillip WoodOct 29, 2026
  14. Ben KnobleOct 29, 2026
  15. Phillip WoodOct 29, 2026
  16. 0/2 worktree: treat a branch being rebased or bisected as checked outJasper Mayone, Oct 30, 2026
  17. 1/2 worktree remove: refuse a worktree in the middle of a rebase or bisectJasper Mayone, Oct 30, 2026
  18. Phillip WoodNov 2, 2026
  19. Jasper MayoneNov 2, 2026
  20. 2/2 ref-filter: name the worktree of a branch being rebased or bisectedJasper Mayone, Oct 30, 2026
  21. Phillip WoodNov 2, 2026
  22. Jasper MayoneNov 2, 2026
  23. Junio C HamanoOct 30, 2026
  24. Jasper MayoneOct 30, 2026
  25. 0/4 worktree: treat a branch being rebased or bisected as checked outJasper Mayone, Nov 2, 2026
  26. 1/4 worktree remove: refuse a worktree in the middle of an operationJasper Mayone, Nov 2, 2026
  27. 2/4 doc: for-each-ref: say how to read %(worktreepath) safelyJasper Mayone, Nov 2, 2026
  28. 3/4 ref-filter: name the worktree of a branch being rebased or bisectedJasper Mayone, Nov 2, 2026
  29. 4/4 branch: do not mark the branch the current worktree is rebasingJasper Mayone, Nov 2, 2026
  30. Harald NordgrenOct 21, 2026
  31. Junio C HamanoOct 21, 2026
  32. Harald NordgrenOct 22, 2026
  33. Kristoffer HaugsbakkOct 22, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.