git: Do not rescan .git on fsmonitor events (#18326)

Piotr Osiewicz created

Fixes #16404 by ignoring events coming from
.git/fsmonitor--daemon/cookies subdirectory.

Closes #16404

Release Notes:

- Improved performance in repositories using Git fsmonitor--daemon
feature.

Change summary

crates/git/src/git.rs           |  3 +++
crates/worktree/src/worktree.rs | 27 +++++++++++++++++++++++++--
2 files changed, 28 insertions(+), 2 deletions(-)

Detailed changes

crates/git/src/git.rs 🔗

@@ -18,6 +18,9 @@ pub mod repository;
 pub mod status;
 
 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 FSMONITOR_DAEMON: LazyLock<&'static OsStr> =
+    LazyLock::new(|| OsStr::new("fsmonitor--daemon"));
 pub static GITIGNORE: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".gitignore"));
 
 #[derive(Clone, Copy, Eq, Hash, PartialEq)]

crates/worktree/src/worktree.rs 🔗

@@ -22,7 +22,7 @@ use fuzzy::CharBag;
 use git::{
     repository::{GitFileStatus, GitRepository, RepoPath},
     status::GitStatus,
-    DOT_GIT, GITIGNORE,
+    COOKIES, DOT_GIT, FSMONITOR_DAEMON, GITIGNORE,
 };
 use gpui::{
     AppContext, AsyncAppContext, BackgroundExecutor, Context, EventEmitter, Model, ModelContext,
@@ -3707,9 +3707,32 @@ impl BackgroundScanner {
             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_dir) = abs_path
                     .ancestors()
-                    .find(|ancestor| ancestor.file_name() == Some(*DOT_GIT))
+                    .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) && file_name == Some(*DOT_GIT) {
+                            true
+                        } else {
+                            fsmonitor_parse_state.take();
+                            false
+                        }
+
+                    })
                 {
                     let dot_git_path = dot_git_dir
                         .strip_prefix(&root_canonical_path)