diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index c81d9d5637e4d6372bf3be83daa185f0dd48c10e..5f8739fd88a87ebbf3af2ecf516e97afb613cea4 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -2046,24 +2046,41 @@ impl BackgroundScanner { async fn scan_dirs(&mut self) -> Result<()> { let root_char_bag; + let root_abs_path; let next_entry_id; let is_dir; { let snapshot = self.snapshot.lock(); root_char_bag = snapshot.root_char_bag; + root_abs_path = snapshot.abs_path.clone(); next_entry_id = snapshot.next_entry_id.clone(); is_dir = snapshot.root_entry().map_or(false, |e| e.is_dir()) }; + // Populate ignores above the root. + for ancestor in root_abs_path.ancestors().skip(1) { + if let Ok(ignore) = build_gitignore(&ancestor.join(&*GITIGNORE), self.fs.as_ref()).await + { + self.snapshot + .lock() + .ignores_by_abs_path + .insert(ancestor.into(), (ignore.into(), 0)); + } + } + if is_dir { let path: Arc = Arc::from(Path::new("")); - let abs_path = self.abs_path(); + let ignore_stack = self + .snapshot + .lock() + .ignore_stack_for_abs_path(&root_abs_path, true); + let (tx, rx) = channel::unbounded(); self.executor .block(tx.send(ScanJob { - abs_path: abs_path.to_path_buf(), + abs_path: root_abs_path.to_path_buf(), path, - ignore_stack: IgnoreStack::none(), + ignore_stack, scan_queue: tx.clone(), })) .unwrap(); @@ -2313,14 +2330,15 @@ impl BackgroundScanner { let mut ignores_to_update = Vec::new(); let mut ignores_to_delete = Vec::new(); for (parent_abs_path, (_, scan_id)) in &snapshot.ignores_by_abs_path { - let parent_path = parent_abs_path.strip_prefix(&snapshot.abs_path).unwrap(); - if *scan_id == snapshot.scan_id && snapshot.entry_for_path(parent_path).is_some() { - ignores_to_update.push(parent_abs_path.clone()); - } + if let Ok(parent_path) = parent_abs_path.strip_prefix(&snapshot.abs_path) { + if *scan_id == snapshot.scan_id && snapshot.entry_for_path(parent_path).is_some() { + ignores_to_update.push(parent_abs_path.clone()); + } - let ignore_path = parent_path.join(&*GITIGNORE); - if snapshot.entry_for_path(ignore_path).is_none() { - ignores_to_delete.push(parent_abs_path.clone()); + let ignore_path = parent_path.join(&*GITIGNORE); + if snapshot.entry_for_path(ignore_path).is_none() { + ignores_to_delete.push(parent_abs_path.clone()); + } } } @@ -2410,20 +2428,6 @@ impl BackgroundScanner { snapshot.entries_by_path.edit(entries_by_path_edits, &()); snapshot.entries_by_id.edit(entries_by_id_edits, &()); } - - async fn build_root_ignore_stack(&self) { - // let parent_abs_path = if let Some(path) = self.abs_path().parent() { - // path - // } else { - // return IgnoreStack::none() - // } - - // let mut cur_path = PathBuf::new(); - // for component in self.abs_path().components() { - // // self.snapshot.lock().ignores.insert(parent_path, (ignore, self.scan_id)); - // cur_path.push(compo) - // } - } } fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag { @@ -2797,23 +2801,28 @@ mod tests { #[gpui::test] async fn test_rescan_with_gitignore(cx: &mut TestAppContext) { - let dir = temp_tree(json!({ - ".git": {}, - ".gitignore": "ignored-dir\n", - "tracked-dir": { - "tracked-file1": "tracked contents", - }, - "ignored-dir": { - "ignored-file1": "ignored contents", + let parent_dir = temp_tree(json!({ + ".gitignore": "ancestor-ignored-file1\nancestor-ignored-file2\n", + "tree": { + ".git": {}, + ".gitignore": "ignored-dir\n", + "tracked-dir": { + "tracked-file1": "", + "ancestor-ignored-file1": "", + }, + "ignored-dir": { + "ignored-file1": "" + } } })); + let dir = parent_dir.path().join("tree"); let http_client = FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone()); let tree = Worktree::local( client, - dir.path(), + dir.as_path(), true, Arc::new(RealFs), Default::default(), @@ -2826,23 +2835,47 @@ mod tests { tree.flush_fs_events(&cx).await; cx.read(|cx| { let tree = tree.read(cx); - let tracked = tree.entry_for_path("tracked-dir/tracked-file1").unwrap(); - let ignored = tree.entry_for_path("ignored-dir/ignored-file1").unwrap(); - assert_eq!(tracked.is_ignored, false); - assert_eq!(ignored.is_ignored, true); + assert!( + !tree + .entry_for_path("tracked-dir/tracked-file1") + .unwrap() + .is_ignored + ); + assert!( + tree.entry_for_path("tracked-dir/ancestor-ignored-file1") + .unwrap() + .is_ignored + ); + assert!( + tree.entry_for_path("ignored-dir/ignored-file1") + .unwrap() + .is_ignored + ); }); - std::fs::write(dir.path().join("tracked-dir/tracked-file2"), "").unwrap(); - std::fs::write(dir.path().join("ignored-dir/ignored-file2"), "").unwrap(); + std::fs::write(dir.join("tracked-dir/tracked-file2"), "").unwrap(); + std::fs::write(dir.join("tracked-dir/ancestor-ignored-file2"), "").unwrap(); + std::fs::write(dir.join("ignored-dir/ignored-file2"), "").unwrap(); tree.flush_fs_events(&cx).await; cx.read(|cx| { let tree = tree.read(cx); - let dot_git = tree.entry_for_path(".git").unwrap(); - let tracked = tree.entry_for_path("tracked-dir/tracked-file2").unwrap(); - let ignored = tree.entry_for_path("ignored-dir/ignored-file2").unwrap(); - assert_eq!(tracked.is_ignored, false); - assert_eq!(ignored.is_ignored, true); - assert_eq!(dot_git.is_ignored, true); + assert!( + !tree + .entry_for_path("tracked-dir/tracked-file2") + .unwrap() + .is_ignored + ); + assert!( + tree.entry_for_path("tracked-dir/ancestor-ignored-file2") + .unwrap() + .is_ignored + ); + assert!( + tree.entry_for_path("ignored-dir/ignored-file2") + .unwrap() + .is_ignored + ); + assert!(tree.entry_for_path(".git").unwrap().is_ignored); }); }