diff --git a/crates/worktree/src/ignore.rs b/crates/worktree/src/ignore.rs index a239520ff9..17c362e2d7 100644 --- a/crates/worktree/src/ignore.rs +++ b/crates/worktree/src/ignore.rs @@ -1,42 +1,60 @@ use ignore::gitignore::Gitignore; use std::{ffi::OsStr, path::Path, sync::Arc}; +#[derive(Clone, Debug)] +pub struct IgnoreStack { + pub repo_root: Option>, + pub top: Arc, +} + #[derive(Debug)] -pub enum IgnoreStack { +pub enum IgnoreStackEntry { None, Global { - repo_root: Option>, ignore: Arc, }, Some { abs_base_path: Arc, ignore: Arc, - parent: Arc, + parent: Arc, }, All, } impl IgnoreStack { - pub fn none() -> Arc { - Arc::new(Self::None) + pub fn none() -> Self { + Self { + repo_root: None, + top: Arc::new(IgnoreStackEntry::None), + } } - pub fn all() -> Arc { - Arc::new(Self::All) + pub fn all() -> Self { + Self { + repo_root: None, + top: Arc::new(IgnoreStackEntry::All), + } } - pub fn global(repo_root: Option>, ignore: Arc) -> Arc { - Arc::new(Self::Global { repo_root, ignore }) + pub fn global(ignore: Arc) -> Self { + Self { + repo_root: None, + top: Arc::new(IgnoreStackEntry::Global { ignore }), + } } - pub fn append(self: Arc, abs_base_path: Arc, ignore: Arc) -> Arc { - match self.as_ref() { - IgnoreStack::All => self, - _ => Arc::new(Self::Some { + pub fn append(self, abs_base_path: Arc, ignore: Arc) -> Self { + let top = match self.top.as_ref() { + IgnoreStackEntry::All => self.top.clone(), + _ => Arc::new(IgnoreStackEntry::Some { abs_base_path, ignore, - parent: self, + parent: self.top.clone(), }), + }; + Self { + repo_root: self.repo_root, + top, } } @@ -45,12 +63,12 @@ impl IgnoreStack { return true; } - match self { - Self::None => false, - Self::All => true, - Self::Global { repo_root, ignore } => { + match self.top.as_ref() { + IgnoreStackEntry::None => false, + IgnoreStackEntry::All => true, + IgnoreStackEntry::Global { ignore } => { let combined_path; - let abs_path = if let Some(repo_root) = repo_root { + let abs_path = if let Some(repo_root) = self.repo_root.as_ref() { combined_path = ignore.path().join( abs_path .strip_prefix(repo_root) @@ -66,12 +84,16 @@ impl IgnoreStack { ignore::Match::Whitelist(_) => false, } } - Self::Some { + IgnoreStackEntry::Some { abs_base_path, ignore, parent: prev, } => match ignore.matched(abs_path.strip_prefix(abs_base_path).unwrap(), is_dir) { - ignore::Match::None => prev.is_abs_path_ignored(abs_path, is_dir), + ignore::Match::None => IgnoreStack { + repo_root: self.repo_root.clone(), + top: prev.clone(), + } + .is_abs_path_ignored(abs_path, is_dir), ignore::Match::Ignore(_) => true, ignore::Match::Whitelist(_) => false, }, diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index b4a9729972..34ffc92f40 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -2777,12 +2777,7 @@ impl LocalSnapshot { inodes } - fn ignore_stack_for_abs_path( - &self, - abs_path: &Path, - is_dir: bool, - fs: &dyn Fs, - ) -> Arc { + fn ignore_stack_for_abs_path(&self, abs_path: &Path, is_dir: bool, fs: &dyn Fs) -> IgnoreStack { let mut new_ignores = Vec::new(); let mut repo_root = None; for (index, ancestor) in abs_path.ancestors().enumerate() { @@ -2803,10 +2798,11 @@ impl LocalSnapshot { } let mut ignore_stack = if let Some(global_gitignore) = self.global_gitignore.clone() { - IgnoreStack::global(repo_root, global_gitignore) + IgnoreStack::global(global_gitignore) } else { IgnoreStack::none() }; + ignore_stack.repo_root = repo_root; for (parent_abs_path, ignore) in new_ignores.into_iter().rev() { if ignore_stack.is_abs_path_ignored(parent_abs_path, true) { ignore_stack = IgnoreStack::all(); @@ -4369,12 +4365,14 @@ impl BackgroundScanner { swap_to_front(&mut child_paths, *GITIGNORE); swap_to_front(&mut child_paths, *DOT_GIT); + let mut repo_root = None; for child_abs_path in child_paths { let child_abs_path: Arc = child_abs_path.into(); let child_name = child_abs_path.file_name().unwrap(); let child_path: Arc = job.path.join(child_name).into(); if child_name == *DOT_GIT { + repo_root = Some(child_abs_path.clone()); let mut state = self.state.lock(); state.insert_git_repository( child_path.clone(), @@ -4386,6 +4384,9 @@ impl BackgroundScanner { Ok(ignore) => { let ignore = Arc::new(ignore); ignore_stack = ignore_stack.append(job.abs_path.clone(), ignore.clone()); + if let Some(repo_root) = repo_root.clone() { + ignore_stack.repo_root = Some(repo_root); + } new_ignore = Some(ignore); } Err(error) => { @@ -4688,7 +4689,7 @@ impl BackgroundScanner { &self, scan_job_tx: Sender, prev_snapshot: LocalSnapshot, - mut ignores_to_update: impl Iterator, Arc)>, + mut ignores_to_update: impl Iterator, IgnoreStack)>, ) { let (ignore_queue_tx, ignore_queue_rx) = channel::unbounded(); { @@ -5147,7 +5148,7 @@ fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag { struct ScanJob { abs_path: Arc, path: Arc, - ignore_stack: Arc, + ignore_stack: IgnoreStack, scan_queue: Sender, ancestor_inodes: TreeSet, is_external: bool, @@ -5155,7 +5156,7 @@ struct ScanJob { struct UpdateIgnoreStatusJob { abs_path: Arc, - ignore_stack: Arc, + ignore_stack: IgnoreStack, ignore_queue: Sender, scan_queue: Sender, }