Skip to content

Commit

Permalink
gitignore: make objects chain be more Arc friendly
Browse files Browse the repository at this point in the history
This partially reverts changes in a9f489c "Switch to ignore crate for
gitignore handling." Since child ignore object no longer needs to access the
root to resolve the prefix path, it's simpler to store a matcher per node.
  • Loading branch information
yuja committed Feb 24, 2024
1 parent 23c1e9b commit ca29791
Showing 1 changed file with 22 additions and 19 deletions.
41 changes: 22 additions & 19 deletions lib/src/gitignore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

use std::path::PathBuf;
use std::sync::Arc;
use std::{fs, io};
use std::{fs, io, iter};

use ignore::gitignore;
use thiserror::Error;
Expand All @@ -39,13 +39,15 @@ pub enum GitIgnoreError {
/// Models the effective contents of multiple .gitignore files.
#[derive(Debug)]
pub struct GitIgnoreFile {
matchers: Vec<gitignore::Gitignore>,
parent: Option<Arc<GitIgnoreFile>>,
matcher: gitignore::Gitignore,
}

impl GitIgnoreFile {
pub fn empty() -> Arc<GitIgnoreFile> {
Arc::new(GitIgnoreFile {
matchers: Default::default(),
parent: None,
matcher: gitignore::Gitignore::empty(),
})
}

Expand All @@ -72,10 +74,12 @@ impl GitIgnoreFile {
builder.add_line(None, line)?;
}
let matcher = builder.build()?;
let mut matchers = self.matchers.clone();
matchers.push(matcher);

Ok(Arc::new(GitIgnoreFile { matchers }))
let parent = if self.matcher.is_empty() {
self.parent.clone() // omit the empty root
} else {
Some(self.clone())
};
Ok(Arc::new(GitIgnoreFile { parent, matcher }))
}

/// Concatenates new `.gitignore` file at the `prefix` directory.
Expand All @@ -99,18 +103,17 @@ impl GitIgnoreFile {
}

fn matches_helper(&self, path: &str, is_dir: bool) -> bool {
self.matchers
.iter()
.rev()
.find_map(|matcher|
// TODO: the documentation warns that
// `matched_path_or_any_parents` is slower than `matched`;
// ideally, we would switch to that.
match matcher.matched_path_or_any_parents(path, is_dir) {
ignore::Match::None => None,
ignore::Match::Ignore(_) => Some(true),
ignore::Match::Whitelist(_) => Some(false),
})
iter::successors(Some(self), |file| file.parent.as_deref())
.find_map(|file| {
// TODO: the documentation warns that
// `matched_path_or_any_parents` is slower than `matched`;
// ideally, we would switch to that.
match file.matcher.matched_path_or_any_parents(path, is_dir) {
ignore::Match::None => None,
ignore::Match::Ignore(_) => Some(true),
ignore::Match::Whitelist(_) => Some(false),
}
})
.unwrap_or_default()
}

Expand Down

0 comments on commit ca29791

Please sign in to comment.