From: Jasper Mayone <me@jaspermayone.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, phillip.wood123@gmail.com,
sunshine@sunshineco.com, kristofferhaugsbakk@fastmail.com,
ben.knoble@gmail.com, haraldnordgren@gmail.com,
Jasper Mayone <me@jaspermayone.com>
Subject: [PATCH v4 0/4] worktree: treat a branch being rebased or bisected as checked out
Date: Mon, 2 Nov 2026 13:29:26 -0500 [thread overview]
Message-ID: <20261102182930.3511-1-me@jaspermayone.com> (raw)
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(¤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
--
2.56.0
next prev parent reply other threads:[~2026-11-02 18:29 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
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 ` Jasper Mayone [this message]
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=20261102182930.3511-1-me@jaspermayone.com \
--to=me@jaspermayone.com \
--cc=ben.knoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=haraldnordgren@gmail.com \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=phillip.wood123@gmail.com \
--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