Merge pull request #1852 from fischman/1727-ignore-contain

`--ignore-contain`: ignore directories containing a named entry.
This commit is contained in:
Thayne McCombs
2026-01-20 00:48:43 -07:00
committed by GitHub
7 changed files with 77 additions and 3 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Upcoming release
## Features
- Add `--ignore-contain` option to ignore directories containing a named entry (e.g. to ignore [`CACHEDIR.TAG`](https://bford.info/cachedir/)); see #1727 (@fischman).
## Bugfixes
Vendored
+4
View File
@@ -265,6 +265,10 @@ Examples:
\-\-exclude '*.pyc'
\-\-exclude node_modules
.TP
.BI "\-\-ignore-contain " name
Exclude directories that (directly) contain the given name.
This option can be specified multiple times.
.TP
.BI "\-\-ignore-file " path
Add a custom ignore-file in '.gitignore' format.
These files have a low precedence.
+4
View File
@@ -528,6 +528,10 @@ pub struct Opts {
)]
pub hyperlink: HyperlinkWhen,
/// Ignore directories containing the named entry.
#[arg(long, value_name = "name")]
pub ignore_contain: Vec<String>,
/// Set number of threads to use for searching & executing (default: number
/// of available CPU cores)
#[arg(long, short = 'j', value_name = "num", hide_short_help = true, value_parser = str::parse::<NonZeroUsize>)]
+3
View File
@@ -130,6 +130,9 @@ pub struct Config {
/// Whether or not to use hyperlinks on paths
pub hyperlink: bool,
/// Names that should stop traversal down their parent. (e.g. https://bford.info/cachedir/).
pub ignore_contain: Vec<String>,
}
impl Config {
+1
View File
@@ -326,6 +326,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
actual_path_separator,
max_results: opts.max_results(),
strip_cwd_prefix: opts.strip_cwd_prefix(|| !(opts.null_separator || has_command)),
ignore_contain: opts.ignore_contain,
})
}
+13 -2
View File
@@ -461,11 +461,22 @@ impl WorkerState {
return WalkState::Quit;
}
let entry = match entry {
Ok(ref e) if e.depth() == 0 => {
if let Ok(e) = &entry {
let entry_path = e.path();
if entry_path.is_dir()
&& config
.ignore_contain
.iter()
.any(|ic| entry_path.join(ic).exists())
{
return WalkState::Skip;
}
if e.depth() == 0 {
// Skip the root directory entry.
return WalkState::Continue;
}
}
let entry = match entry {
Ok(e) => DirEntry::normal(e),
Err(ignore::Error::WithPath {
path,
+51
View File
@@ -2707,3 +2707,54 @@ fn test_hyperlink() {
te.assert_output(&["--hyperlink=always", "a.foo"], &expected);
}
#[test]
fn test_ignore_contain() {
let te = TestEnv::new(
&["include", "exclude", "exclude/sub", "other"],
&[
"top",
"include/foo",
"exclude/CACHEDIR.TAG",
"exclude/sub/nope",
"other/ignoremyparent",
],
);
let expected = "include/
include/foo
symlink
top";
te.assert_output(
&[
"--ignore-contain=CACHEDIR.TAG",
"--ignore-contain=ignoremyparent",
".",
],
expected,
);
}
#[test]
fn test_ignore_contain_precedence_over_depth_check() {
let te = TestEnv::new(
&["include", "exclude", "exclude/sub"],
&[
"top",
"include/foo",
"exclude/CACHEDIR.TAG",
"exclude/sub/nope",
],
);
let expected = "include/foo";
te.assert_output(
&["--ignore-contain=CACHEDIR.TAG", "--min-depth=2", "."],
expected,
);
}
#[test]
fn test_ignore_contain_precedence_over_root_check() {
let te = TestEnv::new(&["include"], &["CACHEDIR.TAG", "top", "include/foo"]);
let expected = "";
te.assert_output(&["--ignore-contain=CACHEDIR.TAG", "."], expected);
}