diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs index e0ce6e2b41a6e5f4a71bf6a99c9c0d7ad255cdc9..af093c353a24029a8765ae19d8cfdea6c5c6efe6 100644 --- a/crates/git/src/git.rs +++ b/crates/git/src/git.rs @@ -20,10 +20,12 @@ pub use git2 as libgit; pub use repository::WORK_DIRECTORY_REPO_PATH; pub static DOT_GIT: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".git")); -pub static COOKIES: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new("cookies")); +pub static GITIGNORE: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".gitignore")); pub static FSMONITOR_DAEMON: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new("fsmonitor--daemon")); -pub static GITIGNORE: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".gitignore")); +pub static COMMIT_MESSAGE: LazyLock<&'static OsStr> = + LazyLock::new(|| OsStr::new("COMMIT_EDITMSG")); +pub static INDEX_LOCK: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new("index.lock")); actions!( git, diff --git a/crates/project/src/worktree_store.rs b/crates/project/src/worktree_store.rs index 49f6c9070d68572cba17a8151e52351d7b07562e..498b76659ff043c68682818390608a98907ee22a 100644 --- a/crates/project/src/worktree_store.rs +++ b/crates/project/src/worktree_store.rs @@ -56,6 +56,7 @@ pub struct WorktreeStore { state: WorktreeStoreState, } +#[derive(Debug)] pub enum WorktreeStoreEvent { WorktreeAdded(Entity), WorktreeRemoved(EntityId, WorktreeId), diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 2c3d75fd827e38a531cfe9c39a91ea61d0e10230..f41caf2100ad80bb729850d773f1748a70987a32 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -23,7 +23,7 @@ use git::{ status::{ FileStatus, GitSummary, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode, }, - GitHostingProviderRegistry, COOKIES, DOT_GIT, FSMONITOR_DAEMON, GITIGNORE, + GitHostingProviderRegistry, COMMIT_MESSAGE, DOT_GIT, FSMONITOR_DAEMON, GITIGNORE, INDEX_LOCK, }; use gpui::{ App, AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, @@ -4317,47 +4317,41 @@ impl BackgroundScanner { } }; + // Certain directories may have FS changes, but do not lead to git data changes that Zed cares about. + // Ignore these, to avoid Zed unnecessarily rescanning git metadata. + let skipped_files_in_dot_git = HashSet::from_iter([*COMMIT_MESSAGE, *INDEX_LOCK]); + let skipped_dirs_in_dot_git = [*FSMONITOR_DAEMON]; + let mut relative_paths = Vec::with_capacity(abs_paths.len()); let mut dot_git_abs_paths = Vec::new(); abs_paths.sort_unstable(); abs_paths.dedup_by(|a, b| a.starts_with(b)); abs_paths.retain(|abs_path| { let abs_path = SanitizedPath::from(abs_path); + let snapshot = &self.state.lock().snapshot; { let mut is_git_related = false; - // We don't want to trigger .git rescan for events within .git/fsmonitor--daemon/cookies directory. - #[derive(PartialEq)] - enum FsMonitorParseState { - Cookies, - FsMonitor - } - let mut fsmonitor_parse_state = None; - if let Some(dot_git_abs_path) = abs_path.as_path() - .ancestors() - .find(|ancestor| { - let file_name = ancestor.file_name(); - if file_name == Some(*COOKIES) { - fsmonitor_parse_state = Some(FsMonitorParseState::Cookies); - false - } else if fsmonitor_parse_state == Some(FsMonitorParseState::Cookies) && file_name == Some(*FSMONITOR_DAEMON) { - fsmonitor_parse_state = Some(FsMonitorParseState::FsMonitor); - false - } else if fsmonitor_parse_state != Some(FsMonitorParseState::FsMonitor) && smol::block_on(is_git_dir(ancestor, self.fs.as_ref())) { - true - } else { - fsmonitor_parse_state.take(); - false - } + let dot_git_paths = abs_path.as_path().ancestors().find_map(|ancestor| { + if smol::block_on(is_git_dir(ancestor, self.fs.as_ref())) { + let path_in_git_dir = abs_path.as_path().strip_prefix(ancestor).expect("stripping off the ancestor"); + Some((ancestor.to_owned(), path_in_git_dir.to_owned())) + } else { + None + } + }); - }) - { - let dot_git_abs_path = dot_git_abs_path.to_path_buf(); + if let Some((dot_git_abs_path, path_in_git_dir)) = dot_git_paths { + if skipped_files_in_dot_git.contains(path_in_git_dir.as_os_str()) || skipped_dirs_in_dot_git.iter().any(|skipped_git_subdir| path_in_git_dir.starts_with(skipped_git_subdir)) { + log::debug!("ignoring event {abs_path:?} as it's in the .git directory among skipped files or directories"); + return false; + } + + is_git_related = true; if !dot_git_abs_paths.contains(&dot_git_abs_path) { dot_git_abs_paths.push(dot_git_abs_path); } - is_git_related = true; } let relative_path: Arc =