diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs index 73f7130e56da9fbcfc2f2210a70f90ecf1f87f46..eab031da17c0e8c84464c676de46d2d03eaeadf2 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/project/src/git_repository.rs @@ -10,7 +10,7 @@ pub struct GitRepository { // Path to the actual .git folder. // Note: if .git is a file, this points to the folder indicated by the .git file git_dir_path: Arc, - last_scan_id: usize, + scan_id: usize, libgit_repository: Arc>, } @@ -22,19 +22,19 @@ impl GitRepository { Some(Self { content_path: libgit_repository.workdir()?.into(), git_dir_path: dotgit_path.canonicalize().log_err()?.into(), - last_scan_id: 0, + scan_id: 0, libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)), }) }) } - pub fn is_path_managed_by(&self, path: &Path) -> bool { + pub fn manages(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.content_path)) .unwrap_or(false) } - pub fn is_path_in_git_folder(&self, path: &Path) -> bool { + pub fn in_dot_git(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.git_dir_path)) .unwrap_or(false) @@ -48,12 +48,12 @@ impl GitRepository { self.git_dir_path.as_ref() } - pub fn last_scan_id(&self) -> usize { - self.last_scan_id + pub fn scan_id(&self) -> usize { + self.scan_id } - pub fn set_scan_id(&mut self, scan_id: usize) { - self.last_scan_id = scan_id; + pub(super) fn set_scan_id(&mut self, scan_id: usize) { + self.scan_id = scan_id; } pub fn with_repo(&mut self, f: F) { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index a9ebfd8612944b7f2315bf98b4d0719f0041be07..aead63102b218cd376612ac9942998a39e083453 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -40,7 +40,6 @@ use std::{ ffi::{OsStr, OsString}, fmt, future::Future, - mem, ops::{Deref, DerefMut}, os::unix::prelude::{OsStrExt, OsStringExt}, path::{Path, PathBuf}, @@ -1307,32 +1306,24 @@ impl LocalSnapshot { } // Gives the most specific git repository for a given path - pub(crate) fn git_repository_for_file_path(&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.is_path_managed_by(&self.abs_path.join(path))) + .find(|repo| repo.manages(&self.abs_path.join(path))) .map(|repo| repo.clone()) } - // ~/zed: - // - src - // - crates - // - .git -> /usr/.git - pub(crate) fn git_repository_for_git_data(&self, path: &Path) -> Option { + pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut GitRepository> { self.git_repositories - .iter() - .find(|repo| repo.is_path_in_git_folder(&self.abs_path.join(path))) - .map(|repo| repo.clone()) + .iter_mut() + .rev() //git_repository is ordered lexicographically + .find(|repo| repo.in_dot_git(&self.abs_path.join(path))) } - pub(crate) fn does_git_repository_track_file_path( - &self, - repo: &GitRepository, - file_path: &Path, - ) -> bool { + pub(crate) fn tracks_filepath(&self, repo: &GitRepository, file_path: &Path) -> bool { // Depends on git_repository_for_file_path returning the most specific git repository for a given path - self.git_repository_for_file_path(&self.abs_path.join(file_path)) + self.repo_for(&self.abs_path.join(file_path)) .map_or(false, |r| r.git_dir_path() == repo.git_dir_path()) } @@ -2433,6 +2424,11 @@ impl BackgroundScanner { fs_entry.is_ignored = ignore_stack.is_all(); snapshot.insert_entry(fs_entry, self.fs.as_ref()); + let scan_id = snapshot.scan_id; + if let Some(repo) = snapshot.in_dot_git(&abs_path) { + repo.set_scan_id(scan_id); + } + let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path); if metadata.is_dir && !ancestor_inodes.contains(&metadata.inode) { ancestor_inodes.insert(metadata.inode); @@ -3172,13 +3168,9 @@ mod tests { tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); - assert!(tree - .git_repository_for_file_path("c.txt".as_ref()) - .is_none()); + assert!(tree.repo_for("c.txt".as_ref()).is_none()); - let repo = tree - .git_repository_for_file_path("dir1/src/b.txt".as_ref()) - .unwrap(); + let repo = tree.repo_for("dir1/src/b.txt".as_ref()).unwrap(); assert_eq!( repo.content_path(), @@ -3189,9 +3181,7 @@ mod tests { root.path().join("dir1/.git").canonicalize().unwrap() ); - let repo = tree - .git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()) - .unwrap(); + let repo = tree.repo_for("dir1/deps/dep1/src/a.txt".as_ref()).unwrap(); assert_eq!( repo.content_path(), @@ -3204,23 +3194,23 @@ mod tests { .canonicalize() .unwrap() ); + }); - let repo = tree - .git_repository_for_git_data("dir1/.git/HEAD".as_ref()) - .unwrap(); + let original_scan_id = tree.read_with(cx, |tree, _cx| { + let tree = tree.as_local().unwrap(); + tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id() + }); - assert_eq!( - repo.content_path(), - root.path().join("dir1").canonicalize().unwrap() - ); - assert_eq!( - repo.git_dir_path(), - root.path().join("dir1/.git").canonicalize().unwrap() - ); + std::fs::write(root.path().join("dir1/.git/random_new_file"), "hello").unwrap(); + tree.flush_fs_events(cx).await; - assert!(tree.does_git_repository_track_file_path(&repo, "dir1/src/b.txt".as_ref())); - assert!(!tree - .does_git_repository_track_file_path(&repo, "dir1/deps/dep1/src/a.txt".as_ref())); + tree.read_with(cx, |tree, _cx| { + let tree = tree.as_local().unwrap(); + let new_scan_id = tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id(); + assert_ne!( + original_scan_id, new_scan_id, + "original {original_scan_id}, new {new_scan_id}" + ); }); std::fs::remove_dir_all(root.path().join("dir1/.git")).unwrap(); @@ -3229,9 +3219,7 @@ mod tests { tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); - assert!(tree - .git_repository_for_file_path("dir1/src/b.txt".as_ref()) - .is_none()); + assert!(tree.repo_for("dir1/src/b.txt".as_ref()).is_none()); }); }