Skip to content

Commit

Permalink
ignore: Add option to skip reading of online-only ignore files
Browse files Browse the repository at this point in the history
  • Loading branch information
fe9lix committed Jul 10, 2024
1 parent 71d71d2 commit 47bfa98
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 9 deletions.
18 changes: 13 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions crates/ignore/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ignore"
version = "0.4.22" #:version
version = "0.4.22" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """
A fast library for efficiently matching ignore files such as `.gitignore`
Expand Down Expand Up @@ -32,7 +32,9 @@ default-features = false
features = ["std", "perf", "syntax", "meta", "nfa", "hybrid", "dfa-onepass"]

[target.'cfg(windows)'.dependencies.winapi-util]
version = "0.1.2"
# Point to our custom fork of the `winapi-util` crate that supports online-only file attribute.
git = "https://github.com/raycast/winapi-util.git"
branch = "online-only-attribute"

[dev-dependencies]
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
Expand Down
25 changes: 24 additions & 1 deletion crates/ignore/src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ struct IgnoreOptions {
/// Whether a git repository must be present in order to apply any
/// git-related ignore rules.
require_git: bool,
/// Whether to skip reading online-only ignore files.
skip_online_only_ignore: bool,
}

/// Ignore is a matcher useful for recursively walking one or more directories.
Expand Down Expand Up @@ -262,6 +264,7 @@ impl Ignore {
&dir,
&self.0.custom_ignore_filenames,
self.0.opts.ignore_case_insensitive,
self.0.opts.skip_online_only_ignore,
);
errs.maybe_push(err);
m
Expand All @@ -274,6 +277,7 @@ impl Ignore {
&dir,
&[".ignore"],
self.0.opts.ignore_case_insensitive,
self.0.opts.skip_online_only_ignore,
);
errs.maybe_push(err);
m
Expand All @@ -286,6 +290,7 @@ impl Ignore {
&dir,
&[".gitignore"],
self.0.opts.ignore_case_insensitive,
self.0.opts.skip_online_only_ignore,
);
errs.maybe_push(err);
m
Expand All @@ -300,6 +305,7 @@ impl Ignore {
&git_dir,
&["info/exclude"],
self.0.opts.ignore_case_insensitive,
self.0.opts.skip_online_only_ignore,
);
errs.maybe_push(err);
m
Expand Down Expand Up @@ -600,6 +606,7 @@ impl IgnoreBuilder {
git_exclude: true,
ignore_case_insensitive: false,
require_git: true,
skip_online_only_ignore: false,
},
}
}
Expand Down Expand Up @@ -773,6 +780,17 @@ impl IgnoreBuilder {
self.opts.ignore_case_insensitive = yes;
self
}

/// Skip reading ignore files that are marked as online-only.
///
/// This is disabled by default.
pub(crate) fn skip_online_only_ignore(
&mut self,
yes: bool,
) -> &mut IgnoreBuilder {
self.opts.skip_online_only_ignore = yes;
self
}
}

/// Creates a new gitignore matcher for the directory given.
Expand All @@ -788,10 +806,15 @@ pub(crate) fn create_gitignore<T: AsRef<OsStr>>(
dir_for_ignorefile: &Path,
names: &[T],
case_insensitive: bool,
skip_online_only: bool,
) -> (Gitignore, Option<Error>) {
let mut builder = GitignoreBuilder::new(dir);
let mut errs = PartialErrorBuilder::default();
builder.case_insensitive(case_insensitive).unwrap();
builder
.case_insensitive(case_insensitive)
.unwrap()
.skip_online_only(skip_online_only)
.unwrap();
for name in names {
let gipath = dir_for_ignorefile.join(name.as_ref());
// This check is not necessary, but is added for performance. Namely,
Expand Down
21 changes: 20 additions & 1 deletion crates/ignore/src/gitignore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use {
};

use crate::{
pathutil::{is_file_name, strip_prefix},
pathutil::{is_file_name, is_online_only, strip_prefix},
Error, Match, PartialErrorBuilder,
};

Expand Down Expand Up @@ -308,6 +308,7 @@ pub struct GitignoreBuilder {
root: PathBuf,
globs: Vec<Glob>,
case_insensitive: bool,
skip_online_only: bool,
}

impl GitignoreBuilder {
Expand All @@ -324,6 +325,7 @@ impl GitignoreBuilder {
root: strip_prefix("./", root).unwrap_or(root).to_path_buf(),
globs: vec![],
case_insensitive: false,
skip_online_only: false,
}
}

Expand Down Expand Up @@ -386,6 +388,10 @@ impl GitignoreBuilder {
/// all other valid globs will still be added.
pub fn add<P: AsRef<Path>>(&mut self, path: P) -> Option<Error> {
let path = path.as_ref();
if self.skip_online_only && is_online_only(path) {
log::debug!("gitignore is online-only: {}", path.display());
return None;
}
let file = match File::open(path) {
Err(err) => return Some(Error::Io(err).with_path(path)),
Ok(file) => file,
Expand Down Expand Up @@ -530,6 +536,19 @@ impl GitignoreBuilder {
self.case_insensitive = yes;
Ok(self)
}

/// Toggle whether online-only ignore files should be read.
/// This is to prevent potential blocking if the internet connection is down
/// while the ignore file is being read–which triggers downloading.
///
/// This is disabled by default.
pub fn skip_online_only(
&mut self,
yes: bool,
) -> Result<&mut GitignoreBuilder, Error> {
self.skip_online_only = yes;
Ok(self)
}
}

/// Return the file path of the current environment's global gitignore file.
Expand Down
27 changes: 27 additions & 0 deletions crates/ignore/src/pathutil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,33 @@ pub(crate) fn is_hidden(dent: &DirEntry) -> bool {
}
}

/// Determine if the file is an online-only file.
///
/// Online-only if the file blocks allocated value is zero.
#[cfg(unix)]
pub(crate) fn is_online_only<P: AsRef<Path>>(path: P) -> bool {
use std::os::unix::fs::MetadataExt;

if let Ok(md) = std::fs::symlink_metadata(path) {
return md.blocks() == 0;
}
false
}

/// Determine if the file is an online-only file.
#[cfg(windows)]
pub(crate) fn is_online_only<P: AsRef<Path>>(path: P) -> bool {
use std::os::windows::fs::MetadataExt;
use winapi_util::file;

if let Ok(md) = std::fs::symlink_metadata(path) {
if file::is_online_only(md.file_attributes() as u64) {
return true;
}
}
false
}

/// Strip `prefix` from the `path` and return the remainder.
///
/// If `path` doesn't have a prefix `prefix`, then return `None`.
Expand Down
8 changes: 8 additions & 0 deletions crates/ignore/src/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,14 @@ impl WalkBuilder {
self
}

/// Skips reading ignore files that are available only online.
///
/// This is disabled by default.
pub fn skip_online_only_ignore(&mut self, yes: bool) -> &mut WalkBuilder {
self.ig_builder.skip_online_only_ignore(yes);
self
}

/// Yields only entries which satisfy the given predicate and skips
/// descending into directories that do not satisfy the given predicate.
///
Expand Down

0 comments on commit 47bfa98

Please sign in to comment.