{"thread":{"id":"66032","subject":"[PATCH] branch: report kind of checkout when rejecting delete","startedAt":"2026-07-18T04:39:21Z","lastAt":"2026-07-29T15:22:26Z","messageCount":10,"participants":["René Scharfe","Junio C Hamano","Phillip Wood","Toon Claes"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"548566","messageId":"9865fc6b-e3fe-4614-9ffe-71af776e1796@web.de","threadId":"66032","inReplyTo":null,"subject":"[PATCH] branch: report kind of checkout when rejecting delete","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2026-07-18T04:39:18Z","receivedAt":"2026-07-18T04:39:21Z","isPatch":true,"body":"git branch refuses to delete branches that are currently checked out\nwith a message like this: \"error: cannot delete branch 'foo' used by\nworktree at '/path/of/worktree'\".  This can be confusing with internal\ncheckouts, e.g. if one tries to delete a branch associated with an\nactive bisect run.\n\nMention the kind of internal checkout, if any, to spare the user from\nremembering that they might have forgotten a bisect or rebase.  To do\nthat, register the checkout reason in a strintmap alongside the existing\nstrmap that stores the worktree path.\n\nSuggested-by: stsp <stsp2@yandex.ru>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\nOriginal message:\nhttps://lore.kernel.org/git/cae34516-5437-49d3-8d39-16f4059a81a8@yandex.ru/\n\n branch.c          | 48 ++++++++++++++++++++++++++---------------------\n branch.h          | 15 +++++++++++++++\n builtin/branch.c  | 31 +++++++++++++++++++++++++++---\n t/t3200-branch.sh |  4 ++--\n 4 files changed, 72 insertions(+), 26 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 243db7d0fc..aaa54f1b62 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -384,6 +384,16 @@ int validate_branchname(const char *name, struct strbuf *ref)\n \n static int initialized_checked_out_branches;\n static struct strmap current_checked_out_branches = STRMAP_INIT;\n+static struct strintmap current_checked_out_branch_kinds = STRINTMAP_INIT;\n+\n+static void register_checked_out_branch(const char *refname, const char *path,\n+\t\t\t\t\tenum branch_checkout_kind kind)\n+{\n+\tchar *old = strmap_put(&current_checked_out_branches, refname,\n+\t\t\t       xstrdup(path));\n+\tfree(old);\n+\tstrintmap_set(&current_checked_out_branch_kinds, refname, kind);\n+}\n \n static void prepare_checked_out_branches(void)\n {\n@@ -397,7 +407,7 @@ static void prepare_checked_out_branches(void)\n \tworktrees = get_worktrees();\n \n \twhile (worktrees[i]) {\n-\t\tchar *old, *wt_gitdir;\n+\t\tchar *wt_gitdir;\n \t\tstruct wt_status_state state = { 0 };\n \t\tstruct worktree *wt = worktrees[i++];\n \t\tstruct string_list update_refs = STRING_LIST_INIT_DUP;\n@@ -405,22 +415,17 @@ static void prepare_checked_out_branches(void)\n \t\tif (wt->is_bare)\n \t\t\tcontinue;\n \n-\t\tif (wt->head_ref) {\n-\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t wt->head_ref,\n-\t\t\t\t\t xstrdup(wt->path));\n-\t\t\tfree(old);\n-\t\t}\n+\t\tif (wt->head_ref)\n+\t\t\tregister_checked_out_branch(wt->head_ref, wt->path,\n+\t\t\t\tBRANCH_CHECKOUT_KIND_CHECKOUT);\n \n \t\tif (wt_status_check_rebase(wt, &state) &&\n \t\t    (state.rebase_in_progress || state.rebase_interactive_in_progress) &&\n \t\t    state.branch) {\n \t\t\tstruct strbuf ref = STRBUF_INIT;\n \t\t\tstrbuf_addf(&ref, \"refs/heads/%s\", state.branch);\n-\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t ref.buf,\n-\t\t\t\t\t xstrdup(wt->path));\n-\t\t\tfree(old);\n+\t\t\tregister_checked_out_branch(ref.buf, wt->path,\n+\t\t\t\tBRANCH_CHECKOUT_KIND_REBASE);\n \t\t\tstrbuf_release(&ref);\n \t\t}\n \t\twt_status_state_free_buffers(&state);\n@@ -429,10 +434,8 @@ static void prepare_checked_out_branches(void)\n \t\t    state.bisecting_from) {\n \t\t\tstruct strbuf ref = STRBUF_INIT;\n \t\t\tstrbuf_addf(&ref, \"refs/heads/%s\", state.bisecting_from);\n-\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t ref.buf,\n-\t\t\t\t\t xstrdup(wt->path));\n-\t\t\tfree(old);\n+\t\t\tregister_checked_out_branch(ref.buf, wt->path,\n+\t\t\t\tBRANCH_CHECKOUT_KIND_BISECT);\n \t\t\tstrbuf_release(&ref);\n \t\t}\n \t\twt_status_state_free_buffers(&state);\n@@ -441,12 +444,9 @@ static void prepare_checked_out_branches(void)\n \t\tif (!sequencer_get_update_refs_state(wt_gitdir,\n \t\t\t\t\t\t     &update_refs)) {\n \t\t\tstruct string_list_item *item;\n-\t\t\tfor_each_string_list_item(item, &update_refs) {\n-\t\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t\t item->string,\n-\t\t\t\t\t\t xstrdup(wt->path));\n-\t\t\t\tfree(old);\n-\t\t\t}\n+\t\t\tfor_each_string_list_item(item, &update_refs)\n+\t\t\t\tregister_checked_out_branch(item->string, wt->path,\n+\t\t\t\t\tBRANCH_CHECKOUT_KIND_UPDATE_REF);\n \t\t\tstring_list_clear(&update_refs, 1);\n \t\t}\n \n@@ -462,6 +462,12 @@ const char *branch_checked_out(const char *refname)\n \treturn strmap_get(&current_checked_out_branches, refname);\n }\n \n+enum branch_checkout_kind branch_checkout_kind(const char *refname)\n+{\n+\tprepare_checked_out_branches();\n+\treturn strintmap_get(&current_checked_out_branch_kinds, refname);\n+}\n+\n /*\n  * Check if a branch 'name' can be created as a new branch; die otherwise.\n  * 'force' can be used when it is OK for the named branch already exists.\ndiff --git a/branch.h b/branch.h\nindex 3dc6e2a0ff..d1073fe1cd 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -15,6 +15,14 @@ enum branch_track {\n \tBRANCH_TRACK_SIMPLE,\n };\n \n+enum branch_checkout_kind {\n+\tBRANCH_CHECKOUT_KIND_UNSPECIFIED = 0,\n+\tBRANCH_CHECKOUT_KIND_CHECKOUT,\n+\tBRANCH_CHECKOUT_KIND_REBASE,\n+\tBRANCH_CHECKOUT_KIND_BISECT,\n+\tBRANCH_CHECKOUT_KIND_UPDATE_REF,\n+};\n+\n /* Functions for acting on the information about branches. */\n \n /**\n@@ -106,6 +114,13 @@ void create_branches_recursively(struct repository *r, const char *name,\n  */\n const char *branch_checked_out(const char *refname);\n \n+/*\n+ * If the branch at 'refname' is currently checked out in a worktree,\n+ * then return the kind of checkout, i.e. whether it was done by an\n+ * actual checkout or a rebase etc.\n+ */\n+enum branch_checkout_kind branch_checkout_kind(const char *refname);\n+\n /*\n  * Check if 'name' can be a valid name for a branch; die otherwise.\n  * Return 1 if the named branch already exists; return 0 otherwise.\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex dede60d27b..3223347129 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -266,9 +266,34 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n \t\tif (kinds == FILTER_REFS_BRANCHES) {\n \t\t\tconst char *path;\n \t\t\tif ((path = branch_checked_out(name))) {\n-\t\t\t\terror(_(\"cannot delete branch '%s' \"\n-\t\t\t\t\t\"used by worktree at '%s'\"),\n-\t\t\t\t      bname.buf, path);\n+\t\t\t\tint kind = branch_checkout_kind(name);\n+\t\t\t\tswitch (kind) {\n+\t\t\t\tcase BRANCH_CHECKOUT_KIND_CHECKOUT:\n+\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n+\t\t\t\t\t\t\"used by worktree at '%s'\"),\n+\t\t\t\t\t      bname.buf, path);\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase BRANCH_CHECKOUT_KIND_REBASE:\n+\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n+\t\t\t\t\t\t\"used by worktree at '%s' \"\n+\t\t\t\t\t\t\"for rebase\"),\n+\t\t\t\t\t      bname.buf, path);\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase BRANCH_CHECKOUT_KIND_BISECT:\n+\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n+\t\t\t\t\t\t\"used by worktree at '%s' \"\n+\t\t\t\t\t\t\"for bisect\"),\n+\t\t\t\t\t      bname.buf, path);\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase BRANCH_CHECKOUT_KIND_UPDATE_REF:\n+\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n+\t\t\t\t\t\t\"used by worktree at '%s' \"\n+\t\t\t\t\t\t\"for update-ref\"),\n+\t\t\t\t\t      bname.buf, path);\n+\t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\tBUG(\"invalid checkout kind %d\", kind);\n+\t\t\t\t}\n \t\t\t\tret = 1;\n \t\t\t\tcontinue;\n \t\t\t}\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex e2682a83a0..e5df493b66 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -930,7 +930,7 @@ test_expect_success 'deleting currently checked out branch fails' '\n \tgit worktree add -b my7 my7 &&\n \ttest_must_fail git -C my7 branch -d my7 &&\n \ttest_must_fail git branch -d my7 2>actual &&\n-\tgrep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n+\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*'\\$\"'\" actual &&\n \trm -r my7 &&\n \tgit worktree prune\n '\n@@ -941,7 +941,7 @@ test_expect_success 'deleting in-use branch fails' '\n \tgit -C my7 bisect start HEAD HEAD~2 &&\n \ttest_must_fail git -C my7 branch -d my7 &&\n \ttest_must_fail git branch -d my7 2>actual &&\n-\tgrep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n+\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*' for bisect\\$\"'\" actual &&\n \trm -r my7 &&\n \tgit worktree prune\n '\n-- \n2.55.0\n\n"},{"id":"548589","messageId":"xmqqjyqsqk1a.fsf@gitster.g","threadId":"66032","inReplyTo":"9865fc6b-e3fe-4614-9ffe-71af776e1796@web.de","subject":"Re: [PATCH] branch: report kind of checkout when rejecting delete","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-18T17:34:09Z","receivedAt":"2026-07-18T17:34:12Z","isPatch":true,"body":"René Scharfe <l.s.r@web.de> writes:\n\n> git branch refuses to delete branches that are currently checked out\n> with a message like this: \"error: cannot delete branch 'foo' used by\n> worktree at '/path/of/worktree'\".  This can be confusing with internal\n> checkouts, e.g. if one tries to delete a branch associated with an\n> active bisect run.\n>\n> Mention the kind of internal checkout, if any, to spare the user from\n> remembering that they might have forgotten a bisect or rebase.  To do\n> that, register the checkout reason in a strintmap alongside the existing\n> strmap that stores the worktree path.\n>\n> Suggested-by: stsp <stsp2@yandex.ru>\n> Signed-off-by: René Scharfe <l.s.r@web.de>\n> ---\n> Original message:\n> https://lore.kernel.org/git/cae34516-5437-49d3-8d39-16f4059a81a8@yandex.ru/\n\nThis reminds me of another recent discussion on rewriting a branch\nthat is checked out elsewhere, where the \"git history\" command\nforgot to apply the same safety check:\n\nhttps://lore.kernel.org/git/e7dbcede-4486-459c-aa64-e44690e01fe0@gmail.com/\n\nWe definitely need an easy-to-use API to determine consistently\nwhich branches are in use, and to teach all commands that repoint\nbranch tips to use it to offer the same safety to users.  The\nframework that this patch introduces might be a good starting point\nfor that effort.\n\n> diff --git a/branch.h b/branch.h\n> index 3dc6e2a0ff..d1073fe1cd 100644\n> --- a/branch.h\n> +++ b/branch.h\n> @@ -15,6 +15,14 @@ enum branch_track {\n>  \tBRANCH_TRACK_SIMPLE,\n>  };\n>  \n> +enum branch_checkout_kind {\n> +\tBRANCH_CHECKOUT_KIND_UNSPECIFIED = 0,\n> +\tBRANCH_CHECKOUT_KIND_CHECKOUT,\n> +\tBRANCH_CHECKOUT_KIND_REBASE,\n> +\tBRANCH_CHECKOUT_KIND_BISECT,\n> +\tBRANCH_CHECKOUT_KIND_UPDATE_REF,\n> +};\n> +\n> ...\n> +/*\n> + * If the branch at 'refname' is currently checked out in a worktree,\n> + * then return the kind of checkout, i.e. whether it was done by an\n> + * actual checkout or a rebase etc.\n> + */\n> +enum branch_checkout_kind branch_checkout_kind(const char *refname);\n\nOK.\n\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index dede60d27b..3223347129 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -266,9 +266,34 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n>  \t\tif (kinds == FILTER_REFS_BRANCHES) {\n>  \t\t\tconst char *path;\n>  \t\t\tif ((path = branch_checked_out(name))) {\n> -\t\t\t\terror(_(\"cannot delete branch '%s' \"\n> -\t\t\t\t\t\"used by worktree at '%s'\"),\n> -\t\t\t\t      bname.buf, path);\n> +\t\t\t\tint kind = branch_checkout_kind(name);\n\nNot \"enum branch_checkout_kind\" but \"int\"?\n\n> +\t\t\t\tswitch (kind) {\n> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_CHECKOUT:\n> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n> +\t\t\t\t\t\t\"used by worktree at '%s'\"),\n> +\t\t\t\t\t      bname.buf, path);\n> +\t\t\t\t\tbreak;\n\nWe may want to be more explicit and say \"cannot delete\nbranch 'frotz' checked out in worktree at '/tmp/nitfol'\"\ninstead.  Unless this is a catch-all entry for states that\nare neither 'rebase', 'bisect', nor 'rebase-merges' but are\nsomehow otherwise in use, that is.\n\n> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_UPDATE_REF:\n> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n> +\t\t\t\t\t\t\"used by worktree at '%s' \"\n> +\t\t\t\t\t\t\"for update-ref\"),\n> +\t\t\t\t\t      bname.buf, path);\n> +\t\t\t\t\tbreak;\n\nI was quite lost when searching for cases where this 'update-ref'\nstate might be encountered, and I still lack confidence.  Can\nwe make the diagnostic message a bit friendlier to our users?\n\nFor instance, something like: 'You are rebasing a history with\nmerges in that other worktree, and the tip of this branch will\nbe updated when that process completes, so you cannot delete\nit from here.'  (Naturally, I may have misidentified the exact\nnature of the error, but this illustrates the level of detail and\nuser-facing clarity I hope to see.)\n\n> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n> index e2682a83a0..e5df493b66 100755\n> --- a/t/t3200-branch.sh\n> +++ b/t/t3200-branch.sh\n> @@ -930,7 +930,7 @@ test_expect_success 'deleting currently checked out branch fails' '\n>  \tgit worktree add -b my7 my7 &&\n>  \ttest_must_fail git -C my7 branch -d my7 &&\n>  \ttest_must_fail git branch -d my7 2>actual &&\n> -\tgrep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n> +\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*'\\$\"'\" actual &&\n>  \trm -r my7 &&\n>  \tgit worktree prune\n>  '\n> @@ -941,7 +941,7 @@ test_expect_success 'deleting in-use branch fails' '\n>  \tgit -C my7 bisect start HEAD HEAD~2 &&\n>  \ttest_must_fail git -C my7 branch -d my7 &&\n>  \ttest_must_fail git branch -d my7 2>actual &&\n> -\tgrep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n> +\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*' for bisect\\$\"'\" actual &&\n>  \trm -r my7 &&\n>  \tgit worktree prune\n>  '\n\nWe distinguish four kinds in the code but we test only two of them?\n\nThanks.  I very much like the direction this is taking us.\n"},{"id":"548594","messageId":"c7357faf-3d2b-46c6-99e7-88d3e2c72a77@web.de","threadId":"66032","inReplyTo":"xmqqjyqsqk1a.fsf@gitster.g","subject":"Re: [PATCH] branch: report kind of checkout when rejecting delete","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2026-07-18T19:07:41Z","receivedAt":"2026-07-18T19:07:56Z","isPatch":true,"body":"On 7/18/26 7:34 PM, Junio C Hamano wrote:\n> René Scharfe <l.s.r@web.de> writes:\n> \n>> git branch refuses to delete branches that are currently checked out\n>> with a message like this: \"error: cannot delete branch 'foo' used by\n>> worktree at '/path/of/worktree'\".  This can be confusing with internal\n>> checkouts, e.g. if one tries to delete a branch associated with an\n>> active bisect run.\n>>\n>> Mention the kind of internal checkout, if any, to spare the user from\n>> remembering that they might have forgotten a bisect or rebase.  To do\n>> that, register the checkout reason in a strintmap alongside the existing\n>> strmap that stores the worktree path.\n>>\n>> Suggested-by: stsp <stsp2@yandex.ru>\n>> Signed-off-by: René Scharfe <l.s.r@web.de>\n>> ---\n>> Original message:\n>> https://lore.kernel.org/git/cae34516-5437-49d3-8d39-16f4059a81a8@yandex.ru/\n> \n> This reminds me of another recent discussion on rewriting a branch\n> that is checked out elsewhere, where the \"git history\" command\n> forgot to apply the same safety check:\n> \n> https://lore.kernel.org/git/e7dbcede-4486-459c-aa64-e44690e01fe0@gmail.com/\n> \n> We definitely need an easy-to-use API to determine consistently\n> which branches are in use, and to teach all commands that repoint\n> branch tips to use it to offer the same safety to users.  The\n> framework that this patch introduces might be a good starting point\n> for that effort.\n\nbranch_checked_out() already allows to check whether a branch is in use,\nbut I guess git history needs to respond differently depending on the\nkind of use, e.g. leave active bisects and rebases untouched and update\nchecked out branches.  branch_checkout_kind() would allow that, but I\nnow wonder if it suffices for cases where branches appear in multiple\nworktrees.  So perhaps the query we need to enable are \"is this branch\nused by a rebase/bisect\" and not the more limited \"tell me one use of\nthis branch\"?\n\n>> diff --git a/branch.h b/branch.h\n>> index 3dc6e2a0ff..d1073fe1cd 100644\n>> --- a/branch.h\n>> +++ b/branch.h\n>> @@ -15,6 +15,14 @@ enum branch_track {\n>>  \tBRANCH_TRACK_SIMPLE,\n>>  };\n>>  \n>> +enum branch_checkout_kind {\n>> +\tBRANCH_CHECKOUT_KIND_UNSPECIFIED = 0,\n>> +\tBRANCH_CHECKOUT_KIND_CHECKOUT,\n>> +\tBRANCH_CHECKOUT_KIND_REBASE,\n>> +\tBRANCH_CHECKOUT_KIND_BISECT,\n>> +\tBRANCH_CHECKOUT_KIND_UPDATE_REF,\n>> +};\n>> +\n>> ...\n>> +/*\n>> + * If the branch at 'refname' is currently checked out in a worktree,\n>> + * then return the kind of checkout, i.e. whether it was done by an\n>> + * actual checkout or a rebase etc.\n>> + */\n>> +enum branch_checkout_kind branch_checkout_kind(const char *refname);\n> \n> OK.\n> \n>> diff --git a/builtin/branch.c b/builtin/branch.c\n>> index dede60d27b..3223347129 100644\n>> --- a/builtin/branch.c\n>> +++ b/builtin/branch.c\n>> @@ -266,9 +266,34 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n>>  \t\tif (kinds == FILTER_REFS_BRANCHES) {\n>>  \t\t\tconst char *path;\n>>  \t\t\tif ((path = branch_checked_out(name))) {\n>> -\t\t\t\terror(_(\"cannot delete branch '%s' \"\n>> -\t\t\t\t\t\"used by worktree at '%s'\"),\n>> -\t\t\t\t      bname.buf, path);\n>> +\t\t\t\tint kind = branch_checkout_kind(name);\n> \n> Not \"enum branch_checkout_kind\" but \"int\"?\n\nYes, it doesn't matter for the switch and is easier to print.\n\n> \n>> +\t\t\t\tswitch (kind) {\n>> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_CHECKOUT:\n>> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n>> +\t\t\t\t\t\t\"used by worktree at '%s'\"),\n>> +\t\t\t\t\t      bname.buf, path);\n>> +\t\t\t\t\tbreak;\n> \n> We may want to be more explicit and say \"cannot delete\n> branch 'frotz' checked out in worktree at '/tmp/nitfol'\"\n> instead.  Unless this is a catch-all entry for states that\n> are neither 'rebase', 'bisect', nor 'rebase-merges' but are\n> somehow otherwise in use, that is.\n> \n>> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_UPDATE_REF:\n>> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n>> +\t\t\t\t\t\t\"used by worktree at '%s' \"\n>> +\t\t\t\t\t\t\"for update-ref\"),\n>> +\t\t\t\t\t      bname.buf, path);\n>> +\t\t\t\t\tbreak;\n> \n> I was quite lost when searching for cases where this 'update-ref'\n> state might be encountered, and I still lack confidence.  Can\n> we make the diagnostic message a bit friendlier to our users?\n> \n> For instance, something like: 'You are rebasing a history with\n> merges in that other worktree, and the tip of this branch will\n> be updated when that process completes, so you cannot delete\n> it from here.'  (Naturally, I may have misidentified the exact\n> nature of the error, but this illustrates the level of detail and\n> user-facing clarity I hope to see.)\n\nThat's quite long.  Would it make sense to throw that update-ref\ncase into the rebase bin, i.e. only distinguish between checkout,\nbisect and rebase?\n\n> \n>> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n>> index e2682a83a0..e5df493b66 100755\n>> --- a/t/t3200-branch.sh\n>> +++ b/t/t3200-branch.sh\n>> @@ -930,7 +930,7 @@ test_expect_success 'deleting currently checked out branch fails' '\n>>  \tgit worktree add -b my7 my7 &&\n>>  \ttest_must_fail git -C my7 branch -d my7 &&\n>>  \ttest_must_fail git branch -d my7 2>actual &&\n>> -\tgrep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n>> +\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*'\\$\"'\" actual &&\n>>  \trm -r my7 &&\n>>  \tgit worktree prune\n>>  '\n>> @@ -941,7 +941,7 @@ test_expect_success 'deleting in-use branch fails' '\n>>  \tgit -C my7 bisect start HEAD HEAD~2 &&\n>>  \ttest_must_fail git -C my7 branch -d my7 &&\n>>  \ttest_must_fail git branch -d my7 2>actual &&\n>> -\tgrep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n>> +\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*' for bisect\\$\"'\" actual &&\n>>  \trm -r my7 &&\n>>  \tgit worktree prune\n>>  '\n> \n> We distinguish four kinds in the code but we test only two of them?\nLaziness, and it was enough for the user requirement..\n\nRené\n\n"},{"id":"548612","messageId":"xmqqa4roq7a8.fsf@gitster.g","threadId":"66032","inReplyTo":"c7357faf-3d2b-46c6-99e7-88d3e2c72a77@web.de","subject":"Re: [PATCH] branch: report kind of checkout when rejecting delete","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-18T22:09:35Z","receivedAt":"2026-07-18T22:09:38Z","isPatch":true,"body":"René Scharfe <l.s.r@web.de> writes:\n\n>>> +\t\t\t\tint kind = branch_checkout_kind(name);\n>> \n>> Not \"enum branch_checkout_kind\" but \"int\"?\n>\n> Yes, it doesn't matter for the switch and is easier to print.\n\nI do not understand the \"print\" part.  I was probably in the last\ngroup of people who was forced to switch from CPP macros to enum\nand their argument was always \"'print kind' in GDB gives symbolic\noutput\".  As \"enum\" is an glorified \"int\", wouldn't \n\n\tint i_kind;\n\tenum branch_checkout_kind e_kind;\n\tBUG(_(\"we did not expect %d %d\"), e_kind, i_kind);\n\ndo just what we expect?\n\n>>> +\t\t\t\tswitch (kind) {\n>>> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_CHECKOUT:\n>>> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n>>> +\t\t\t\t\t\t\"used by worktree at '%s'\"),\n>>> +\t\t\t\t\t      bname.buf, path);\n>>> +\t\t\t\t\tbreak;\n>> \n>> We may want to be more explicit and say \"cannot delete\n>> branch 'frotz' checked out in worktree at '/tmp/nitfol'\"\n>> instead.  Unless this is a catch-all entry for states that\n>> are neither 'rebase', 'bisect', nor 'rebase-merges' but are\n>> somehow otherwise in use, that is.\n>> \n>>> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_UPDATE_REF:\n>>> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n>>> +\t\t\t\t\t\t\"used by worktree at '%s' \"\n>>> +\t\t\t\t\t\t\"for update-ref\"),\n>>> +\t\t\t\t\t      bname.buf, path);\n>>> +\t\t\t\t\tbreak;\n>> \n>> I was quite lost when searching for cases where this 'update-ref'\n>> state might be encountered, and I still lack confidence.  Can\n>> we make the diagnostic message a bit friendlier to our users?\n>> \n>> For instance, something like: 'You are rebasing a history with\n>> merges in that other worktree, and the tip of this branch will\n>> be updated when that process completes, so you cannot delete\n>> it from here.'  (Naturally, I may have misidentified the exact\n>> nature of the error, but this illustrates the level of detail and\n>> user-facing clarity I hope to see.)\n>\n> That's quite long.  Would it make sense to throw that update-ref\n> case into the rebase bin, i.e. only distinguish between checkout,\n> bisect and rebase?\n\nShortening a quite long expression down to digestable pieces is left\nas an exercise for those with this particular itch to scratch ;-).\nI do not personally mind if it ends up indistinguishable from other\n\"rebase\" case (or unified the \"kind\" enum into one), but others may\nhave ideas to shorten the message to fit in the pattern we see\nabove.\n\nThanks.\n"},{"id":"548621","messageId":"20acbbc9-67e0-4b9e-9e4c-9f5dbebccc58@web.de","threadId":"66032","inReplyTo":"xmqqa4roq7a8.fsf@gitster.g","subject":"Re: [PATCH] branch: report kind of checkout when rejecting delete","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2026-07-19T05:55:48Z","receivedAt":"2026-07-19T05:55:58Z","isPatch":true,"body":"On 7/19/26 12:09 AM, Junio C Hamano wrote:\n> René Scharfe <l.s.r@web.de> writes:\n> \n>>>> +\t\t\t\tint kind = branch_checkout_kind(name);\n>>>\n>>> Not \"enum branch_checkout_kind\" but \"int\"?\n>>\n>> Yes, it doesn't matter for the switch and is easier to print.\n> \n> I do not understand the \"print\" part.  I was probably in the last\n> group of people who was forced to switch from CPP macros to enum\n> and their argument was always \"'print kind' in GDB gives symbolic\n> output\".  As \"enum\" is an glorified \"int\", wouldn't \n> \n> \tint i_kind;\n> \tenum branch_checkout_kind e_kind;\n> \tBUG(_(\"we did not expect %d %d\"), e_kind, i_kind);\n> \n> do just what we expect?\n\nTrue, integer promotion makes this work regardless of the underlying\ntype of the enum.  It was easier for me to make that conversion\nexplicit than to remember the conversion rule.  Which isn't that\ncomplicated, though, admittedly.\n\nRené\n\n"},{"id":"548624","messageId":"03a332e0-afaa-4562-a503-2ff8a8f9f2ac@gmail.com","threadId":"66032","inReplyTo":"xmqqa4roq7a8.fsf@gitster.g","subject":"Re: [PATCH] branch: report kind of checkout when rejecting delete","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-07-19T09:50:59Z","receivedAt":"2026-07-19T09:51:05Z","isPatch":true,"body":"On 18/07/2026 23:09, Junio C Hamano wrote:\n> René Scharfe <l.s.r@web.de> writes:\n> \n>>>> +\t\t\t\tswitch (kind) {\n>>>> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_CHECKOUT:\n>>>> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n>>>> +\t\t\t\t\t\t\"used by worktree at '%s'\"),\n>>>> +\t\t\t\t\t      bname.buf, path);\n>>>> +\t\t\t\t\tbreak;\n>>>\n>>> We may want to be more explicit and say \"cannot delete\n>>> branch 'frotz' checked out in worktree at '/tmp/nitfol'\"\n>>> instead.  Unless this is a catch-all entry for states that\n>>> are neither 'rebase', 'bisect', nor 'rebase-merges' but are\n>>> somehow otherwise in use, that is.\n\nThat's a great suggestion, I don't think there are any other cases so it \nshould be fine to say \"checked out\".\n>>>> +\t\t\t\tcase BRANCH_CHECKOUT_KIND_UPDATE_REF:\n>>>> +\t\t\t\t\terror(_(\"cannot delete branch '%s' \"\n>>>> +\t\t\t\t\t\t\"used by worktree at '%s' \"\n>>>> +\t\t\t\t\t\t\"for update-ref\"),\n>>>> +\t\t\t\t\t      bname.buf, path);\n>>>> +\t\t\t\t\tbreak;\n>>>\n>>> I was quite lost when searching for cases where this 'update-ref'\n>>> state might be encountered, and I still lack confidence.  Can\n>>> we make the diagnostic message a bit friendlier to our users?\n>>>\n>>> For instance, something like: 'You are rebasing a history with\n>>> merges in that other worktree, and the tip of this branch will\n>>> be updated when that process completes, so you cannot delete\n>>> it from here.'  (Naturally, I may have misidentified the exact\n>>> nature of the error, but this illustrates the level of detail and\n>>> user-facing clarity I hope to see.)\n>>\n>> That's quite long.  Would it make sense to throw that update-ref\n>> case into the rebase bin, i.e. only distinguish between checkout,\n>> bisect and rebase?\n\nI also wondered whether we should fold this into the rebase case. My \nconcern is that if the user sees\n\n     cannot delete branch 'feature' because it is being rebased in the\n     worktree '../feature'\n\nand then they do\n\n     cd ../feature\n     git status\n\nthey'll see a different branch name in the status output which is \nconfusing. So I think we either need to improve the status output to \nshow all the branches that are being rewritten (which to my mind is the \nbetter option, it is more work but shouldn't be too difficult as it \nalready parses \"rebase-merge/git-rebase-todo\" and \"rebase-merge/done\"), \nor  say something like\n\n     cannot delete branch 'feature' because it is being updated by a\n     rebase running in '../feature' which is updating multiple branches.\n\nfor the update-refs case.\n\nThanks for working on this, it is a nice usability improvement.\n\nPhillip\n\n\n\n> Shortening a quite long expression down to digestable pieces is left\n> as an exercise for those with this particular itch to scratch ;-).\n> I do not personally mind if it ends up indistinguishable from other\n> \"rebase\" case (or unified the \"kind\" enum into one), but others may\n> have ideas to shorten the message to fit in the pattern we see\n> above.\n> \n> Thanks.\n\n"},{"id":"548940","messageId":"590382fb-731b-4e14-911e-ff68356d1082@web.de","threadId":"66032","inReplyTo":"9865fc6b-e3fe-4614-9ffe-71af776e1796@web.de","subject":"[PATCH v2] branch: report active bisect run when rejecting delete","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2026-07-25T10:41:07Z","receivedAt":"2026-07-25T10:41:15Z","isPatch":true,"body":"git branch refuses to delete branches that are currently checked out\nwith a message like this: \"error: cannot delete branch 'foo' used by\nworktree at '/path/of/worktree'\".  This can be confusing if it's an\ninternal checkout for git bisect.  Report a more specific error in\nthat case to help users that might have forgotten their bisect run.\n\nSuggested-by: stsp <stsp2@yandex.ru>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\nChanges since v2:\n- Only report bisect runs as specific rejection reason for now.\n- Leave rebasing unaddressed because I don't see how to formulate\n  clear and readable messages for those scenarios, yet.\n- Collect all checkout reasons of all branches in a simple array for\n  easy use, e.g. to eventually address rebases or for git status.\n- Rebased onto the landed test_grep conversion.\n\n branch.c          | 80 +++++++++++++++++++++++++++++++++--------------\n branch.h          |  6 ++++\n builtin/branch.c  |  7 +++++\n t/t3200-branch.sh |  4 +--\n 4 files changed, 72 insertions(+), 25 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 243db7d0fc..a9fc790818 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -385,6 +385,39 @@ int validate_branchname(const char *name, struct strbuf *ref)\n static int initialized_checked_out_branches;\n static struct strmap current_checked_out_branches = STRMAP_INIT;\n \n+enum branch_checkout_kind {\n+\tBRANCH_CHECKOUT_KIND_CHECKOUT,\n+\tBRANCH_CHECKOUT_KIND_REBASE,\n+\tBRANCH_CHECKOUT_KIND_BISECT,\n+\tBRANCH_CHECKOUT_KIND_UPDATE_REF,\n+};\n+\n+struct checked_out_branch {\n+\tchar *refname;\n+\tchar *path;\n+\tenum branch_checkout_kind kind;\n+};\n+\n+static struct checked_out_branch *checked_out_branches;\n+static size_t checked_out_branches_alloc, checked_out_branches_nr;\n+\n+static void register_checked_out_branch(const char *prefix, const char *name,\n+\t\t\t\t\tconst char *path,\n+\t\t\t\t\tenum branch_checkout_kind kind)\n+{\n+\tchar *refname = xstrfmt(\"%s%s\", prefix, name);\n+\tchar *path_copy = xstrdup(path);\n+\n+\tALLOC_GROW(checked_out_branches, checked_out_branches_nr + 1,\n+\t\t   checked_out_branches_alloc);\n+\tchecked_out_branches[checked_out_branches_nr].refname = refname;\n+\tchecked_out_branches[checked_out_branches_nr].path = path_copy;\n+\tchecked_out_branches[checked_out_branches_nr].kind = kind;\n+\tchecked_out_branches_nr++;\n+\n+\tstrmap_put(&current_checked_out_branches, refname, path_copy);\n+}\n+\n static void prepare_checked_out_branches(void)\n {\n \tint i = 0;\n@@ -397,7 +430,7 @@ static void prepare_checked_out_branches(void)\n \tworktrees = get_worktrees();\n \n \twhile (worktrees[i]) {\n-\t\tchar *old, *wt_gitdir;\n+\t\tchar *wt_gitdir;\n \t\tstruct wt_status_state state = { 0 };\n \t\tstruct worktree *wt = worktrees[i++];\n \t\tstruct string_list update_refs = STRING_LIST_INIT_DUP;\n@@ -406,34 +439,25 @@ static void prepare_checked_out_branches(void)\n \t\t\tcontinue;\n \n \t\tif (wt->head_ref) {\n-\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t wt->head_ref,\n-\t\t\t\t\t xstrdup(wt->path));\n-\t\t\tfree(old);\n+\t\t\tregister_checked_out_branch(\"\", wt->head_ref, wt->path,\n+\t\t\t\t\t\t    BRANCH_CHECKOUT_KIND_CHECKOUT);\n \t\t}\n \n \t\tif (wt_status_check_rebase(wt, &state) &&\n \t\t    (state.rebase_in_progress || state.rebase_interactive_in_progress) &&\n \t\t    state.branch) {\n-\t\t\tstruct strbuf ref = STRBUF_INIT;\n-\t\t\tstrbuf_addf(&ref, \"refs/heads/%s\", state.branch);\n-\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t ref.buf,\n-\t\t\t\t\t xstrdup(wt->path));\n-\t\t\tfree(old);\n-\t\t\tstrbuf_release(&ref);\n+\t\t\tregister_checked_out_branch(\"refs/heads/\", state.branch,\n+\t\t\t\t\t\t    wt->path,\n+\t\t\t\t\t\t    BRANCH_CHECKOUT_KIND_REBASE);\n \t\t}\n \t\twt_status_state_free_buffers(&state);\n \n \t\tif (wt_status_check_bisect(wt, &state) &&\n \t\t    state.bisecting_from) {\n-\t\t\tstruct strbuf ref = STRBUF_INIT;\n-\t\t\tstrbuf_addf(&ref, \"refs/heads/%s\", state.bisecting_from);\n-\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t ref.buf,\n-\t\t\t\t\t xstrdup(wt->path));\n-\t\t\tfree(old);\n-\t\t\tstrbuf_release(&ref);\n+\t\t\tregister_checked_out_branch(\"refs/heads/\",\n+\t\t\t\t\t\t    state.bisecting_from,\n+\t\t\t\t\t\t    wt->path,\n+\t\t\t\t\t\t    BRANCH_CHECKOUT_KIND_BISECT);\n \t\t}\n \t\twt_status_state_free_buffers(&state);\n \n@@ -442,10 +466,9 @@ static void prepare_checked_out_branches(void)\n \t\t\t\t\t\t     &update_refs)) {\n \t\t\tstruct string_list_item *item;\n \t\t\tfor_each_string_list_item(item, &update_refs) {\n-\t\t\t\told = strmap_put(&current_checked_out_branches,\n-\t\t\t\t\t\t item->string,\n-\t\t\t\t\t\t xstrdup(wt->path));\n-\t\t\t\tfree(old);\n+\t\t\t\tregister_checked_out_branch(\"\", item->string,\n+\t\t\t\t\t\t\t    wt->path,\n+\t\t\t\t\t\t\t    BRANCH_CHECKOUT_KIND_UPDATE_REF);\n \t\t\t}\n \t\t\tstring_list_clear(&update_refs, 1);\n \t\t}\n@@ -462,6 +485,17 @@ const char *branch_checked_out(const char *refname)\n \treturn strmap_get(&current_checked_out_branches, refname);\n }\n \n+const char *branch_bisecting(const char *refname)\n+{\n+\tprepare_checked_out_branches();\n+\tfor (size_t i = 0; i < checked_out_branches_nr; i++) {\n+\t\tif (!strcmp(refname, checked_out_branches[i].refname) &&\n+\t\t    checked_out_branches[i].kind == BRANCH_CHECKOUT_KIND_BISECT)\n+\t\t\treturn checked_out_branches[i].path;\n+\t}\n+\treturn NULL;\n+}\n+\n /*\n  * Check if a branch 'name' can be created as a new branch; die otherwise.\n  * 'force' can be used when it is OK for the named branch already exists.\ndiff --git a/branch.h b/branch.h\nindex 3dc6e2a0ff..e9b1f7b37d 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -106,6 +106,12 @@ void create_branches_recursively(struct repository *r, const char *name,\n  */\n const char *branch_checked_out(const char *refname);\n \n+/*\n+ * If the branch at 'refname' is currently used for bisecting in a\n+ * worktree, then return the path to that worktree.\n+ */\n+const char *branch_bisecting(const char *refname);\n+\n /*\n  * Check if 'name' can be a valid name for a branch; die otherwise.\n  * Return 1 if the named branch already exists; return 0 otherwise.\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex dede60d27b..29e4ec6c67 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -265,6 +265,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n \n \t\tif (kinds == FILTER_REFS_BRANCHES) {\n \t\t\tconst char *path;\n+\t\t\tif ((path = branch_bisecting(name))) {\n+\t\t\t\terror(_(\"cannot delete branch '%s' \"\n+\t\t\t\t\t\"used by worktree at '%s' for bisect\"),\n+\t\t\t\t\t      bname.buf, path);\n+\t\t\t\tret = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif ((path = branch_checked_out(name))) {\n \t\t\t\terror(_(\"cannot delete branch '%s' \"\n \t\t\t\t\t\"used by worktree at '%s'\"),\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 1ecbafbee1..051434d9c6 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -930,7 +930,7 @@ test_expect_success 'deleting currently checked out branch fails' '\n \tgit worktree add -b my7 my7 &&\n \ttest_must_fail git -C my7 branch -d my7 &&\n \ttest_must_fail git branch -d my7 2>actual &&\n-\ttest_grep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n+\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*'\\$\"'\" actual &&\n \trm -r my7 &&\n \tgit worktree prune\n '\n@@ -941,7 +941,7 @@ test_expect_success 'deleting in-use branch fails' '\n \tgit -C my7 bisect start HEAD HEAD~2 &&\n \ttest_must_fail git -C my7 branch -d my7 &&\n \ttest_must_fail git branch -d my7 2>actual &&\n-\ttest_grep \"^error: cannot delete branch .my7. used by worktree at \" actual &&\n+\ttest_grep \"^error: cannot delete branch '\"'\"'my7'\"'\"' used by worktree at '\"'.*' for bisect\\$\"'\" actual &&\n \trm -r my7 &&\n \tgit worktree prune\n '\n-- \n2.55.0\n"},{"id":"549016","messageId":"xmqqbjbtyd80.fsf@gitster.g","threadId":"66032","inReplyTo":"590382fb-731b-4e14-911e-ff68356d1082@web.de","subject":"Re: [PATCH v2] branch: report active bisect run when rejecting delete","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-26T15:36:47Z","receivedAt":"2026-07-26T15:36:51Z","isPatch":true,"body":"René Scharfe <l.s.r@web.de> writes:\n\n> git branch refuses to delete branches that are currently checked out\n> with a message like this: \"error: cannot delete branch 'foo' used by\n> worktree at '/path/of/worktree'\".  This can be confusing if it's an\n> internal checkout for git bisect.  Report a more specific error in\n> that case to help users that might have forgotten their bisect run.\n>\n> Suggested-by: stsp <stsp2@yandex.ru>\n> Signed-off-by: René Scharfe <l.s.r@web.de>\n> ---\n> Changes since v2:\n> - Only report bisect runs as specific rejection reason for now.\n> - Leave rebasing unaddressed because I don't see how to formulate\n>   clear and readable messages for those scenarios, yet.\n> - Collect all checkout reasons of all branches in a simple array for\n>   easy use, e.g. to eventually address rebases or for git status.\n> - Rebased onto the landed test_grep conversion.\n\nThe scope of this patch has shrunk a bit.  While the 'in use by a\nrebase' state is still recognized internally, unlike in the previous\nround, we guard users only against the 'in use by a bisect' state and\nthe 'in use by being checked out' state.\n\nThis is probably a good single step, so unless there are objections,\nlet's mark the topic for 'next'.\n\nMy understanding of what is stopping us from taking the next step is\nthat we do not know the exact phrasing to express the 'in use by a\nrebase' state concisely.\n\nThanks.\n"},{"id":"549126","messageId":"871pcndynd.fsf@emacs.iotcl.com","threadId":"66032","inReplyTo":"xmqqbjbtyd80.fsf@gitster.g","subject":"Re: [PATCH v2] branch: report active bisect run when rejecting delete","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2026-07-28T13:36:22Z","receivedAt":"2026-07-28T13:36:41Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> René Scharfe <l.s.r@web.de> writes:\n>\n>> git branch refuses to delete branches that are currently checked out\n>> with a message like this: \"error: cannot delete branch 'foo' used by\n>> worktree at '/path/of/worktree'\".  This can be confusing if it's an\n>> internal checkout for git bisect.  Report a more specific error in\n>> that case to help users that might have forgotten their bisect run.\n>>\n>> Suggested-by: stsp <stsp2@yandex.ru>\n>> Signed-off-by: René Scharfe <l.s.r@web.de>\n>> ---\n>> Changes since v2:\n>> - Only report bisect runs as specific rejection reason for now.\n\nWorks for me.\n\n>> - Leave rebasing unaddressed because I don't see how to formulate\n>>   clear and readable messages for those scenarios, yet.\n>> - Collect all checkout reasons of all branches in a simple array for\n>>   easy use, e.g. to eventually address rebases or for git status.\n\nThat's handy for future use.\n\n>> - Rebased onto the landed test_grep conversion.\n>\n> The scope of this patch has shrunk a bit.  While the 'in use by a\n> rebase' state is still recognized internally, unlike in the previous\n> round, we guard users only against the 'in use by a bisect' state and\n> the 'in use by being checked out' state.\n>\n> This is probably a good single step, so unless there are objections,\n> let's mark the topic for 'next'.\n\nYeah, I agree it's a step in the right direction.\n\nI've reviewed the patch and looks good to me.\n\nTo answer your other question:\n\n> This reminds me of another recent discussion on rewriting a branch\n> that is checked out elsewhere, where the \"git history\" command\n> forgot to apply the same safety check:\n> \n> https://lore.kernel.org/git/e7dbcede-4486-459c-aa64-e44690e01fe0@gmail.com/\n> \n> We definitely need an easy-to-use API to determine consistently\n> which branches are in use, and to teach all commands that repoint\n> branch tips to use it to offer the same safety to users.  The\n> framework that this patch introduces might be a good starting point\n> for that effort.\n\nYeah, there is definetly some overlap here. As far as I can tell this is\na good step in the direction of an API the git-history(1) can use.\n\n-- \nCheers,\nToon\n"},{"id":"549213","messageId":"3bdca3f4-affa-4443-afb6-fd7ca20cf012@gmail.com","threadId":"66032","inReplyTo":"xmqqbjbtyd80.fsf@gitster.g","subject":"Re: [PATCH v2] branch: report active bisect run when rejecting delete","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-07-29T15:22:22Z","receivedAt":"2026-07-29T15:22:26Z","isPatch":true,"body":"On 26/07/2026 16:36, Junio C Hamano wrote:\n> René Scharfe <l.s.r@web.de> writes:\n> \n>> git branch refuses to delete branches that are currently checked out\n>> with a message like this: \"error: cannot delete branch 'foo' used by\n>> worktree at '/path/of/worktree'\".  This can be confusing if it's an\n>> internal checkout for git bisect.  Report a more specific error in\n>> that case to help users that might have forgotten their bisect run.\n>>\n>> Suggested-by: stsp <stsp2@yandex.ru>\n>> Signed-off-by: René Scharfe <l.s.r@web.de>\n>> ---\n>> Changes since v2:\n>> - Only report bisect runs as specific rejection reason for now.\n>> - Leave rebasing unaddressed because I don't see how to formulate\n>>    clear and readable messages for those scenarios, yet.\n>> - Collect all checkout reasons of all branches in a simple array for\n>>    easy use, e.g. to eventually address rebases or for git status.\n>> - Rebased onto the landed test_grep conversion.\n> \n> The scope of this patch has shrunk a bit.  While the 'in use by a\n> rebase' state is still recognized internally, unlike in the previous\n> round, we guard users only against the 'in use by a bisect' state and\n> the 'in use by being checked out' state.\n> \n> This is probably a good single step, so unless there are objections,\n> let's mark the topic for 'next'.\n\nI agree this is a useful improvement as it is.\n\nThanks\n\nPhillip\n\n> My understanding of what is stopping us from taking the next step is\n> that we do not know the exact phrasing to express the 'in use by a\n> rebase' state concisely.\n> \n> Thanks.\n> \n\n"}]}