From 4717ce1da380c4b17c25dc637730d393c36b8a50 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 12:55:31 -0700 Subject: [PATCH] WIP: Move statuses to entries co-authored-by: julia --- crates/project/src/worktree.rs | 60 ++++++++++++++++++++++------------ crates/rpc/src/proto.rs | 5 +++ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 678b34ebed32ca07519ade5a7b90c7022112e9b0..3f4df1328b90b2b31b6f928c36875aa2f3cfb957 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1474,8 +1474,9 @@ impl Snapshot { #[cfg(any(test, feature = "test-support"))] pub fn status_for_file(&self, path: impl Into) -> Option { + let path = path.into(); self.entries_by_path - .get(&PathKey(Arc::from(path.into())), &()) + .get(&PathKey(Arc::from(path)), &()) .and_then(|entry| entry.git_status) } @@ -1929,25 +1930,25 @@ impl LocalSnapshot { if self.git_repositories.get(&work_dir_id).is_none() { let repo = fs.open_repo(abs_path.as_path())?; let work_directory = RepositoryWorkDirectory(work_dir.clone()); - let scan_id = self.scan_id; let repo_lock = repo.lock(); self.repository_entries.insert( - work_directory, + work_directory.clone(), RepositoryEntry { work_directory: work_dir_id.into(), branch: repo_lock.branch_name().map(Into::into), - // TODO: statuses - // statuses: repo_lock.statuses().unwrap_or_default(), }, ); + + self.scan_statuses(repo_lock.deref(), &work_directory); + drop(repo_lock); self.git_repositories.insert( work_dir_id, LocalRepositoryEntry { - git_dir_scan_id: scan_id, + git_dir_scan_id: 0, repo_ptr: repo, git_dir_path: parent_path.clone(), }, @@ -1957,6 +1958,23 @@ impl LocalSnapshot { Some(()) } + fn scan_statuses(&mut self, repo_ptr: &dyn GitRepository, work_directory: &RepositoryWorkDirectory) { + let statuses = repo_ptr.statuses().unwrap_or_default(); + for (repo_path, status) in statuses.iter() { + let Some(entry) = self.entry_for_path(&work_directory.0.join(repo_path)) else { + continue; + }; + + let mut entry = entry.clone(); + entry.git_status = Some(*status); + + // TODO statuses + // Bubble + + self.entries_by_path.insert_or_replace(entry, &()); + } + } + fn ancestor_inodes_for_path(&self, path: &Path) -> TreeSet { let mut inodes = TreeSet::default(); for ancestor in path.ancestors().skip(1) { @@ -3114,8 +3132,15 @@ impl BackgroundScanner { if repo.git_dir_scan_id == scan_id { return None; } + (*entry_id, repo.repo_ptr.to_owned()) }; + /* + 1. Populate dir, initializes the git repo + 2. Sometimes, we get a file event inside the .git repo, before it's initializaed + In both cases, we should end up with an initialized repo and a full status scan + + */ let work_dir = snapshot .entry_for_id(entry_id) @@ -3136,20 +3161,8 @@ impl BackgroundScanner { 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, &()); - } + snapshot.scan_statuses(repo.deref(), &work_dir); } else { if snapshot .entry_for_path(&path) @@ -4945,7 +4958,7 @@ mod tests { // TODO: Stream statuses UPDATE THIS TO CHECK BUBBLIBG BEHAVIOR #[gpui::test] - async fn test_git_status(cx: &mut TestAppContext) { + async fn test_git_status(deterministic: Arc, cx: &mut TestAppContext) { const IGNORE_RULE: &'static str = "**/target"; let root = temp_tree(json!({ @@ -4982,6 +4995,7 @@ mod tests { cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) .await; + const A_TXT: &'static str = "a.txt"; const B_TXT: &'static str = "b.txt"; const E_TXT: &'static str = "c/d/e.txt"; @@ -5001,6 +5015,7 @@ mod tests { std::fs::write(work_dir.join(A_TXT), "aa").unwrap(); tree.flush_fs_events(cx).await; + deterministic.run_until_parked(); // Check that the right git state is observed on startup tree.read_with(cx, |tree, _cx| { @@ -5027,6 +5042,7 @@ mod tests { git_add(Path::new(B_TXT), &repo); git_commit("Committing modified and added", &repo); tree.flush_fs_events(cx).await; + deterministic.run_until_parked(); // Check that repo only changes are tracked tree.read_with(cx, |tree, _cx| { @@ -5044,6 +5060,7 @@ mod tests { std::fs::write(work_dir.join(E_TXT), "eeee").unwrap(); std::fs::write(work_dir.join(BUILD_FILE), "this should be ignored").unwrap(); tree.flush_fs_events(cx).await; + deterministic.run_until_parked(); // Check that more complex repo changes are tracked tree.read_with(cx, |tree, _cx| { @@ -5076,6 +5093,7 @@ mod tests { git_commit("Committing modified git ignore", &repo); tree.flush_fs_events(cx).await; + deterministic.run_until_parked(); let mut renamed_dir_name = "first_directory/second_directory"; const RENAMED_FILE: &'static str = "rf.txt"; @@ -5088,6 +5106,7 @@ mod tests { .unwrap(); tree.flush_fs_events(cx).await; + deterministic.run_until_parked(); tree.read_with(cx, |tree, _cx| { let snapshot = tree.snapshot(); @@ -5107,6 +5126,7 @@ mod tests { .unwrap(); tree.flush_fs_events(cx).await; + deterministic.run_until_parked(); tree.read_with(cx, |tree, _cx| { let snapshot = tree.snapshot(); diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 07925a0486e6d210ceac44a7c5a5428b3f48a58e..814b40403febbd4c4deaafc273749f469a042d9f 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -497,6 +497,11 @@ pub fn split_worktree_update( .map(|repo| (repo.work_directory_id, repo)) .collect::>(); + // Maintain a list of inflight repositories + // Every time we send a repository's work directory, stick it in the list of in-flight repositories + // Every go of the loop, drain each in-flight repository's statuses + // Until we have no more data + iter::from_fn(move || { if done_files { return None;