Autotrust new git worktrees (#45138)

Conrad Irwin and Kirill Bulatov created

Follow-up of https://github.com/zed-industries/zed/pull/44887

- Inherit git worktree trust
- Tidy up the security modal


Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>

Change summary

crates/git_ui/src/worktree_picker.rs   | 41 +++++++++++++++++++---
crates/workspace/src/security_modal.rs | 51 +++++++++------------------
2 files changed, 53 insertions(+), 39 deletions(-)

Detailed changes

crates/git_ui/src/worktree_picker.rs 🔗

@@ -1,4 +1,5 @@
 use anyhow::Context as _;
+use collections::HashSet;
 use fuzzy::StringMatchCandidate;
 
 use git::repository::Worktree as GitWorktree;
@@ -9,7 +10,11 @@ use gpui::{
     actions, rems,
 };
 use picker::{Picker, PickerDelegate, PickerEditorPosition};
-use project::{DirectoryLister, git_store::Repository};
+use project::{
+    DirectoryLister,
+    git_store::Repository,
+    trusted_worktrees::{PathTrust, RemoteHostLocation, TrustedWorktrees},
+};
 use recent_projects::{RemoteConnectionModal, connect};
 use remote::{RemoteConnectionOptions, remote_client::ConnectionIdentifier};
 use std::{path::PathBuf, sync::Arc};
@@ -219,7 +224,6 @@ impl WorktreeListDelegate {
         window: &mut Window,
         cx: &mut Context<Picker<Self>>,
     ) {
-        let workspace = self.workspace.clone();
         let Some(repo) = self.repo.clone() else {
             return;
         };
@@ -247,6 +251,7 @@ impl WorktreeListDelegate {
 
         let branch = worktree_branch.to_string();
         let window_handle = window.window_handle();
+        let workspace = self.workspace.clone();
         cx.spawn_in(window, async move |_, cx| {
             let Some(paths) = worktree_path.await? else {
                 return anyhow::Ok(());
@@ -257,8 +262,32 @@ impl WorktreeListDelegate {
                 repo.create_worktree(branch.clone(), path.clone(), commit)
             })?
             .await??;
-
-            let final_path = path.join(branch);
+            let new_worktree_path = path.join(branch);
+
+            workspace.update(cx, |workspace, cx| {
+                if let Some(trusted_worktrees) = TrustedWorktrees::try_get_global(cx) {
+                    let repo_path = &repo.read(cx).snapshot().work_directory_abs_path;
+                    let project = workspace.project();
+                    if let Some((parent_worktree, _)) =
+                        project.read(cx).find_worktree(repo_path, cx)
+                    {
+                        trusted_worktrees.update(cx, |trusted_worktrees, cx| {
+                            if trusted_worktrees.can_trust(parent_worktree.read(cx).id(), cx) {
+                                trusted_worktrees.trust(
+                                    HashSet::from_iter([PathTrust::AbsPath(
+                                        new_worktree_path.clone(),
+                                    )]),
+                                    project
+                                        .read(cx)
+                                        .remote_connection_options(cx)
+                                        .map(RemoteHostLocation::from),
+                                    cx,
+                                );
+                            }
+                        });
+                    }
+                }
+            })?;
 
             let (connection_options, app_state, is_local) =
                 workspace.update(cx, |workspace, cx| {
@@ -274,7 +303,7 @@ impl WorktreeListDelegate {
                     .update_in(cx, |workspace, window, cx| {
                         workspace.open_workspace_for_paths(
                             replace_current_window,
-                            vec![final_path],
+                            vec![new_worktree_path],
                             window,
                             cx,
                         )
@@ -283,7 +312,7 @@ impl WorktreeListDelegate {
             } else if let Some(connection_options) = connection_options {
                 open_remote_worktree(
                     connection_options,
-                    vec![final_path],
+                    vec![new_worktree_path],
                     app_state,
                     window_handle,
                     replace_current_window,

crates/workspace/src/security_modal.rs 🔗

@@ -102,46 +102,31 @@ impl Render for SecurityModal {
                             .child(Icon::new(IconName::Warning).color(Color::Warning))
                             .child(Label::new(header_label)),
                     )
-                    .children(self.restricted_paths.values().map(|restricted_path| {
+                    .children(self.restricted_paths.values().filter_map(|restricted_path| {
                         let abs_path = if restricted_path.is_file {
                             restricted_path.abs_path.parent()
                         } else {
                             Some(restricted_path.abs_path.as_ref())
-                        };
-
-                        let label = match abs_path {
-                            Some(abs_path) => match &restricted_path.host {
-                                Some(remote_host) => match &remote_host.user_name {
-                                    Some(user_name) => format!(
-                                        "{} ({}@{})",
-                                        self.shorten_path(abs_path).display(),
-                                        user_name,
-                                        remote_host.host_identifier
-                                    ),
-                                    None => format!(
-                                        "{} ({})",
-                                        self.shorten_path(abs_path).display(),
-                                        remote_host.host_identifier
-                                    ),
-                                },
-                                None => self.shorten_path(abs_path).display().to_string(),
-                            },
-                            None => match &restricted_path.host {
-                                Some(remote_host) => match &remote_host.user_name {
-                                    Some(user_name) => format!(
-                                        "Workspace trust ({}@{})",
-                                        user_name, remote_host.host_identifier
-                                    ),
-                                    None => {
-                                        format!("Workspace trust ({})", remote_host.host_identifier)
-                                    }
-                                },
-                                None => "Workspace trust".to_string(),
+                        }?;
+                        let label = match &restricted_path.host {
+                            Some(remote_host) => match &remote_host.user_name {
+                                Some(user_name) => format!(
+                                    "{} ({}@{})",
+                                    self.shorten_path(abs_path).display(),
+                                    user_name,
+                                    remote_host.host_identifier
+                                ),
+                                None => format!(
+                                    "{} ({})",
+                                    self.shorten_path(abs_path).display(),
+                                    remote_host.host_identifier
+                                ),
                             },
+                            None => self.shorten_path(abs_path).display().to_string(),
                         };
-                        h_flex()
+                        Some(h_flex()
                             .pl(IconSize::default().rems() + rems(0.5))
-                            .child(Label::new(label).color(Color::Muted))
+                            .child(Label::new(label).color(Color::Muted)))
                     })),
             )
             .child(