Don't create repos for invisible worktrees (#27894)

Cole Miller created

Closes #ISSUE

Release Notes:

- Fixed git repositories being added for files outside the project

Change summary

crates/project/src/git_store.rs     | 19 +++++++--
crates/project/src/project_tests.rs | 62 +++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+), 5 deletions(-)

Detailed changes

crates/project/src/git_store.rs 🔗

@@ -1017,7 +1017,18 @@ impl GitStore {
                 }
             }
             WorktreeStoreEvent::WorktreeUpdatedGitRepositories(worktree_id, changed_repos) => {
-                self.update_repositories_from_worktrees(
+                let Some(worktree) = worktree_store.read(cx).worktree_for_id(*worktree_id, cx)
+                else {
+                    return;
+                };
+                if !worktree.read(cx).is_visible() {
+                    log::debug!(
+                        "not adding repositories for local worktree {:?} because it's not visible",
+                        worktree.read(cx).abs_path()
+                    );
+                    return;
+                }
+                self.update_repositories_from_worktree(
                     project_environment.clone(),
                     next_repository_id.clone(),
                     downstream
@@ -1027,9 +1038,7 @@ impl GitStore {
                     fs.clone(),
                     cx,
                 );
-                if let Some(worktree) = worktree_store.read(cx).worktree_for_id(*worktree_id, cx) {
-                    self.local_worktree_git_repos_changed(worktree, changed_repos, cx);
-                }
+                self.local_worktree_git_repos_changed(worktree, changed_repos, cx);
             }
             _ => {}
         }
@@ -1050,7 +1059,7 @@ impl GitStore {
     }
 
     /// Update our list of repositories and schedule git scans in response to a notification from a worktree,
-    fn update_repositories_from_worktrees(
+    fn update_repositories_from_worktree(
         &mut self,
         project_environment: Entity<ProjectEnvironment>,
         next_repository_id: Arc<AtomicU64>,

crates/project/src/project_tests.rs 🔗

@@ -7851,6 +7851,68 @@ async fn test_file_status(cx: &mut gpui::TestAppContext) {
     });
 }
 
+#[gpui::test]
+async fn test_repos_in_invisible_worktrees(
+    executor: BackgroundExecutor,
+    cx: &mut gpui::TestAppContext,
+) {
+    init_test(cx);
+    let fs = FakeFs::new(executor);
+    fs.insert_tree(
+        path!("/root"),
+        json!({
+            "dir1": {
+                ".git": {},
+                "dep1": {
+                    ".git": {},
+                    "src": {
+                        "a.txt": "",
+                    },
+                },
+                "b.txt": "",
+            },
+        }),
+    )
+    .await;
+
+    let project = Project::test(fs.clone(), [path!("/root/dir1/dep1").as_ref()], cx).await;
+    let visible_worktree =
+        project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap());
+    visible_worktree
+        .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
+        .await;
+
+    let repos = project.read_with(cx, |project, cx| {
+        project
+            .repositories(cx)
+            .values()
+            .map(|repo| repo.read(cx).work_directory_abs_path.clone())
+            .collect::<Vec<_>>()
+    });
+    pretty_assertions::assert_eq!(repos, [Path::new(path!("/root/dir1/dep1")).into()]);
+
+    let (invisible_worktree, _) = project
+        .update(cx, |project, cx| {
+            project.worktree_store.update(cx, |worktree_store, cx| {
+                worktree_store.find_or_create_worktree(path!("/root/dir1/b.txt"), false, cx)
+            })
+        })
+        .await
+        .expect("failed to create worktree");
+    invisible_worktree
+        .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
+        .await;
+
+    let repos = project.read_with(cx, |project, cx| {
+        project
+            .repositories(cx)
+            .values()
+            .map(|repo| repo.read(cx).work_directory_abs_path.clone())
+            .collect::<Vec<_>>()
+    });
+    pretty_assertions::assert_eq!(repos, [Path::new(path!("/root/dir1/dep1")).into()]);
+}
+
 #[gpui::test(iterations = 10)]
 async fn test_rescan_with_gitignore(cx: &mut gpui::TestAppContext) {
     init_test(cx);