Honor gitignores above worktree root

This commit is contained in:
Antonio Scandurra 2022-07-08 11:19:46 +02:00
parent 9328ab121a
commit e66144104f

View file

@ -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<Path> = 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);
});
}