From 30e7e8b55086a4496bfd863950eaabda2527e3e7 Mon Sep 17 00:00:00 2001
From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Date: Tue, 7 Apr 2026 23:21:17 -0300
Subject: [PATCH] Adjust worktree name display in agent panel and git picker
(#53351)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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 |
|--------|--------|--------|
|
|
|
|
Release Notes:
- Git: Fixed display of the main worktree name in the worktree picker.
---
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(-)
diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs
index f522f05fdeb93ea0a2063f03f6da8827133e8648..abba2ae69b466f176116eb933a87082a2f1c5280 100644
--- a/crates/agent_ui/src/agent_panel.rs
+++ b/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())
diff --git a/crates/git_ui/src/branch_picker.rs b/crates/git_ui/src/branch_picker.rs
index 2bf4a1991f7a302ed73fe098e8914fedd0f9eb2a..f46eb08ef9caf35b3e8fab1ce65c449f76ea2ed4 100644
--- a/crates/git_ui/src/branch_picker.rs
+++ b/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(
diff --git a/crates/git_ui/src/worktree_picker.rs b/crates/git_ui/src/worktree_picker.rs
index bd1d694fa30bb914569fbb5e6e3c67de3e3d86a0..6c95f42e99169370a08119bc22e2b71e33cb270d 100644
--- a/crates/git_ui/src/worktree_picker.rs
+++ b/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,
forbidden_deletion_path: Option,
+ current_worktree_path: Option,
}
impl WorktreeListDelegate {
@@ -282,6 +285,10 @@ impl WorktreeListDelegate {
_window: &mut Window,
cx: &mut Context,
) -> 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::>();
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>,
) -> Option {
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(),