diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 12dcab9e87..18407b8d40 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -2570,8 +2570,8 @@ impl OutlinePanel { let auto_fold_dirs = OutlinePanelSettings::get_global(cx).auto_fold_dirs; let active_multi_buffer = active_editor.read(cx).buffer().clone(); let new_entries = self.new_entries_for_fs_update.clone(); - let repo_snapshots = self.project.update(cx, |project, cx| { - project.git_store().read(cx).repo_snapshots(cx) + let snapshots_by_abs_path = self.project.update(cx, |project, cx| { + project.git_store().read(cx).repo_snapshots_by_path(cx) }); self.updating_fs_entries = true; self.fs_entries_update_task = cx.spawn_in(window, async move |outline_panel, cx| { @@ -2698,7 +2698,7 @@ impl OutlinePanel { entry, }; let mut traversal = GitTraversal::new( - &repo_snapshots, + &snapshots_by_abs_path, worktree.traverse_from_path( true, true, diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index a872ece011..4e5ebdeae7 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -54,7 +54,7 @@ use std::{ ops::Range, path::{Path, PathBuf}, sync::{ - Arc, OnceLock, + Arc, atomic::{self, AtomicU64}, }, time::Instant, @@ -2211,6 +2211,13 @@ impl GitStore { .map(|(id, repo)| (*id, repo.read(cx).snapshot.clone())) .collect() } + + pub fn repo_snapshots_by_path(&self, cx: &App) -> BTreeMap, RepositorySnapshot> { + self.repositories_by_abs_root_path + .iter() + .map(|(path, repo)| (path.clone(), repo.read(cx).snapshot.clone())) + .collect() + } } impl BufferGitState { diff --git a/crates/project/src/git_store/git_traversal.rs b/crates/project/src/git_store/git_traversal.rs index 68ed03cfe9..95f7a8af2a 100644 --- a/crates/project/src/git_store/git_traversal.rs +++ b/crates/project/src/git_store/git_traversal.rs @@ -1,6 +1,10 @@ -use collections::HashMap; use git::status::GitSummary; -use std::{ops::Deref, path::Path}; +use std::{ + collections::BTreeMap, + ops::{Deref, Range}, + path::Path, + sync::Arc, +}; use sum_tree::Cursor; use text::Bias; use worktree::{Entry, PathProgress, PathTarget, Traversal}; @@ -11,18 +15,18 @@ use super::{RepositoryId, RepositorySnapshot, StatusEntry}; pub struct GitTraversal<'a> { traversal: Traversal<'a>, current_entry_summary: Option, - repo_snapshots: &'a HashMap, + snapshots_by_abs_path: &'a BTreeMap, RepositorySnapshot>, repo_location: Option<(RepositoryId, Cursor<'a, StatusEntry, PathProgress<'a>>)>, } impl<'a> GitTraversal<'a> { pub fn new( - repo_snapshots: &'a HashMap, + snapshots_by_abs_path: &'a BTreeMap, RepositorySnapshot>, traversal: Traversal<'a>, ) -> GitTraversal<'a> { let mut this = GitTraversal { traversal, - repo_snapshots, + snapshots_by_abs_path, current_entry_summary: None, repo_location: None, }; @@ -42,14 +46,15 @@ impl<'a> GitTraversal<'a> { return; }; - let Some((repo, repo_path)) = self - .repo_snapshots - .values() - .filter_map(|repo_snapshot| { - let repo_path = repo_snapshot.abs_path_to_repo_path(&abs_path)?; - Some((repo_snapshot, repo_path)) - }) - .max_by_key(|(repo, _)| repo.work_directory_abs_path.clone()) + let range: Range> = Arc::from("".as_ref())..Arc::from(abs_path.as_ref()); + let Some((repo, repo_path)) = + self.snapshots_by_abs_path + .range(range) + .last() + .and_then(|(_, repo)| { + let repo_path = repo.abs_path_to_repo_path(&abs_path)?; + Some((repo, repo_path)) + }) else { self.repo_location = None; return; @@ -145,12 +150,12 @@ pub struct ChildEntriesGitIter<'a> { impl<'a> ChildEntriesGitIter<'a> { pub fn new( - repo_snapshots: &'a HashMap, + snapshots_by_abs_path: &'a BTreeMap, RepositorySnapshot>, worktree_snapshot: &'a worktree::Snapshot, parent_path: &'a Path, ) -> Self { let mut traversal = GitTraversal::new( - repo_snapshots, + snapshots_by_abs_path, worktree_snapshot.traverse_from_path(true, true, true, parent_path), ); traversal.advance(); @@ -310,7 +315,7 @@ mod tests { let (repo_snapshots, worktree_snapshot) = project.read_with(cx, |project, cx| { ( - project.git_store().read(cx).repo_snapshots(cx), + project.git_store().read(cx).repo_snapshots_by_path(cx), project.worktrees(cx).next().unwrap().read(cx).snapshot(), ) }); @@ -385,7 +390,7 @@ mod tests { let (repo_snapshots, worktree_snapshot) = project.read_with(cx, |project, cx| { ( - project.git_store().read(cx).repo_snapshots(cx), + project.git_store().read(cx).repo_snapshots_by_path(cx), project.worktrees(cx).next().unwrap().read(cx).snapshot(), ) }); @@ -511,7 +516,7 @@ mod tests { let (repo_snapshots, worktree_snapshot) = project.read_with(cx, |project, cx| { ( - project.git_store().read(cx).repo_snapshots(cx), + project.git_store().read(cx).repo_snapshots_by_path(cx), project.worktrees(cx).next().unwrap().read(cx).snapshot(), ) }); @@ -620,7 +625,7 @@ mod tests { let (repo_snapshots, worktree_snapshot) = project.read_with(cx, |project, cx| { ( - project.git_store().read(cx).repo_snapshots(cx), + project.git_store().read(cx).repo_snapshots_by_path(cx), project.worktrees(cx).next().unwrap().read(cx).snapshot(), ) }); @@ -748,7 +753,7 @@ mod tests { let (repo_snapshots, worktree_snapshot) = project.read_with(cx, |project, cx| { ( - project.git_store().read(cx).repo_snapshots(cx), + project.git_store().read(cx).repo_snapshots_by_path(cx), project.worktrees(cx).next().unwrap().read(cx).snapshot(), ) }); @@ -766,7 +771,7 @@ mod tests { #[track_caller] fn check_git_statuses( - repo_snapshots: &HashMap, + repo_snapshots: &std::collections::BTreeMap, RepositorySnapshot>, worktree_snapshot: &worktree::Snapshot, expected_statuses: &[(&Path, GitSummary)], ) { diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 9f799b5be6..c999df8265 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1806,11 +1806,11 @@ impl ProjectPanel { let parent_entry = worktree.entry_for_path(parent_path)?; // Remove all siblings that are being deleted except the last marked entry - let repo_snapshots = git_store.repo_snapshots(cx); + let snapshots_by_root_path = git_store.repo_snapshots_by_path(cx); let worktree_snapshot = worktree.snapshot(); let hide_gitignore = ProjectPanelSettings::get_global(cx).hide_gitignore; let mut siblings: Vec<_> = - ChildEntriesGitIter::new(&repo_snapshots, &worktree_snapshot, parent_path) + ChildEntriesGitIter::new(&snapshots_by_root_path, &worktree_snapshot, parent_path) .filter(|sibling| { (sibling.id == latest_entry.id) || (!marked_entries_in_worktree.contains(&&SelectedEntry { @@ -2858,7 +2858,8 @@ impl ProjectPanel { let auto_collapse_dirs = settings.auto_fold_dirs; let hide_gitignore = settings.hide_gitignore; let project = self.project.read(cx); - let repo_snapshots = project.git_store().read(cx).repo_snapshots(cx); + let git_store = project.git_store().read(cx); + let snapshots_by_root_path = git_store.repo_snapshots_by_path(cx); self.last_worktree_root_id = project .visible_worktrees(cx) .next_back() @@ -2903,7 +2904,7 @@ impl ProjectPanel { let mut visible_worktree_entries = Vec::new(); let mut entry_iter = - GitTraversal::new(&repo_snapshots, worktree_snapshot.entries(true, 0)); + GitTraversal::new(&snapshots_by_root_path, worktree_snapshot.entries(true, 0)); let mut auto_folded_ancestors = vec![]; while let Some(entry) = entry_iter.entry() { if hide_root && Some(entry.entry) == worktree.read(cx).root_entry() { @@ -3503,16 +3504,12 @@ impl ProjectPanel { .cloned(); } - let repo_snapshots = self - .project - .read(cx) - .git_store() - .read(cx) - .repo_snapshots(cx); + let git_store = self.project.read(cx).git_store().read(cx); + let snapshots_by_root_path = git_store.repo_snapshots_by_path(cx); let worktree = self.project.read(cx).worktree_for_id(worktree_id, cx)?; worktree.read_with(cx, |tree, _| { utils::ReversibleIterable::new( - GitTraversal::new(&repo_snapshots, tree.entries(true, 0usize)), + GitTraversal::new(&snapshots_by_root_path, tree.entries(true, 0usize)), reverse_search, ) .find_single_ended(|ele| predicate(*ele, worktree_id)) @@ -3532,12 +3529,8 @@ impl ProjectPanel { .iter() .map(|(worktree_id, _, _)| *worktree_id) .collect(); - let repo_snapshots = self - .project - .read(cx) - .git_store() - .read(cx) - .repo_snapshots(cx); + let git_store = self.project.read(cx).git_store().read(cx); + let snapshots_by_root_path = git_store.repo_snapshots_by_path(cx); let mut last_found: Option = None; @@ -3554,7 +3547,7 @@ impl ProjectPanel { let tree_id = worktree.id(); let mut first_iter = GitTraversal::new( - &repo_snapshots, + &snapshots_by_root_path, worktree.traverse_from_path(true, true, true, entry.path.as_ref()), ); @@ -3570,7 +3563,7 @@ impl ProjectPanel { .map(|ele| ele.to_owned()); let second_iter = - GitTraversal::new(&repo_snapshots, worktree.entries(true, 0usize)); + GitTraversal::new(&snapshots_by_root_path, worktree.entries(true, 0usize)); let second = if reverse_search { second_iter