From 22e4086658365324c7984ecf7a44ff74240ebfc4 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 11:02:59 -0700 Subject: [PATCH] WIP: Move statuses to be on their associated file entries in worktree co-authored-by: Julia --- Cargo.lock | 1 + crates/collab/src/db.rs | 143 +-------- crates/collab/src/tests/integration_tests.rs | 3 +- crates/fs/Cargo.toml | 2 + crates/fs/src/repository.rs | 21 ++ crates/project/src/project.rs | 12 +- crates/project/src/worktree.rs | 309 +++++-------------- crates/project_panel/src/project_panel.rs | 11 +- crates/rpc/proto/zed.proto | 3 +- crates/rpc/src/rpc.rs | 2 +- 10 files changed, 124 insertions(+), 383 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3751b34d31..4bea44ffee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2438,6 +2438,7 @@ dependencies = [ "parking_lot 0.11.2", "regex", "rope", + "rpc", "serde", "serde_derive", "serde_json", diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index fd28fb9101..e3d553c606 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1537,6 +1537,8 @@ impl Database { }), is_symlink: db_entry.is_symlink, is_ignored: db_entry.is_ignored, + // TODO stream statuses + git_status: None, }); } } @@ -1571,54 +1573,6 @@ impl Database { worktree.updated_repositories.push(proto::RepositoryEntry { work_directory_id: db_repository.work_directory_id as u64, branch: db_repository.branch, - removed_repo_paths: Default::default(), - updated_statuses: Default::default(), - }); - } - } - } - - // Repository Status Entries - for repository in worktree.updated_repositories.iter_mut() { - let repository_status_entry_filter = - if let Some(rejoined_worktree) = rejoined_worktree { - worktree_repository_statuses::Column::ScanId - .gt(rejoined_worktree.scan_id) - } else { - worktree_repository_statuses::Column::IsDeleted.eq(false) - }; - - let mut db_repository_statuses = - worktree_repository_statuses::Entity::find() - .filter( - Condition::all() - .add( - worktree_repository_statuses::Column::ProjectId - .eq(project.id), - ) - .add( - worktree_repository_statuses::Column::WorktreeId - .eq(worktree.id), - ) - .add( - worktree_repository_statuses::Column::WorkDirectoryId - .eq(repository.work_directory_id), - ) - .add(repository_status_entry_filter), - ) - .stream(&*tx) - .await?; - - while let Some(db_status_entry) = db_repository_statuses.next().await { - let db_status_entry = db_status_entry?; - if db_status_entry.is_deleted { - repository - .removed_repo_paths - .push(db_status_entry.repo_path); - } else { - repository.updated_statuses.push(proto::StatusEntry { - repo_path: db_status_entry.repo_path, - status: db_status_entry.status as i32, }); } } @@ -2446,68 +2400,6 @@ impl Database { ) .exec(&*tx) .await?; - - for repository in update.updated_repositories.iter() { - if !repository.updated_statuses.is_empty() { - worktree_repository_statuses::Entity::insert_many( - repository.updated_statuses.iter().map(|status_entry| { - worktree_repository_statuses::ActiveModel { - project_id: ActiveValue::set(project_id), - worktree_id: ActiveValue::set(worktree_id), - work_directory_id: ActiveValue::set( - repository.work_directory_id as i64, - ), - repo_path: ActiveValue::set(status_entry.repo_path.clone()), - status: ActiveValue::set(status_entry.status as i64), - scan_id: ActiveValue::set(update.scan_id as i64), - is_deleted: ActiveValue::set(false), - } - }), - ) - .on_conflict( - OnConflict::columns([ - worktree_repository_statuses::Column::ProjectId, - worktree_repository_statuses::Column::WorktreeId, - worktree_repository_statuses::Column::WorkDirectoryId, - worktree_repository_statuses::Column::RepoPath, - ]) - .update_columns([ - worktree_repository_statuses::Column::ScanId, - worktree_repository_statuses::Column::Status, - worktree_repository_statuses::Column::IsDeleted, - ]) - .to_owned(), - ) - .exec(&*tx) - .await?; - } - - if !repository.removed_repo_paths.is_empty() { - worktree_repository_statuses::Entity::update_many() - .filter( - worktree_repository_statuses::Column::ProjectId - .eq(project_id) - .and( - worktree_repository_statuses::Column::WorktreeId - .eq(worktree_id), - ) - .and( - worktree_repository_statuses::Column::WorkDirectoryId - .eq(repository.work_directory_id as i64), - ) - .and(worktree_repository_statuses::Column::RepoPath.is_in( - repository.removed_repo_paths.iter().map(String::as_str), - )), - ) - .set(worktree_repository_statuses::ActiveModel { - is_deleted: ActiveValue::Set(true), - scan_id: ActiveValue::Set(update.scan_id as i64), - ..Default::default() - }) - .exec(&*tx) - .await?; - } - } } if !update.removed_repositories.is_empty() { @@ -2738,6 +2630,8 @@ impl Database { }), is_symlink: db_entry.is_symlink, is_ignored: db_entry.is_ignored, + // TODO stream statuses + git_status: None, }); } } @@ -2763,41 +2657,12 @@ impl Database { proto::RepositoryEntry { work_directory_id: db_repository_entry.work_directory_id as u64, branch: db_repository_entry.branch, - removed_repo_paths: Default::default(), - updated_statuses: Default::default(), }, ); } } } - { - let mut db_status_entries = worktree_repository_statuses::Entity::find() - .filter( - Condition::all() - .add(worktree_repository_statuses::Column::ProjectId.eq(project_id)) - .add(worktree_repository_statuses::Column::IsDeleted.eq(false)), - ) - .stream(&*tx) - .await?; - - while let Some(db_status_entry) = db_status_entries.next().await { - let db_status_entry = db_status_entry?; - if let Some(worktree) = worktrees.get_mut(&(db_status_entry.worktree_id as u64)) - { - if let Some(repository_entry) = worktree - .repository_entries - .get_mut(&(db_status_entry.work_directory_id as u64)) - { - repository_entry.updated_statuses.push(proto::StatusEntry { - repo_path: db_status_entry.repo_path, - status: db_status_entry.status as i32, - }); - } - } - } - } - // Populate worktree diagnostic summaries. { let mut db_summaries = worktree_diagnostic_summary::Entity::find() diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 53726113db..5f5057478e 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -2763,8 +2763,7 @@ async fn test_git_status_sync( assert_eq!(worktrees.len(), 1); let worktree = worktrees[0].clone(); let snapshot = worktree.read(cx).snapshot(); - let root_entry = snapshot.root_git_entry().unwrap(); - assert_eq!(root_entry.status_for_file(&snapshot, file), status); + assert_eq!(snapshot.status_for_file(file), status); } // Smoke test status reading diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 54c6ce362a..7dda3f7273 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -14,6 +14,8 @@ lsp = { path = "../lsp" } rope = { path = "../rope" } util = { path = "../util" } sum_tree = { path = "../sum_tree" } +rpc = { path = "../rpc" } + anyhow.workspace = true async-trait.workspace = true futures.workspace = true diff --git a/crates/fs/src/repository.rs b/crates/fs/src/repository.rs index 2c309351fc..8305d11bb4 100644 --- a/crates/fs/src/repository.rs +++ b/crates/fs/src/repository.rs @@ -1,6 +1,7 @@ use anyhow::Result; use collections::HashMap; use parking_lot::Mutex; +use rpc::proto; use serde_derive::{Deserialize, Serialize}; use std::{ cmp::Ordering, @@ -197,6 +198,26 @@ pub enum GitFileStatus { Conflict, } +impl GitFileStatus { + pub fn from_proto(git_status: Option) -> Option { + git_status.and_then(|status| { + proto::GitStatus::from_i32(status).map(|status| match status { + proto::GitStatus::Added => GitFileStatus::Added, + proto::GitStatus::Modified => GitFileStatus::Modified, + proto::GitStatus::Conflict => GitFileStatus::Conflict, + }) + }) + } + + pub fn to_proto(self) -> i32 { + match self { + GitFileStatus::Added => proto::GitStatus::Added as i32, + GitFileStatus::Modified => proto::GitStatus::Modified as i32, + GitFileStatus::Conflict => proto::GitStatus::Conflict as i32, + } + } +} + #[derive(Clone, Debug, Ord, Hash, PartialOrd, Eq, PartialEq)] pub struct RepoPath(PathBuf); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index fd522c5061..ed30c0ecd4 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5095,9 +5095,9 @@ impl Project { return None; } let path = &project_path.path; - changed_repos.iter().find(|(work_dir, change)| { - path.starts_with(work_dir) && change.git_dir_changed - })?; + changed_repos + .iter() + .find(|(work_dir, _)| path.starts_with(work_dir))?; let receiver = receiver.clone(); let path = path.clone(); Some(async move { @@ -5120,9 +5120,9 @@ impl Project { return None; } let path = file.path(); - changed_repos.iter().find(|(work_dir, change)| { - path.starts_with(work_dir) && change.git_dir_changed - })?; + changed_repos + .iter() + .find(|(work_dir, _)| path.starts_with(work_dir))?; Some((buffer, path.clone())) }) .collect::>(); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index dc3c172775..678b34ebed 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -7,7 +7,7 @@ use client::{proto, Client}; use clock::ReplicaId; use collections::{HashMap, VecDeque}; use fs::{ - repository::{GitFileStatus, GitRepository, RepoPath, RepoPathDescendants}, + repository::{GitFileStatus, GitRepository, RepoPath}, Fs, LineEnding, }; use futures::{ @@ -55,7 +55,7 @@ use std::{ time::{Duration, SystemTime}, }; use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet}; -use util::{paths::HOME, ResultExt, TakeUntilExt}; +use util::{paths::HOME, ResultExt}; #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] pub struct WorktreeId(usize); @@ -124,15 +124,6 @@ pub struct Snapshot { pub struct RepositoryEntry { pub(crate) work_directory: WorkDirectoryEntry, pub(crate) branch: Option>, - pub(crate) statuses: TreeMap, -} - -fn read_git_status(git_status: i32) -> Option { - proto::GitStatus::from_i32(git_status).map(|status| match status { - proto::GitStatus::Added => GitFileStatus::Added, - proto::GitStatus::Modified => GitFileStatus::Modified, - proto::GitStatus::Conflict => GitFileStatus::Conflict, - }) } impl RepositoryEntry { @@ -150,115 +141,19 @@ impl RepositoryEntry { .map(|entry| RepositoryWorkDirectory(entry.path.clone())) } - pub fn status_for_path(&self, snapshot: &Snapshot, path: &Path) -> Option { - self.work_directory - .relativize(snapshot, path) - .and_then(|repo_path| { - self.statuses - .iter_from(&repo_path) - .take_while(|(key, _)| key.starts_with(&repo_path)) - // Short circut once we've found the highest level - .take_until(|(_, status)| status == &&GitFileStatus::Conflict) - .map(|(_, status)| status) - .reduce( - |status_first, status_second| match (status_first, status_second) { - (GitFileStatus::Conflict, _) | (_, GitFileStatus::Conflict) => { - &GitFileStatus::Conflict - } - (GitFileStatus::Modified, _) | (_, GitFileStatus::Modified) => { - &GitFileStatus::Modified - } - _ => &GitFileStatus::Added, - }, - ) - .copied() - }) - } - - #[cfg(any(test, feature = "test-support"))] - pub fn status_for_file(&self, snapshot: &Snapshot, path: &Path) -> Option { - self.work_directory - .relativize(snapshot, path) - .and_then(|repo_path| (&self.statuses).get(&repo_path)) - .cloned() - } - - pub fn build_update(&self, other: &Self) -> proto::RepositoryEntry { - let mut updated_statuses: Vec = Vec::new(); - let mut removed_statuses: Vec = Vec::new(); - - let mut self_statuses = self.statuses.iter().peekable(); - let mut other_statuses = other.statuses.iter().peekable(); - loop { - match (self_statuses.peek(), other_statuses.peek()) { - (Some((self_repo_path, self_status)), Some((other_repo_path, other_status))) => { - match Ord::cmp(self_repo_path, other_repo_path) { - Ordering::Less => { - updated_statuses.push(make_status_entry(self_repo_path, self_status)); - self_statuses.next(); - } - Ordering::Equal => { - if self_status != other_status { - updated_statuses - .push(make_status_entry(self_repo_path, self_status)); - } - - self_statuses.next(); - other_statuses.next(); - } - Ordering::Greater => { - removed_statuses.push(make_repo_path(other_repo_path)); - other_statuses.next(); - } - } - } - (Some((self_repo_path, self_status)), None) => { - updated_statuses.push(make_status_entry(self_repo_path, self_status)); - self_statuses.next(); - } - (None, Some((other_repo_path, _))) => { - removed_statuses.push(make_repo_path(other_repo_path)); - other_statuses.next(); - } - (None, None) => break, - } - } - + pub fn build_update(&self, _: &Self) -> proto::RepositoryEntry { proto::RepositoryEntry { work_directory_id: self.work_directory_id().to_proto(), branch: self.branch.as_ref().map(|str| str.to_string()), - removed_repo_paths: removed_statuses, - updated_statuses, } } } -fn make_repo_path(path: &RepoPath) -> String { - path.as_os_str().to_string_lossy().to_string() -} - -fn make_status_entry(path: &RepoPath, status: &GitFileStatus) -> proto::StatusEntry { - proto::StatusEntry { - repo_path: make_repo_path(path), - status: match status { - GitFileStatus::Added => proto::GitStatus::Added.into(), - GitFileStatus::Modified => proto::GitStatus::Modified.into(), - GitFileStatus::Conflict => proto::GitStatus::Conflict.into(), - }, - } -} - impl From<&RepositoryEntry> for proto::RepositoryEntry { fn from(value: &RepositoryEntry) -> Self { proto::RepositoryEntry { work_directory_id: value.work_directory.to_proto(), branch: value.branch.as_ref().map(|str| str.to_string()), - updated_statuses: value - .statuses - .iter() - .map(|(repo_path, status)| make_status_entry(repo_path, status)) - .collect(), - removed_repo_paths: Default::default(), } } } @@ -330,7 +225,6 @@ pub struct BackgroundScannerState { #[derive(Debug, Clone)] pub struct LocalRepositoryEntry { - pub(crate) work_dir_scan_id: usize, pub(crate) git_dir_scan_id: usize, pub(crate) repo_ptr: Arc>, /// Path to the actual .git folder. @@ -867,18 +761,13 @@ impl LocalWorktree { entry.path.clone(), GitRepositoryChange { old_repository: None, - git_dir_changed: true, }, )); } new_repos.next(); } Ordering::Equal => { - let git_dir_changed = - new_repo.git_dir_scan_id != old_repo.git_dir_scan_id; - let work_dir_changed = - new_repo.work_dir_scan_id != old_repo.work_dir_scan_id; - if git_dir_changed || work_dir_changed { + if new_repo.git_dir_scan_id != old_repo.git_dir_scan_id { if let Some(entry) = new_snapshot.entry_for_id(new_entry_id) { let old_repo = old_snapshot .repository_entries @@ -888,7 +777,6 @@ impl LocalWorktree { entry.path.clone(), GitRepositoryChange { old_repository: old_repo, - git_dir_changed, }, )); } @@ -906,7 +794,6 @@ impl LocalWorktree { entry.path.clone(), GitRepositoryChange { old_repository: old_repo, - git_dir_changed: true, }, )); } @@ -920,7 +807,6 @@ impl LocalWorktree { entry.path.clone(), GitRepositoryChange { old_repository: None, - git_dir_changed: true, }, )); } @@ -936,7 +822,6 @@ impl LocalWorktree { entry.path.clone(), GitRepositoryChange { old_repository: old_repo, - git_dir_changed: true, }, )); } @@ -1587,6 +1472,13 @@ impl Snapshot { Some(removed_entry.path) } + #[cfg(any(test, feature = "test-support"))] + pub fn status_for_file(&self, path: impl Into) -> Option { + self.entries_by_path + .get(&PathKey(Arc::from(path.into())), &()) + .and_then(|entry| entry.git_status) + } + pub(crate) fn apply_remote_update(&mut self, mut update: proto::UpdateWorktree) -> Result<()> { let mut entries_by_path_edits = Vec::new(); let mut entries_by_id_edits = Vec::new(); @@ -1638,26 +1530,10 @@ impl Snapshot { ProjectEntryId::from_proto(repository.work_directory_id).into(); if let Some(entry) = self.entry_for_id(*work_directory_entry) { - let mut statuses = TreeMap::default(); - for status_entry in repository.updated_statuses { - let Some(git_file_status) = read_git_status(status_entry.status) else { - continue; - }; - - let repo_path = RepoPath::new(status_entry.repo_path.into()); - statuses.insert(repo_path, git_file_status); - } - let work_directory = RepositoryWorkDirectory(entry.path.clone()); if self.repository_entries.get(&work_directory).is_some() { self.repository_entries.update(&work_directory, |repo| { repo.branch = repository.branch.map(Into::into); - repo.statuses.insert_tree(statuses); - - for repo_path in repository.removed_repo_paths { - let repo_path = RepoPath::new(repo_path.into()); - repo.statuses.remove(&repo_path); - } }); } else { self.repository_entries.insert( @@ -1665,7 +1541,6 @@ impl Snapshot { RepositoryEntry { work_directory: work_directory_entry, branch: repository.branch.map(Into::into), - statuses, }, ) } @@ -2063,7 +1938,8 @@ impl LocalSnapshot { RepositoryEntry { work_directory: work_dir_id.into(), branch: repo_lock.branch_name().map(Into::into), - statuses: repo_lock.statuses().unwrap_or_default(), + // TODO: statuses + // statuses: repo_lock.statuses().unwrap_or_default(), }, ); drop(repo_lock); @@ -2071,7 +1947,6 @@ impl LocalSnapshot { self.git_repositories.insert( work_dir_id, LocalRepositoryEntry { - work_dir_scan_id: scan_id, git_dir_scan_id: scan_id, repo_ptr: repo, git_dir_path: parent_path.clone(), @@ -2498,6 +2373,7 @@ pub struct Entry { pub mtime: SystemTime, pub is_symlink: bool, pub is_ignored: bool, + pub git_status: Option, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -2526,9 +2402,6 @@ pub enum PathChange { pub struct GitRepositoryChange { /// The previous state of the repository, if it already existed. pub old_repository: Option, - /// Whether the content of the .git directory changed. This will be false - /// if only the repository's work directory changed. - pub git_dir_changed: bool, } pub type UpdatedEntriesSet = Arc<[(Arc, ProjectEntryId, PathChange)]>; @@ -2553,6 +2426,7 @@ impl Entry { mtime: metadata.mtime, is_symlink: metadata.is_symlink, is_ignored: false, + git_status: None, } } @@ -3199,8 +3073,6 @@ impl BackgroundScanner { .components() .any(|component| component.as_os_str() == *DOT_GIT) { - let scan_id = snapshot.scan_id; - if let Some(repository) = snapshot.repository_for_work_directory(path) { let entry = repository.work_directory.0; snapshot.git_repositories.remove(&entry); @@ -3210,23 +3082,11 @@ impl BackgroundScanner { .remove(&RepositoryWorkDirectory(path.into())); return Some(()); } - - let repo = snapshot.repository_for_path(&path)?; - let repo_path = repo.work_directory.relativize(&snapshot, &path)?; - let work_dir = repo.work_directory(snapshot)?; - let work_dir_id = repo.work_directory; - - snapshot - .git_repositories - .update(&work_dir_id, |entry| entry.work_dir_scan_id = scan_id); - - snapshot.repository_entries.update(&work_dir, |entry| { - entry - .statuses - .remove_range(&repo_path, &RepoPathDescendants(&repo_path)) - }); } + // TODO statuses + // Track when a .git is removed and iterate over the file system there + Some(()) } @@ -3264,35 +3124,46 @@ impl BackgroundScanner { let repo = repo_ptr.lock(); repo.reload_index(); let branch = repo.branch_name(); - let statuses = repo.statuses().unwrap_or_default(); snapshot.git_repositories.update(&entry_id, |entry| { - entry.work_dir_scan_id = scan_id; entry.git_dir_scan_id = scan_id; }); - snapshot.repository_entries.update(&work_dir, |entry| { - entry.branch = branch.map(Into::into); - entry.statuses = statuses; - }); + snapshot + .snapshot + .repository_entries + .update(&work_dir, |entry| { + entry.branch = branch.map(Into::into); + }); + + let statuses = repo.statuses().unwrap_or_default(); + for (repo_path, status) in statuses.iter() { + let Some(entry) = snapshot.entry_for_path(&work_dir.0.join(repo_path)) else { + continue; + }; + + let mut entry = entry.clone(); + entry.git_status = Some(*status); + + // TODO statuses + // Bubble + + snapshot.entries_by_path.insert_or_replace(entry, &()); + } } else { if snapshot .entry_for_path(&path) .map(|entry| entry.is_ignored) .unwrap_or(false) { - self.remove_repo_path(&path, snapshot); return None; } let repo = snapshot.repository_for_path(&path)?; - let work_dir = repo.work_directory(snapshot)?; let work_dir_id = repo.work_directory.clone(); - let (local_repo, git_dir_scan_id) = snapshot.git_repositories.update(&work_dir_id, |entry| { - entry.work_dir_scan_id = scan_id; (entry.repo_ptr.clone(), entry.git_dir_scan_id) })?; @@ -3301,22 +3172,25 @@ impl BackgroundScanner { return None; } - let mut repository = snapshot.repository_entries.remove(&work_dir)?; - - for entry in snapshot.descendent_entries(false, false, path) { + for mut entry in snapshot + .descendent_entries(false, false, path) + .cloned() + .collect::>() + .into_iter() + { let Some(repo_path) = repo.work_directory.relativize(snapshot, &entry.path) else { continue; }; - let status = local_repo.lock().status(&repo_path); - if let Some(status) = status { - repository.statuses.insert(repo_path.clone(), status); - } else { - repository.statuses.remove(&repo_path); - } - } + let Some(status) = local_repo.lock().status(&repo_path) else { + continue; + }; - snapshot.repository_entries.insert(work_dir, repository) + entry.git_status = Some(status); + snapshot.entries_by_path.insert_or_replace(entry, &()); + // TODO statuses + // Bubble + } } Some(()) @@ -3831,6 +3705,7 @@ impl<'a> From<&'a Entry> for proto::Entry { mtime: Some(entry.mtime.into()), is_symlink: entry.is_symlink, is_ignored: entry.is_ignored, + git_status: entry.git_status.map(|status| status.to_proto()), } } } @@ -3856,6 +3731,7 @@ impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry { mtime: mtime.into(), is_symlink: entry.is_symlink, is_ignored: entry.is_ignored, + git_status: GitFileStatus::from_proto(entry.git_status), }) } else { Err(anyhow!( @@ -4911,14 +4787,14 @@ mod tests { cx.read(|cx| { let tree = tree.read(cx); - let (work_dir, repo) = tree.repositories().next().unwrap(); + let (work_dir, _) = tree.repositories().next().unwrap(); assert_eq!(work_dir.as_ref(), Path::new("projects/project1")); assert_eq!( - repo.status_for_file(tree, Path::new("projects/project1/a")), + tree.status_for_file(Path::new("projects/project1/a")), Some(GitFileStatus::Modified) ); assert_eq!( - repo.status_for_file(tree, Path::new("projects/project1/b")), + tree.status_for_file(Path::new("projects/project1/b")), Some(GitFileStatus::Added) ); }); @@ -4932,14 +4808,14 @@ mod tests { cx.read(|cx| { let tree = tree.read(cx); - let (work_dir, repo) = tree.repositories().next().unwrap(); + let (work_dir, _) = tree.repositories().next().unwrap(); assert_eq!(work_dir.as_ref(), Path::new("projects/project2")); assert_eq!( - repo.status_for_file(tree, Path::new("projects/project2/a")), + tree.status_for_file(Path::new("projects/project2/a")), Some(GitFileStatus::Modified) ); assert_eq!( - repo.status_for_file(tree, Path::new("projects/project2/b")), + tree.status_for_file(Path::new("projects/project2/b")), Some(GitFileStatus::Added) ); }); @@ -5067,6 +4943,7 @@ mod tests { }); } + // TODO: Stream statuses UPDATE THIS TO CHECK BUBBLIBG BEHAVIOR #[gpui::test] async fn test_git_status(cx: &mut TestAppContext) { const IGNORE_RULE: &'static str = "**/target"; @@ -5111,6 +4988,7 @@ mod tests { const F_TXT: &'static str = "f.txt"; const DOTGITIGNORE: &'static str = ".gitignore"; const BUILD_FILE: &'static str = "target/build_file"; + let project_path: &Path = &Path::new("project"); let work_dir = root.path().join("project"); let mut repo = git_init(work_dir.as_path()); @@ -5128,21 +5006,20 @@ mod tests { tree.read_with(cx, |tree, _cx| { let snapshot = tree.snapshot(); assert_eq!(snapshot.repository_entries.iter().count(), 1); - let (dir, repo) = snapshot.repository_entries.iter().next().unwrap(); + let (dir, _) = snapshot.repository_entries.iter().next().unwrap(); assert_eq!(dir.0.as_ref(), Path::new("project")); - assert_eq!(repo.statuses.iter().count(), 3); assert_eq!( - repo.statuses.get(&Path::new(A_TXT).into()), - Some(&GitFileStatus::Modified) + snapshot.status_for_file(project_path.join(A_TXT)), + Some(GitFileStatus::Modified) ); assert_eq!( - repo.statuses.get(&Path::new(B_TXT).into()), - Some(&GitFileStatus::Added) + snapshot.status_for_file(project_path.join(B_TXT)), + Some(GitFileStatus::Added) ); assert_eq!( - repo.statuses.get(&Path::new(F_TXT).into()), - Some(&GitFileStatus::Added) + snapshot.status_for_file(project_path.join(F_TXT)), + Some(GitFileStatus::Added) ); }); @@ -5154,12 +5031,10 @@ mod tests { // Check that repo only changes are tracked tree.read_with(cx, |tree, _cx| { let snapshot = tree.snapshot(); - let (_, repo) = snapshot.repository_entries.iter().next().unwrap(); - assert_eq!(repo.statuses.iter().count(), 1); assert_eq!( - repo.statuses.get(&Path::new(F_TXT).into()), - Some(&GitFileStatus::Added) + snapshot.status_for_file(project_path.join(F_TXT)), + Some(GitFileStatus::Added) ); }); @@ -5173,21 +5048,19 @@ mod tests { // Check that more complex repo changes are tracked tree.read_with(cx, |tree, _cx| { let snapshot = tree.snapshot(); - let (_, repo) = snapshot.repository_entries.iter().next().unwrap(); - assert_eq!(repo.statuses.iter().count(), 3); - assert_eq!(repo.statuses.get(&Path::new(A_TXT).into()), None); + assert_eq!(snapshot.status_for_file(project_path.join(A_TXT)), None); assert_eq!( - repo.statuses.get(&Path::new(B_TXT).into()), - Some(&GitFileStatus::Added) + snapshot.status_for_file(project_path.join(B_TXT)), + Some(GitFileStatus::Added) ); assert_eq!( - repo.statuses.get(&Path::new(E_TXT).into()), - Some(&GitFileStatus::Modified) + snapshot.status_for_file(project_path.join(E_TXT)), + Some(GitFileStatus::Modified) ); assert_eq!( - repo.statuses.get(&Path::new(F_TXT).into()), - Some(&GitFileStatus::Added) + snapshot.status_for_file(project_path.join(F_TXT)), + Some(GitFileStatus::Added) ); }); @@ -5204,14 +5077,6 @@ mod tests { tree.flush_fs_events(cx).await; - // Check that non-repo behavior is tracked - tree.read_with(cx, |tree, _cx| { - let snapshot = tree.snapshot(); - let (_, repo) = snapshot.repository_entries.iter().next().unwrap(); - - assert_eq!(repo.statuses.iter().count(), 0); - }); - let mut renamed_dir_name = "first_directory/second_directory"; const RENAMED_FILE: &'static str = "rf.txt"; @@ -5226,13 +5091,10 @@ mod tests { tree.read_with(cx, |tree, _cx| { let snapshot = tree.snapshot(); - let (_, repo) = snapshot.repository_entries.iter().next().unwrap(); - - assert_eq!(repo.statuses.iter().count(), 1); assert_eq!( - repo.statuses - .get(&Path::new(renamed_dir_name).join(RENAMED_FILE).into()), - Some(&GitFileStatus::Added) + snapshot + .status_for_file(&project_path.join(renamed_dir_name).join(RENAMED_FILE)), + Some(GitFileStatus::Added) ); }); @@ -5248,13 +5110,10 @@ mod tests { tree.read_with(cx, |tree, _cx| { let snapshot = tree.snapshot(); - let (_, repo) = snapshot.repository_entries.iter().next().unwrap(); - assert_eq!(repo.statuses.iter().count(), 1); assert_eq!( - repo.statuses - .get(&Path::new(renamed_dir_name).join(RENAMED_FILE).into()), - Some(&GitFileStatus::Added) + snapshot.status_for_file(Path::new(renamed_dir_name).join(RENAMED_FILE)), + Some(GitFileStatus::Added) ); }); } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 60af95174f..f6f3d67f3a 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1002,6 +1002,7 @@ impl ProjectPanel { mtime: entry.mtime, is_symlink: false, is_ignored: false, + git_status: entry.git_status, }); } if expanded_dir_ids.binary_search(&entry.id).is_err() @@ -1108,14 +1109,8 @@ impl ProjectPanel { .unwrap_or(&[]); let entry_range = range.start.saturating_sub(ix)..end_ix - ix; - for (entry, repo) in - snapshot.entries_with_repositories(visible_worktree_entries[entry_range].iter()) - { - let status = (git_status_setting - && entry.path.parent().is_some() - && !entry.is_ignored) - .then(|| repo.and_then(|repo| repo.status_for_path(&snapshot, &entry.path))) - .flatten(); + for entry in visible_worktree_entries[entry_range].iter() { + let status = git_status_setting.then(|| entry.git_status).flatten(); let mut details = EntryDetails { filename: entry diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 848cc1c2fa..001d503875 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -996,13 +996,12 @@ message Entry { Timestamp mtime = 5; bool is_symlink = 6; bool is_ignored = 7; + optional GitStatus git_status = 8; } message RepositoryEntry { uint64 work_directory_id = 1; optional string branch = 2; - repeated string removed_repo_paths = 3; - repeated StatusEntry updated_statuses = 4; } message StatusEntry { diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index b929de9596..bef6efa529 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 56; +pub const PROTOCOL_VERSION: u32 = 57;