From 71b2126eca51cd3a5c9796a86aad6800a33e9184 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 28 Sep 2022 11:42:22 -0700 Subject: [PATCH] WIP, re-doing fs and fake git repos --- Cargo.lock | 1 + crates/git/Cargo.toml | 2 + crates/git/src/repository.rs | 123 ++++++++++++++++++++++++++++----- crates/language/src/buffer.rs | 1 - crates/project/src/fs.rs | 10 +++ crates/project/src/project.rs | 8 +-- crates/project/src/worktree.rs | 50 +++++++------- 7 files changed, 145 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8918158be..3c87f336de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2230,6 +2230,7 @@ name = "git" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "clock", "git2", "lazy_static", diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 79ac56d098..7ef9a953ba 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -17,6 +17,8 @@ util = { path = "../util" } log = { version = "0.4.16", features = ["kv_unstable_serde"] } smol = "1.2" parking_lot = "0.11.1" +async-trait = "0.1" + [dev-dependencies] unindent = "0.1.7" diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index a38d13ef0d..19ba0d1238 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -4,8 +4,29 @@ use parking_lot::Mutex; use std::{path::Path, sync::Arc}; use util::ResultExt; +#[async_trait::async_trait] +pub trait GitRepository: Send + Sync + std::fmt::Debug { + fn manages(&self, path: &Path) -> bool; + + fn in_dot_git(&self, path: &Path) -> bool; + + fn content_path(&self) -> &Path; + + fn git_dir_path(&self) -> &Path; + + fn scan_id(&self) -> usize; + + fn set_scan_id(&mut self, scan_id: usize); + + fn git_repo(&self) -> Arc>; + + fn boxed_clone(&self) -> Box; + + async fn load_head_text(&self, relative_file_path: &Path) -> Option; +} + #[derive(Clone)] -pub struct GitRepository { +pub struct RealGitRepository { // Path to folder containing the .git file or directory content_path: Arc, // Path to the actual .git folder. @@ -15,50 +36,48 @@ pub struct GitRepository { libgit_repository: Arc>, } -impl GitRepository { - pub fn open(dotgit_path: &Path) -> Option { +impl RealGitRepository { + pub fn open(dotgit_path: &Path) -> Option> { LibGitRepository::open(&dotgit_path) .log_err() - .and_then(|libgit_repository| { - Some(Self { + .and_then::, _>(|libgit_repository| { + Some(Box::new(Self { content_path: libgit_repository.workdir()?.into(), git_dir_path: dotgit_path.canonicalize().log_err()?.into(), scan_id: 0, libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)), - }) + })) }) } +} - pub fn manages(&self, path: &Path) -> bool { +#[async_trait::async_trait] +impl GitRepository for RealGitRepository { + fn manages(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.content_path)) .unwrap_or(false) } - pub fn in_dot_git(&self, path: &Path) -> bool { + fn in_dot_git(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.git_dir_path)) .unwrap_or(false) } - pub fn content_path(&self) -> &Path { + fn content_path(&self) -> &Path { self.content_path.as_ref() } - pub fn git_dir_path(&self) -> &Path { + fn git_dir_path(&self) -> &Path { self.git_dir_path.as_ref() } - pub fn scan_id(&self) -> usize { + fn scan_id(&self) -> usize { self.scan_id } - pub fn set_scan_id(&mut self, scan_id: usize) { - println!("setting scan id"); - self.scan_id = scan_id; - } - - pub async fn load_head_text(&self, relative_file_path: &Path) -> Option { + async fn load_head_text(&self, relative_file_path: &Path) -> Option { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { let object = repo .head()? @@ -81,9 +100,21 @@ impl GitRepository { } None } + + fn git_repo(&self) -> Arc> { + self.libgit_repository.clone() + } + + fn set_scan_id(&mut self, scan_id: usize) { + self.scan_id = scan_id; + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } } -impl std::fmt::Debug for GitRepository { +impl std::fmt::Debug for RealGitRepository { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("GitRepository") .field("content_path", &self.content_path) @@ -93,3 +124,59 @@ impl std::fmt::Debug for GitRepository { .finish() } } + +#[derive(Debug, Clone)] +pub struct FakeGitRepository { + content_path: Arc, + git_dir_path: Arc, + scan_id: usize, +} + +impl FakeGitRepository { + pub fn open(dotgit_path: &Path, scan_id: usize) -> Box { + Box::new(FakeGitRepository { + content_path: dotgit_path.parent().unwrap().into(), + git_dir_path: dotgit_path.into(), + scan_id, + }) + } +} + +#[async_trait::async_trait] +impl GitRepository for FakeGitRepository { + fn manages(&self, path: &Path) -> bool { + path.starts_with(self.content_path()) + } + + fn in_dot_git(&self, path: &Path) -> bool { + path.starts_with(self.git_dir_path()) + } + + fn content_path(&self) -> &Path { + &self.content_path + } + + fn git_dir_path(&self) -> &Path { + &self.git_dir_path + } + + fn scan_id(&self) -> usize { + self.scan_id + } + + async fn load_head_text(&self, _: &Path) -> Option { + unimplemented!() + } + + fn git_repo(&self) -> Arc> { + unimplemented!() + } + + fn set_scan_id(&mut self, scan_id: usize) { + self.scan_id = scan_id; + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } +} diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 13fe6daed5..0268f1cc68 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -672,7 +672,6 @@ impl Buffer { } pub fn git_diff_recalc(&mut self, cx: &mut ModelContext) { - println!("recalc"); if self.git_diff_status.update_in_progress { self.git_diff_status.update_requested = true; return; diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 6a496910a0..4b27a23856 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; +use git::repository::{FakeGitRepository, GitRepository, RealGitRepository}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ @@ -43,6 +44,7 @@ pub trait Fs: Send + Sync { path: &Path, latency: Duration, ) -> Pin>>>; + async fn open_repo(&self, abs_dot_git: &Path) -> Option>; fn is_fake(&self) -> bool; #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> &FakeFs; @@ -236,6 +238,10 @@ impl Fs for RealFs { }))) } + fn open_repo(&self, abs_dot_git: &Path) -> Option> { + RealGitRepository::open(&abs_dot_git) + } + fn is_fake(&self) -> bool { false } @@ -847,6 +853,10 @@ impl Fs for FakeFs { })) } + fn open_repo(&self, abs_dot_git: &Path) -> Option> { + Some(FakeGitRepository::open(abs_dot_git.into(), 0)) + } + fn is_fake(&self) -> bool { true } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 57af588c68..a2a49c9c93 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4537,7 +4537,6 @@ impl Project { cx.subscribe(worktree, |this, worktree, event, cx| match event { worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx), worktree::Event::UpdatedGitRepositories(updated_repos) => { - println!("{updated_repos:#?}"); this.update_local_worktree_buffers_git_repos(updated_repos, cx) } }) @@ -4649,7 +4648,7 @@ impl Project { fn update_local_worktree_buffers_git_repos( &mut self, - repos: &[GitRepository], + repos: &[Box], cx: &mut ModelContext, ) { //TODO: Produce protos @@ -4662,18 +4661,15 @@ impl Project { }; let path = file.path().clone(); let abs_path = file.abs_path(cx); - println!("got file"); let repo = match repos.iter().find(|repo| repo.manages(&abs_path)) { - Some(repo) => repo.clone(), + Some(repo) => repo.boxed_clone(), None => return, }; - println!("got repo"); cx.spawn(|_, mut cx| async move { let head_text = repo.load_head_text(&path).await; buffer.update(&mut cx, |buffer, cx| { - println!("got calling update"); buffer.update_head_text(head_text, cx); }); }) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 7fd37dc016..ae55659f98 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -100,7 +100,7 @@ pub struct Snapshot { pub struct LocalSnapshot { abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, - git_repositories: Vec, + git_repositories: Vec>, removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, @@ -115,7 +115,7 @@ impl Clone for LocalSnapshot { git_repositories: self .git_repositories .iter() - .map(|repo| repo.clone()) + .map(|repo| repo.boxed_clone()) .collect(), removed_entry_ids: self.removed_entry_ids.clone(), next_entry_id: self.next_entry_id.clone(), @@ -157,7 +157,7 @@ struct ShareState { pub enum Event { UpdatedEntries, - UpdatedGitRepositories(Vec), + UpdatedGitRepositories(Vec>), } impl Entity for Worktree { @@ -581,16 +581,13 @@ impl LocalWorktree { } fn list_updated_repos( - old_repos: &[GitRepository], - new_repos: &[GitRepository], - ) -> Vec { - println!("old repos: {:#?}", old_repos); - println!("new repos: {:#?}", new_repos); - + old_repos: &[Box], + new_repos: &[Box], + ) -> Vec> { fn diff<'a>( - a: &'a [GitRepository], - b: &'a [GitRepository], - updated: &mut HashMap<&'a Path, GitRepository>, + a: &'a [Box], + b: &'a [Box], + updated: &mut HashMap<&'a Path, Box>, ) { for a_repo in a { let matched = b.iter().find(|b_repo| { @@ -599,17 +596,17 @@ impl LocalWorktree { }); if matched.is_some() { - updated.insert(a_repo.git_dir_path(), a_repo.clone()); + updated.insert(a_repo.git_dir_path(), a_repo.boxed_clone()); } } } - let mut updated = HashMap::<&Path, GitRepository>::default(); + let mut updated = HashMap::<&Path, Box>::default(); diff(old_repos, new_repos, &mut updated); diff(new_repos, old_repos, &mut updated); - dbg!(updated.into_values().collect()) + updated.into_values().collect() } pub fn scan_complete(&self) -> impl Future { @@ -1364,23 +1361,22 @@ impl LocalSnapshot { } // Gives the most specific git repository for a given path - pub(crate) fn repo_for(&self, path: &Path) -> Option { + pub(crate) fn repo_for(&self, path: &Path) -> Option> { self.git_repositories .iter() .rev() //git_repository is ordered lexicographically .find(|repo| repo.manages(&self.abs_path.join(path))) - .map(|repo| repo.clone()) + .map(|repo| repo.boxed_clone()) } - pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut GitRepository> { - println!("chechking {path:?}"); + pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut Box> { self.git_repositories .iter_mut() .rev() //git_repository is ordered lexicographically .find(|repo| repo.in_dot_git(&self.abs_path.join(path))) } - pub(crate) fn tracks_filepath(&self, repo: &GitRepository, file_path: &Path) -> bool { + pub(crate) fn _tracks_filepath(&self, repo: &dyn GitRepository, file_path: &Path) -> bool { // Depends on git_repository_for_file_path returning the most specific git repository for a given path self.repo_for(&self.abs_path.join(file_path)) .map_or(false, |r| r.git_dir_path() == repo.git_dir_path()) @@ -1522,6 +1518,7 @@ impl LocalSnapshot { parent_path: Arc, entries: impl IntoIterator, ignore: Option>, + fs: &dyn Fs, ) { let mut parent_entry = if let Some(parent_entry) = self.entries_by_path.get(&PathKey(parent_path.clone()), &()) @@ -1553,7 +1550,7 @@ impl LocalSnapshot { .git_repositories .binary_search_by_key(&abs_path.as_path(), |repo| repo.git_dir_path()) { - if let Some(repository) = GitRepository::open(&abs_path) { + if let Some(repository) = fs.open_repo(abs_path.as_path()) { self.git_repositories.insert(ix, repository); } } @@ -2402,9 +2399,12 @@ impl BackgroundScanner { new_entries.push(child_entry); } - self.snapshot - .lock() - .populate_dir(job.path.clone(), new_entries, new_ignore); + self.snapshot.lock().populate_dir( + job.path.clone(), + new_entries, + new_ignore, + self.fs.as_ref(), + ); for new_job in new_jobs { job.scan_queue.send(new_job).await.unwrap(); } @@ -2602,7 +2602,7 @@ impl BackgroundScanner { let new_repos = snapshot .git_repositories .iter() - .cloned() + .map(|repo| repo.boxed_clone()) .filter(|repo| git::libgit::Repository::open(repo.git_dir_path()).is_ok()) .collect();