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

[PATCH 1/3] checkout: provide hint when failing due to another worktree

From
GSGabriel Scherer <gabriel.scherer@inria.fr>
Date
Sep 13, 2025, 14:13 UTC
Message-ID
<20250913141327.2775228-2-gabriel.scherer@inria.fr>
In-Reply-To
<20250913141327.2775228-1-gabriel.scherer@inria.fr>
From: "Gabriel.Scherer" <gabriel.scherer@inria.fr>

When checkout/switch fails because the target branch is already used by another worktree, we now hint that the user could use --detach or --ignore-other-worktrees.

Note: this error can also happen on rebase, which unfortunately does
not support --ignore-other-worktrees. We do not show advice in this
case, and leave 'rebase --ignore-other-worktrees' to future work.
Signed-off-by: Gabriel Scherer <gabriel.scherer@inria.fr>
---
 Documentation/config/advice.adoc |  3 +++
 advice.c                         |  1 +
 advice.h                         |  1 +
 branch.c                         | 13 +++++++++++--
 branch.h                         |  4 ++++
 builtin/checkout.c               | 12 ++++++++++--
 6 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/Documentation/config/advice.adoc b/Documentation/config/advice.adoc
index 257db58918..9ee64f44ea 100644
--- a/Documentation/config/advice.adoc
+++ b/Documentation/config/advice.adoc
@@ -27,6 +27,9 @@ all advice messages.
 		Shown when a fetch refspec for multiple remotes maps to
 		the same remote-tracking branch namespace and causes branch
 		tracking set-up to fail.
+	branchUsedInOtherWorktree::
+		Shown when the user attemps to switch to a branch
+		that is already checked out in another worktree.
 	checkoutAmbiguousRemoteBranchName::
 		Shown when the argument to
 		linkgit:git-checkout[1] and linkgit:git-switch[1]
diff --git a/advice.c b/advice.c
index e5f0ff8449..5c9b763472 100644
--- a/advice.c
+++ b/advice.c
@@ -50,6 +50,7 @@ static struct {
 	[ADVICE_AMBIGUOUS_FETCH_REFSPEC]		= { "ambiguousFetchRefspec" },
 	[ADVICE_AM_WORK_DIR] 				= { "amWorkDir" },
 	[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] 	= { "checkoutAmbiguousRemoteBranchName" },
+	[ADVICE_BRANCH_USED_IN_OTHER_WORKTREE]		= { "branchUsedInOtherWorktree" },
 	[ADVICE_COMMIT_BEFORE_MERGE]			= { "commitBeforeMerge" },
 	[ADVICE_DEFAULT_BRANCH_NAME]			= { "defaultBranchName" },
 	[ADVICE_DETACHED_HEAD]				= { "detachedHead" },
diff --git a/advice.h b/advice.h
index 727dcecf4a..6b11df945b 100644
--- a/advice.h
+++ b/advice.h
@@ -16,6 +16,7 @@ enum advice_type {
 	ADVICE_ADD_IGNORED_FILE,
 	ADVICE_AMBIGUOUS_FETCH_REFSPEC,
 	ADVICE_AM_WORK_DIR,
+	ADVICE_BRANCH_USED_IN_OTHER_WORKTREE,
 	ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
 	ADVICE_COMMIT_BEFORE_MERGE,
 	ADVICE_DEFAULT_BRANCH_NAME,
diff --git a/branch.c b/branch.c
index 26be358347..76aa2cbf44 100644
--- a/branch.c
+++ b/branch.c
@@ -844,7 +844,7 @@ void remove_branch_state(struct repository *r, int verbose)
 	remove_merge_branch_state(r);
 }
 
-void die_if_checked_out(const char *branch, int ignore_current_worktree)
+int die_message_if_checked_out(const char *branch, int ignore_current_worktree)
 {
 	struct worktree **worktrees = get_worktrees();
 
@@ -854,10 +854,19 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
 
 		if (is_shared_symref(worktrees[i], "HEAD", branch)) {
 			skip_prefix(branch, "refs/heads/", &branch);
-			die(_("'%s' is already used by worktree at '%s'"),
+			return die_message(
+				_("'%s' is already used by worktree at '%s'"),
 				branch, worktrees[i]->path);
 		}
 	}
 
 	free_worktrees(worktrees);
+	return 0;
+}
+
+void die_if_checked_out(const char *branch, int ignore_current_worktree)
+{
+	int code = die_message_if_checked_out(branch, ignore_current_worktree);
+	if (code)
+		exit(code);
 }
diff --git a/branch.h b/branch.h
index ec2f35fda4..84c81508e9 100644
--- a/branch.h
+++ b/branch.h
@@ -155,4 +155,8 @@ int read_branch_desc(struct strbuf *, const char *branch_name);
  */
 void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
+/* Similar to 'die_if_checked_out' above, but returns a non-zero
+   error code after printing the message instead of dying immediately. */
+int die_message_if_checked_out(const char *branch, int ignore_current_worktree);
+
 #endif
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe..e4b78f4a05 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1582,8 +1582,16 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
 		return;
 	head_ref = refs_resolve_refdup(get_main_ref_store(the_repository),
 				       "HEAD", 0, NULL, &flags);
-	if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
-		die_if_checked_out(full_ref, 1);
+	if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref))) {
+		int code = die_message_if_checked_out(full_ref, 1);
+		if (code) {
+			advise_if_enabled(
+				ADVICE_BRANCH_USED_IN_OTHER_WORKTREE,
+				_("Use --detach to avoid this restriction,\n"
+				"or --ignore-other-worktrees to ignore it."));
+			exit(code);
+		}
+	}
 	free(head_ref);
 }
 
-- 
2.51.0
Previous: Gabriel SchererNext: Kristoffer Haugsbakk
Message 2 of 20 in “extend --ignore-other-worktrees to 'rebase', add hints”
  1. 0/3 extend --ignore-other-worktrees to 'rebase', add hintsGabriel Scherer, Sep 13, 2025
  2. 1/3 checkout: provide hint when failing due to another worktreeGabriel Scherer, Sep 13, 2025
  3. Kristoffer HaugsbakkSep 13, 2025
  4. Gabriel SchererSep 14, 2025
  5. Junio C HamanoSep 15, 2025
  6. Gabriel SchererSep 15, 2025
  7. Junio C HamanoSep 16, 2025
  8. Junio C HamanoSep 17, 2025
  9. Gabriel SchererSep 17, 2025
  10. Phillip WoodSep 19, 2025
  11. Ben KnobleSep 14, 2025
  12. Ben KnobleSep 14, 2025
  13. Kristoffer HaugsbakkSep 14, 2025
  14. 2/3 rebase: support --ignore-other-worktreesGabriel Scherer, Sep 13, 2025
  15. Phillip WoodSep 15, 2025
  16. Junio C HamanoSep 15, 2025
  17. 3/3 rebase: hint when failing on branch used by another worktreeGabriel Scherer, Sep 13, 2025
  18. Ben KnobleSep 13, 2025
  19. Gabriel SchererSep 13, 2025
  20. Phillip WoodSep 15, 2025

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.