diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 509d694885b1ad..c9c9ade975e0f9 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -3584,6 +3584,23 @@ impl GitStore { .collect() } + fn coalesce_repo_paths(mut paths: Vec) -> Vec { + paths.sort(); + + let mut coalesced = Vec::with_capacity(paths.len()); + for path in paths { + if coalesced + .last() + .is_some_and(|ancestor: &RepoPath| path.starts_with(ancestor)) + { + continue; + } + coalesced.push(path); + } + + coalesced + } + fn process_updated_entries( &self, worktree: &Entity, @@ -3660,6 +3677,10 @@ impl GitStore { } } + for paths in paths_by_git_repo.values_mut() { + *paths = Self::coalesce_repo_paths(mem::take(paths)); + } + paths_by_git_repo }) } @@ -8664,6 +8685,7 @@ mod tests { use super::*; use crate::Project; use fs::FakeFs; + use git::repository::{RepoPath, repo_path}; use gpui::TestAppContext; use gpui::proptest::prelude::*; use rand::{SeedableRng, rngs::StdRng}; @@ -9008,6 +9030,43 @@ mod tests { ); }); } + + fn repo_paths(paths: &[&str]) -> Vec { + paths.iter().map(repo_path).collect() + } + + #[test] + fn coalesce_repo_paths_keeps_root_only() { + let coalesced = GitStore::coalesce_repo_paths(repo_paths(&["", "src", "src/lib.rs"])); + + assert_eq!(coalesced, repo_paths(&[""])); + } + + #[test] + fn coalesce_repo_paths_keeps_existing_ancestors() { + let coalesced = GitStore::coalesce_repo_paths(repo_paths(&[ + "src", + "src/lib.rs", + "src/nested/file.rs", + "tests/test.rs", + ])); + + assert_eq!(coalesced, repo_paths(&["src", "tests/test.rs"])); + } + + #[test] + fn coalesce_repo_paths_does_not_invent_missing_parents() { + let coalesced = GitStore::coalesce_repo_paths(repo_paths(&[ + "submodule/a.txt", + "submodule/nested/b.txt", + "top_level.rs", + ])); + + assert_eq!( + coalesced, + repo_paths(&["submodule/a.txt", "submodule/nested/b.txt", "top_level.rs"]) + ); + } } /// This snapshot computes the repository state on the foreground thread while