@@ -486,6 +486,10 @@ impl BranchListDelegate {
let is_remote;
let result = match &entry {
Entry::Branch { branch, .. } => {
+ if branch.is_head {
+ return Ok(());
+ }
+
is_remote = branch.is_remote();
repo.update(cx, |repo, _| {
repo.delete_branch(is_remote, branch.name().to_string())
@@ -1151,20 +1155,29 @@ impl PickerDelegate for BranchListDelegate {
let delete_and_select_btns = h_flex()
.gap_1()
- .child(
- Button::new("delete-branch", "Delete")
- .key_binding(
- KeyBinding::for_action_in(
- &branch_picker::DeleteBranch,
- &focus_handle,
- cx,
- )
- .map(|kb| kb.size(rems_from_px(12.))),
+ .when(
+ !selected_entry
+ .and_then(|entry| entry.as_branch())
+ .is_some_and(|branch| branch.is_head),
+ |this| {
+ this.child(
+ Button::new("delete-branch", "Delete")
+ .key_binding(
+ KeyBinding::for_action_in(
+ &branch_picker::DeleteBranch,
+ &focus_handle,
+ cx,
+ )
+ .map(|kb| kb.size(rems_from_px(12.))),
+ )
+ .on_click(|_, window, cx| {
+ window.dispatch_action(
+ branch_picker::DeleteBranch.boxed_clone(),
+ cx,
+ );
+ }),
)
- .on_click(|_, window, cx| {
- window
- .dispatch_action(branch_picker::DeleteBranch.boxed_clone(), cx);
- }),
+ },
)
.child(
Button::new("select_branch", "Select")
@@ -19,7 +19,7 @@ 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;
+use util::{ResultExt, debug_panic};
use workspace::{ModalView, MultiWorkspace, Workspace, notifications::DetachAndPromptErr};
use crate::git_panel::show_error_toast;
@@ -115,6 +115,7 @@ impl WorktreeList {
this.picker.update(cx, |picker, cx| {
picker.delegate.all_worktrees = Some(all_worktrees);
picker.delegate.default_branch = default_branch;
+ picker.delegate.refresh_forbidden_deletion_path(cx);
picker.refresh(window, cx);
})
})?;
@@ -261,6 +262,7 @@ pub struct WorktreeListDelegate {
modifiers: Modifiers,
focus_handle: FocusHandle,
default_branch: Option<SharedString>,
+ forbidden_deletion_path: Option<PathBuf>,
}
impl WorktreeListDelegate {
@@ -280,6 +282,7 @@ impl WorktreeListDelegate {
modifiers: Default::default(),
focus_handle: cx.focus_handle(),
default_branch: None,
+ forbidden_deletion_path: None,
}
}
@@ -452,7 +455,7 @@ impl WorktreeListDelegate {
let Some(entry) = self.matches.get(idx).cloned() else {
return;
};
- if entry.is_new {
+ if entry.is_new || self.forbidden_deletion_path.as_ref() == Some(&entry.worktree.path) {
return;
}
let Some(repo) = self.repo.clone() else {
@@ -486,6 +489,7 @@ impl WorktreeListDelegate {
if let Some(all_worktrees) = &mut picker.delegate.all_worktrees {
all_worktrees.retain(|w| w.path != path);
}
+ picker.delegate.refresh_forbidden_deletion_path(cx);
if picker.delegate.matches.is_empty() {
picker.delegate.selected_index = 0;
} else if picker.delegate.selected_index >= picker.delegate.matches.len() {
@@ -498,6 +502,29 @@ impl WorktreeListDelegate {
})
.detach();
}
+
+ fn refresh_forbidden_deletion_path(&mut self, cx: &App) {
+ let Some(workspace) = self.workspace.upgrade() else {
+ debug_panic!("Workspace should always be available or else the picker would be closed");
+ self.forbidden_deletion_path = None;
+ return;
+ };
+
+ let visible_worktree_paths = workspace.read_with(cx, |workspace, cx| {
+ workspace
+ .project()
+ .read(cx)
+ .visible_worktrees(cx)
+ .map(|worktree| worktree.read(cx).abs_path().to_path_buf())
+ .collect::<Vec<_>>()
+ });
+
+ self.forbidden_deletion_path = if visible_worktree_paths.len() == 1 {
+ visible_worktree_paths.into_iter().next()
+ } else {
+ None
+ };
+ }
}
async fn open_remote_worktree(
@@ -771,6 +798,9 @@ impl PickerDelegate for WorktreeListDelegate {
let focus_handle = self.focus_handle.clone();
+ let can_delete =
+ !entry.is_new && self.forbidden_deletion_path.as_ref() != Some(&entry.worktree.path);
+
let delete_button = |entry_ix: usize| {
IconButton::new(("delete-worktree", entry_ix), IconName::Trash)
.icon_size(IconSize::Small)
@@ -839,7 +869,7 @@ impl PickerDelegate for WorktreeListDelegate {
}
})),
)
- .when(!entry.is_new, |this| {
+ .when(can_delete, |this| {
if selected {
this.end_slot(delete_button(ix))
} else {
@@ -857,6 +887,9 @@ impl PickerDelegate for WorktreeListDelegate {
let focus_handle = self.focus_handle.clone();
let selected_entry = self.matches.get(self.selected_index);
let is_creating = selected_entry.is_some_and(|entry| entry.is_new);
+ let can_delete = selected_entry.is_some_and(|entry| {
+ !entry.is_new && self.forbidden_deletion_path.as_ref() != Some(&entry.worktree.path)
+ });
let footer_container = h_flex()
.w_full()
@@ -904,16 +937,18 @@ impl PickerDelegate for WorktreeListDelegate {
} else {
Some(
footer_container
- .child(
- Button::new("delete-worktree", "Delete")
- .key_binding(
- KeyBinding::for_action_in(&DeleteWorktree, &focus_handle, cx)
- .map(|kb| kb.size(rems_from_px(12.))),
- )
- .on_click(|_, window, cx| {
- window.dispatch_action(DeleteWorktree.boxed_clone(), cx)
- }),
- )
+ .when(can_delete, |this| {
+ this.child(
+ Button::new("delete-worktree", "Delete")
+ .key_binding(
+ KeyBinding::for_action_in(&DeleteWorktree, &focus_handle, cx)
+ .map(|kb| kb.size(rems_from_px(12.))),
+ )
+ .on_click(|_, window, cx| {
+ window.dispatch_action(DeleteWorktree.boxed_clone(), cx)
+ }),
+ )
+ })
.child(
Button::new("open-in-new-window", "Open in New Window")
.key_binding(