Adjust worktree name display in agent panel and git picker (#53351)

Danilo Leal created

This PR adjust the name of the worktree within the agent panel: we
display `main` if you're in the main worktree and then the worktree name
when in a linked one. Additionally, I also took the opportunity to fix
something that confused me in the general worktree picker: for the list
item that represented the main worktree, we were using the current
checked out branch name for that item's name. I think that's a bit
incorrect, given the _name_ of that worktree isn't the name of the
branch you have active at that moment. For now, I'm using simply `main`
as the worktree name. Lastly, also added a little blue check icon in the
worktree _and branch_ picker to highlight which worktree/branch is
currently active.

| main worktree | linked worktree | main worktree in picker |
|--------|--------|--------|
| <img width="802" height="338" alt="Screenshot 2026-04-07 at 10  32
2@2x"
src="https://github.com/user-attachments/assets/aad993e6-adc3-4c8d-b01a-abb2597182e7"
/> | <img width="804" height="332" alt="Screenshot 2026-04-07 at 10 
32@2x"
src="https://github.com/user-attachments/assets/e59c5b86-8cee-476c-ab7b-32ae87249c1a"
/> | <img width="804" height="856" alt="Screenshot 2026-04-07 at 10 
36@2x"
src="https://github.com/user-attachments/assets/282d89a8-570e-4e62-b703-853bed9b21ff"
/> |

Release Notes:

- Git: Fixed display of the main worktree name in the worktree picker.

Change summary

crates/agent_ui/src/agent_panel.rs   | 14 ++++++
crates/git_ui/src/branch_picker.rs   | 12 +++++-
crates/git_ui/src/worktree_picker.rs | 60 ++++++++++++++++++++++++------
3 files changed, 71 insertions(+), 15 deletions(-)

Detailed changes

crates/agent_ui/src/agent_panel.rs 🔗

@@ -62,7 +62,7 @@ use gpui::{
 use language::LanguageRegistry;
 use language_model::LanguageModelRegistry;
 use project::project_settings::ProjectSettings;
-use project::{Project, ProjectPath, Worktree};
+use project::{Project, ProjectPath, Worktree, linked_worktree_short_name};
 use prompt_store::{PromptStore, UserPromptId};
 use rules_library::{RulesLibrary, open_rules_library};
 use settings::TerminalDockPosition;
@@ -641,6 +641,18 @@ impl StartThreadIn {
                         }
                     }
 
+                    if let Some(name) = linked_worktree_short_name(
+                        repo.original_repo_abs_path.as_ref(),
+                        repo.work_directory_abs_path.as_ref(),
+                    ) {
+                        if visible_paths
+                            .iter()
+                            .any(|p| p.as_path() == repo.work_directory_abs_path.as_ref())
+                        {
+                            return Some(SharedString::from(format!("({})", name)));
+                        }
+                    }
+
                     if visible_paths
                         .iter()
                         .any(|p| p.as_path() == work_dir.as_ref())

crates/git_ui/src/branch_picker.rs 🔗

@@ -885,12 +885,16 @@ impl PickerDelegate for BranchListDelegate {
             })
             .unwrap_or_else(|| (None, None, None));
 
+        let is_head_branch = entry.as_branch().is_some_and(|branch| branch.is_head);
+
         let entry_icon = match entry {
             Entry::NewUrl { .. } | Entry::NewBranch { .. } | Entry::NewRemoteName { .. } => {
                 IconName::Plus
             }
             Entry::Branch { branch, .. } => {
-                if branch.is_remote() {
+                if is_head_branch {
+                    IconName::Check
+                } else if branch.is_remote() {
                     IconName::Screen
                 } else {
                     IconName::GitBranchAlt
@@ -975,7 +979,11 @@ impl PickerDelegate for BranchListDelegate {
                         .flex_grow()
                         .child(
                             Icon::new(entry_icon)
-                                .color(Color::Muted)
+                                .color(if is_head_branch {
+                                    Color::Accent
+                                } else {
+                                    Color::Muted
+                                })
                                 .size(IconSize::Small),
                         )
                         .child(

crates/git_ui/src/worktree_picker.rs 🔗

@@ -19,13 +19,15 @@ use remote_connection::{RemoteConnectionModal, connect};
 use settings::Settings;
 use std::{path::PathBuf, sync::Arc};
 use ui::{HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, Tooltip, prelude::*};
-use util::{ResultExt, debug_panic};
+use util::{ResultExt, debug_panic, paths::PathExt};
 use workspace::{
     ModalView, MultiWorkspace, OpenMode, Workspace, notifications::DetachAndPromptErr,
 };
 
 use crate::git_panel::show_error_toast;
 
+const MAIN_WORKTREE_DISPLAY_NAME: &str = "main";
+
 actions!(
     git,
     [
@@ -273,6 +275,7 @@ pub struct WorktreeListDelegate {
     focus_handle: FocusHandle,
     default_branch: Option<SharedString>,
     forbidden_deletion_path: Option<PathBuf>,
+    current_worktree_path: Option<PathBuf>,
 }
 
 impl WorktreeListDelegate {
@@ -282,6 +285,10 @@ impl WorktreeListDelegate {
         _window: &mut Window,
         cx: &mut Context<WorktreeList>,
     ) -> Self {
+        let current_worktree_path = repo
+            .as_ref()
+            .map(|r| r.read(cx).work_directory_abs_path.to_path_buf());
+
         Self {
             matches: vec![],
             all_worktrees: None,
@@ -293,6 +300,7 @@ impl WorktreeListDelegate {
             focus_handle: cx.focus_handle(),
             default_branch: None,
             forbidden_deletion_path: None,
+            current_worktree_path,
         }
     }
 
@@ -699,7 +707,14 @@ impl PickerDelegate for WorktreeListDelegate {
                 let candidates = all_worktrees
                     .iter()
                     .enumerate()
-                    .map(|(ix, worktree)| StringMatchCandidate::new(ix, worktree.display_name()))
+                    .map(|(ix, worktree)| {
+                        let name = if worktree.is_main {
+                            MAIN_WORKTREE_DISPLAY_NAME
+                        } else {
+                            worktree.display_name()
+                        };
+                        StringMatchCandidate::new(ix, name)
+                    })
                     .collect::<Vec<StringMatchCandidate>>();
                 fuzzy::match_strings(
                     &candidates,
@@ -722,9 +737,14 @@ impl PickerDelegate for WorktreeListDelegate {
             picker
                 .update(cx, |picker, _| {
                     if !query.is_empty()
-                        && !matches
-                            .first()
-                            .is_some_and(|entry| entry.worktree.display_name() == query)
+                        && !matches.first().is_some_and(|entry| {
+                            let name = if entry.worktree.is_main {
+                                MAIN_WORKTREE_DISPLAY_NAME
+                            } else {
+                                entry.worktree.display_name()
+                            };
+                            name == query
+                        })
                     {
                         let query = query.replace(' ', "-");
                         matches.push(WorktreeEntry {
@@ -777,7 +797,7 @@ impl PickerDelegate for WorktreeListDelegate {
         cx: &mut Context<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let entry = &self.matches.get(ix)?;
-        let path = entry.worktree.path.to_string_lossy().to_string();
+        let path = entry.worktree.path.compact().to_string_lossy().to_string();
         let sha = entry
             .worktree
             .sha
@@ -800,17 +820,21 @@ impl PickerDelegate for WorktreeListDelegate {
                 ),
             )
         } else {
-            let branch = entry.worktree.display_name();
-            let branch_first_line = branch.lines().next().unwrap_or(branch);
+            let display_name = if entry.worktree.is_main {
+                MAIN_WORKTREE_DISPLAY_NAME
+            } else {
+                entry.worktree.display_name()
+            };
+            let first_line = display_name.lines().next().unwrap_or(display_name);
             let positions: Vec<_> = entry
                 .positions
                 .iter()
                 .copied()
-                .filter(|&pos| pos < branch_first_line.len())
+                .filter(|&pos| pos < first_line.len())
                 .collect();
 
             (
-                HighlightedLabel::new(branch_first_line.to_owned(), positions)
+                HighlightedLabel::new(first_line.to_owned(), positions)
                     .truncate()
                     .into_any_element(),
                 path,
@@ -832,8 +856,16 @@ impl PickerDelegate for WorktreeListDelegate {
                 }))
         };
 
+        let is_current = !entry.is_new
+            && self
+                .current_worktree_path
+                .as_ref()
+                .is_some_and(|current| *current == entry.worktree.path);
+
         let entry_icon = if entry.is_new {
             IconName::Plus
+        } else if is_current {
+            IconName::Check
         } else {
             IconName::GitWorktree
         };
@@ -849,7 +881,11 @@ impl PickerDelegate for WorktreeListDelegate {
                         .gap_2p5()
                         .child(
                             Icon::new(entry_icon)
-                                .color(Color::Muted)
+                                .color(if is_current {
+                                    Color::Accent
+                                } else {
+                                    Color::Muted
+                                })
                                 .size(IconSize::Small),
                         )
                         .child(v_flex().w_full().child(branch_name).map(|this| {
@@ -879,7 +915,7 @@ impl PickerDelegate for WorktreeListDelegate {
                                         )
                                         .child(
                                             Label::new(sublabel)
-                                                .truncate()
+                                                .truncate_start()
                                                 .color(Color::Muted)
                                                 .size(LabelSize::Small)
                                                 .flex_1(),