Fix a bug that prevented repositories from being deduplicated (#27884)

Cole Miller created

Closes #ISSUE

Release Notes:

- Ensure that only one repository is shown in the git UI when two
subdirectories of a repository root are open in Zed

Change summary

crates/project/src/git_store.rs     |  6 +++
crates/project/src/project_tests.rs | 46 +++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 1 deletion(-)

Detailed changes

crates/project/src/git_store.rs 🔗

@@ -1062,8 +1062,12 @@ impl GitStore {
         let mut removed_ids = Vec::new();
         for update in updated_git_repositories.iter() {
             if let Some((id, existing)) = self.repositories.iter().find(|(_, repo)| {
-                Some(&repo.read(cx).work_directory_abs_path)
+                let existing_work_directory_abs_path =
+                    repo.read(cx).work_directory_abs_path.clone();
+                Some(&existing_work_directory_abs_path)
                     == update.old_work_directory_abs_path.as_ref()
+                    || Some(&existing_work_directory_abs_path)
+                        == update.new_work_directory_abs_path.as_ref()
             }) {
                 if let Some(new_work_directory_abs_path) =
                     update.new_work_directory_abs_path.clone()

crates/project/src/project_tests.rs 🔗

@@ -7986,6 +7986,52 @@ async fn test_rescan_with_gitignore(cx: &mut gpui::TestAppContext) {
     });
 }
 
+#[gpui::test]
+async fn test_repository_deduplication(cx: &mut gpui::TestAppContext) {
+    init_test(cx);
+    let fs = FakeFs::new(cx.background_executor.clone());
+    fs.insert_tree(
+        path!("/root"),
+        json!({
+            "project": {
+                ".git": {},
+                "child1": {
+                    "a.txt": "A",
+                },
+                "child2": {
+                    "b.txt": "B",
+                }
+            }
+        }),
+    )
+    .await;
+
+    let project = Project::test(
+        fs.clone(),
+        [
+            path!("/root/project/child1").as_ref(),
+            path!("/root/project/child2").as_ref(),
+        ],
+        cx,
+    )
+    .await;
+
+    let tree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap());
+    tree.flush_fs_events(cx).await;
+    cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
+        .await;
+    cx.executor().run_until_parked();
+
+    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/project")).into()]);
+}
+
 async fn search(
     project: &Entity<Project>,
     query: SearchQuery,