From 6ad56d5852aadb4d4b6a07ededcc1be9138bdbc9 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Wed, 4 Mar 2026 13:45:05 +0100 Subject: [PATCH] Show worktree name next to branch in branches list When a branch is checked out by another worktree, show the worktree name in the label, e.g. "(worktree cosmos2)" instead of just "(worktree)", so you can immediately see which worktree holds it. --- pkg/gui/presentation/branches.go | 36 +++++++++++++++---- pkg/gui/presentation/branches_test.go | 8 ++--- ..._branches_checked_out_in_other_worktree.go | 4 +-- .../tests/worktree/add_from_branch.go | 4 +-- .../worktree/add_from_branch_detached.go | 2 +- .../tests/worktree/add_from_commit.go | 2 +- .../tests/worktree/associate_branch_bisect.go | 4 +-- .../tests/worktree/associate_branch_rebase.go | 6 ++-- .../worktree/detach_worktree_from_branch.go | 2 +- .../worktree/fast_forward_worktree_branch.go | 8 ++--- ...nch_should_not_pollute_current_worktree.go | 8 ++--- .../worktree/remove_worktree_from_branch.go | 2 +- 12 files changed, 55 insertions(+), 31 deletions(-) diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go index cdf0af0c3..7a32265a2 100644 --- a/pkg/gui/presentation/branches.go +++ b/pkg/gui/presentation/branches.go @@ -57,7 +57,6 @@ func getBranchDisplayStrings( showCommitHash := fullDescription || userConfig.Gui.ShowBranchCommitHash branchStatus := BranchStatus(b, itemOperation, tr, now, userConfig) divergence := divergenceStr(b, itemOperation, tr, userConfig) - worktreeIcon := lo.Ternary(icons.IsIconEnabled(), icons.LINKED_WORKTREE_ICON, fmt.Sprintf("(%s)", tr.LcWorktree)) // Recency is always three characters, plus one for the space availableWidth := viewWidth - 4 @@ -72,17 +71,42 @@ func getBranchDisplayStrings( } paddingNeededForDivergence := availableWidth - if checkedOutByWorkTree { - availableWidth -= utils.StringWidth(worktreeIcon) + 1 + displayName := b.Name + if b.DisplayName != "" { + displayName = b.DisplayName } if len(branchStatus) > 0 { availableWidth -= utils.StringWidth(utils.Decolorise(branchStatus)) + 1 } - displayName := b.Name - if b.DisplayName != "" { - displayName = b.DisplayName + worktreeIcon := "" + if checkedOutByWorkTree { + if wt, ok := git_commands.WorktreeForBranch(b, worktrees); ok && wt.Name != b.Name { + if icons.IsIconEnabled() { + worktreeIcon = fmt.Sprintf("(%s %s)", icons.LINKED_WORKTREE_ICON, wt.Name) + } else { + worktreeIcon = fmt.Sprintf("(%s %s)", tr.LcWorktree, wt.Name) + } + + // If the worktree name doesn't fit in the available width, omit it + remaining := availableWidth - utils.StringWidth(worktreeIcon) - 1 + if remaining < utils.StringWidth(displayName) { + if icons.IsIconEnabled() { + worktreeIcon = icons.LINKED_WORKTREE_ICON + } else { + worktreeIcon = fmt.Sprintf("(%s)", tr.LcWorktree) + } + } + } else { + if icons.IsIconEnabled() { + worktreeIcon = icons.LINKED_WORKTREE_ICON + } else { + worktreeIcon = fmt.Sprintf("(%s)", tr.LcWorktree) + } + } + + availableWidth -= utils.StringWidth(worktreeIcon) + 1 } nameTextStyle := GetBranchTextStyle(b.Name) diff --git a/pkg/gui/presentation/branches_test.go b/pkg/gui/presentation/branches_test.go index a73c7aa56..8c646e7d7 100644 --- a/pkg/gui/presentation/branches_test.go +++ b/pkg/gui/presentation/branches_test.go @@ -62,7 +62,7 @@ func Test_getBranchDisplayStrings(t *testing.T) { useIcons: false, checkedOutByWorktree: true, showDivergenceCfg: "none", - expected: []string{"1m", "branch_name (worktree)"}, + expected: []string{"1m", "branch_name (worktree other-worktree)"}, }, { branch: &models.Branch{Name: "branch_name", Recency: "1m"}, @@ -72,7 +72,7 @@ func Test_getBranchDisplayStrings(t *testing.T) { useIcons: true, checkedOutByWorktree: true, showDivergenceCfg: "none", - expected: []string{"1m", "󰘬", "branch_name 󰌹"}, + expected: []string{"1m", "󰘬", "branch_name (󰌹 other-worktree)"}, }, { branch: &models.Branch{ @@ -104,7 +104,7 @@ func Test_getBranchDisplayStrings(t *testing.T) { useIcons: false, checkedOutByWorktree: true, showDivergenceCfg: "none", - expected: []string{"1m", "branch_name (worktree) ↓5↑3"}, + expected: []string{"1m", "branch_name (worktree other-worktree) ↓5↑3"}, }, { branch: &models.Branch{ @@ -347,7 +347,7 @@ func Test_getBranchDisplayStrings(t *testing.T) { worktrees := []*models.Worktree{} if s.checkedOutByWorktree { - worktrees = append(worktrees, &models.Worktree{Branch: s.branch.Name}) + worktrees = append(worktrees, &models.Worktree{Branch: s.branch.Name, Name: "other-worktree"}) } t.Run(fmt.Sprintf("getBranchDisplayStrings_%d", i), func(t *testing.T) { diff --git a/pkg/integration/tests/sync/fetch_and_auto_forward_branches_all_branches_checked_out_in_other_worktree.go b/pkg/integration/tests/sync/fetch_and_auto_forward_branches_all_branches_checked_out_in_other_worktree.go index bc1ecd294..428c85e07 100644 --- a/pkg/integration/tests/sync/fetch_and_auto_forward_branches_all_branches_checked_out_in_other_worktree.go +++ b/pkg/integration/tests/sync/fetch_and_auto_forward_branches_all_branches_checked_out_in_other_worktree.go @@ -38,7 +38,7 @@ var FetchAndAutoForwardBranchesAllBranchesCheckedOutInOtherWorktree = NewIntegra Contains("checked-out").IsSelected(), Contains("diverged ↓2↑1"), Contains("feature ↓2").DoesNotContain("↑"), - Contains("master (worktree) ↓1").DoesNotContain("↑"), + Contains("master (worktree linked-worktree) ↓1").DoesNotContain("↑"), ) t.Views().Files(). @@ -51,7 +51,7 @@ var FetchAndAutoForwardBranchesAllBranchesCheckedOutInOtherWorktree = NewIntegra Contains("checked-out").IsSelected(), Contains("diverged ↓2↑1"), Contains("feature ✓"), - Contains("master (worktree) ↓1"), + Contains("master (worktree linked-worktree) ↓1"), ) }, }) diff --git a/pkg/integration/tests/worktree/add_from_branch.go b/pkg/integration/tests/worktree/add_from_branch.go index 70aa4dd60..ab7807571 100644 --- a/pkg/integration/tests/worktree/add_from_branch.go +++ b/pkg/integration/tests/worktree/add_from_branch.go @@ -42,7 +42,7 @@ var AddFromBranch = NewIntegrationTest(NewIntegrationTestArgs{ IsFocused(). Lines( Contains("newbranch").IsSelected(), - Contains("mybranch (worktree)"), + Contains("mybranch (worktree repo)"), ). NavigateToLine(Contains("mybranch")). Press(keys.Universal.Select). @@ -54,7 +54,7 @@ var AddFromBranch = NewIntegrationTest(NewIntegrationTestArgs{ }). Lines( Contains("mybranch").IsSelected(), - Contains("newbranch (worktree)"), + Contains("newbranch (worktree linked-worktree)"), ). // Confirm the files view is still showing in the files window Press(keys.Universal.PrevBlock) diff --git a/pkg/integration/tests/worktree/add_from_branch_detached.go b/pkg/integration/tests/worktree/add_from_branch_detached.go index 584de344e..70f27dc81 100644 --- a/pkg/integration/tests/worktree/add_from_branch_detached.go +++ b/pkg/integration/tests/worktree/add_from_branch_detached.go @@ -37,7 +37,7 @@ var AddFromBranchDetached = NewIntegrationTest(NewIntegrationTestArgs{ IsFocused(). Lines( Contains("(no branch)").IsSelected(), - Contains("mybranch (worktree)"), + Contains("mybranch (worktree repo)"), ) t.Views().Status(). diff --git a/pkg/integration/tests/worktree/add_from_commit.go b/pkg/integration/tests/worktree/add_from_commit.go index a171f74a3..49b69697b 100644 --- a/pkg/integration/tests/worktree/add_from_commit.go +++ b/pkg/integration/tests/worktree/add_from_commit.go @@ -50,7 +50,7 @@ var AddFromCommit = NewIntegrationTest(NewIntegrationTestArgs{ IsFocused(). Lines( Contains("newbranch").IsSelected(), - Contains("mybranch (worktree)"), + Contains("mybranch (worktree repo)"), ) }, }) diff --git a/pkg/integration/tests/worktree/associate_branch_bisect.go b/pkg/integration/tests/worktree/associate_branch_bisect.go index 77d46e176..13cc290e7 100644 --- a/pkg/integration/tests/worktree/associate_branch_bisect.go +++ b/pkg/integration/tests/worktree/associate_branch_bisect.go @@ -30,7 +30,7 @@ var AssociateBranchBisect = NewIntegrationTest(NewIntegrationTestArgs{ Focus(). Lines( Contains("mybranch").IsSelected(), - Contains("newbranch (worktree)"), + Contains("newbranch (worktree linked-worktree)"), ) // start a bisect on the main worktree @@ -70,7 +70,7 @@ var AssociateBranchBisect = NewIntegrationTest(NewIntegrationTestArgs{ }). Lines( Contains("newbranch").IsSelected(), - Contains("mybranch (worktree)"), + Contains("mybranch (worktree repo)"), ) // switch back to main worktree diff --git a/pkg/integration/tests/worktree/associate_branch_rebase.go b/pkg/integration/tests/worktree/associate_branch_rebase.go index b0b04b805..ebd18f1f0 100644 --- a/pkg/integration/tests/worktree/associate_branch_rebase.go +++ b/pkg/integration/tests/worktree/associate_branch_rebase.go @@ -31,7 +31,7 @@ var AssociateBranchRebase = NewIntegrationTest(NewIntegrationTestArgs{ Focus(). Lines( Contains("mybranch").IsSelected(), - Contains("newbranch (worktree)"), + Contains("newbranch (worktree linked-worktree)"), ) // start a rebase on the main worktree @@ -57,7 +57,7 @@ var AssociateBranchRebase = NewIntegrationTest(NewIntegrationTestArgs{ }). Lines( Contains("newbranch").IsSelected(), - Contains("mybranch (worktree)"), + Contains("mybranch (worktree repo)"), ) // start a rebase on the linked worktree @@ -83,7 +83,7 @@ var AssociateBranchRebase = NewIntegrationTest(NewIntegrationTestArgs{ Contains("(no branch").IsSelected(), Contains("mybranch"), // even though the linked worktree is rebasing, we still associate it with the branch - Contains("newbranch (worktree)"), + Contains("newbranch (worktree linked-worktree)"), ) }, }) diff --git a/pkg/integration/tests/worktree/detach_worktree_from_branch.go b/pkg/integration/tests/worktree/detach_worktree_from_branch.go index 7b9430aa6..acd40e6ad 100644 --- a/pkg/integration/tests/worktree/detach_worktree_from_branch.go +++ b/pkg/integration/tests/worktree/detach_worktree_from_branch.go @@ -23,7 +23,7 @@ var DetachWorktreeFromBranch = NewIntegrationTest(NewIntegrationTestArgs{ Focus(). Lines( Contains("mybranch").IsSelected(), - Contains("newbranch (worktree)"), + Contains("newbranch (worktree linked-worktree)"), ). NavigateToLine(Contains("newbranch")). Press(keys.Universal.Remove). diff --git a/pkg/integration/tests/worktree/fast_forward_worktree_branch.go b/pkg/integration/tests/worktree/fast_forward_worktree_branch.go index 6c382a867..b3c18ac86 100644 --- a/pkg/integration/tests/worktree/fast_forward_worktree_branch.go +++ b/pkg/integration/tests/worktree/fast_forward_worktree_branch.go @@ -35,18 +35,18 @@ var FastForwardWorktreeBranch = NewIntegrationTest(NewIntegrationTestArgs{ Focus(). Lines( Contains("mybranch").Contains("↓1").IsSelected(), - Contains("newbranch (worktree)").Contains("↓1"), + Contains("newbranch (worktree linked-worktree)").Contains("↓1"), ). Press(keys.Branches.FastForward). Lines( Contains("mybranch").Contains("✓").IsSelected(), - Contains("newbranch (worktree)").Contains("↓1"), + Contains("newbranch (worktree linked-worktree)").Contains("↓1"), ). - NavigateToLine(Contains("newbranch (worktree)")). + NavigateToLine(Contains("newbranch (worktree linked-worktree)")). Press(keys.Branches.FastForward). Lines( Contains("mybranch").Contains("✓"), - Contains("newbranch (worktree)").Contains("✓").IsSelected(), + Contains("newbranch (worktree linked-worktree)").Contains("✓").IsSelected(), ) }, }) diff --git a/pkg/integration/tests/worktree/fast_forward_worktree_branch_should_not_pollute_current_worktree.go b/pkg/integration/tests/worktree/fast_forward_worktree_branch_should_not_pollute_current_worktree.go index cbe1ed943..539eac3fb 100644 --- a/pkg/integration/tests/worktree/fast_forward_worktree_branch_should_not_pollute_current_worktree.go +++ b/pkg/integration/tests/worktree/fast_forward_worktree_branch_should_not_pollute_current_worktree.go @@ -35,18 +35,18 @@ var FastForwardWorktreeBranchShouldNotPolluteCurrentWorktree = NewIntegrationTes Focus(). Lines( Contains("mybranch").Contains("↓1").IsSelected(), - Contains("newbranch (worktree)").Contains("↓1"), + Contains("newbranch (worktree linked-worktree)").Contains("↓1"), ). Press(keys.Branches.FastForward). Lines( Contains("mybranch").Contains("✓").IsSelected(), - Contains("newbranch (worktree)").Contains("↓1"), + Contains("newbranch (worktree linked-worktree)").Contains("↓1"), ). - NavigateToLine(Contains("newbranch (worktree)")). + NavigateToLine(Contains("newbranch (worktree linked-worktree)")). Press(keys.Branches.FastForward). Lines( Contains("mybranch").Contains("✓"), - Contains("newbranch (worktree)").Contains("✓").IsSelected(), + Contains("newbranch (worktree linked-worktree)").Contains("✓").IsSelected(), ). NavigateToLine(Contains("mybranch")) diff --git a/pkg/integration/tests/worktree/remove_worktree_from_branch.go b/pkg/integration/tests/worktree/remove_worktree_from_branch.go index f7f5d01cf..1aa9645f3 100644 --- a/pkg/integration/tests/worktree/remove_worktree_from_branch.go +++ b/pkg/integration/tests/worktree/remove_worktree_from_branch.go @@ -24,7 +24,7 @@ var RemoveWorktreeFromBranch = NewIntegrationTest(NewIntegrationTestArgs{ Focus(). Lines( Contains("mybranch").IsSelected(), - Contains("newbranch (worktree)"), + Contains("newbranch (worktree linked-worktree)"), ). NavigateToLine(Contains("newbranch")). Press(keys.Universal.Remove).