diff --git a/doc/fd.1 b/doc/fd.1 index df42b17..278c429 100644 --- a/doc/fd.1 +++ b/doc/fd.1 @@ -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. diff --git a/src/cli.rs b/src/cli.rs index 3311484..c923380 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -531,12 +531,9 @@ pub struct Opts { /// Ignore directories containing the named entry. #[arg( long, - value_name = "name", - hide_short_help = true, - help = "Ignore directories containing this name", - long_help + value_name = "name" )] - pub ignore_contain: Option, + pub ignore_contain: Vec, /// Set number of threads to use for searching & executing (default: number /// of available CPU cores) diff --git a/src/config.rs b/src/config.rs index 66f4c6b..708a993 100644 --- a/src/config.rs +++ b/src/config.rs @@ -131,8 +131,8 @@ pub struct Config { /// Whether or not to use hyperlinks on paths pub hyperlink: bool, - /// A name that should stop traversal down its parent. (e.g. https://bford.info/cachedir/). - pub ignore_contain: Option, + /// Names that should stop traversal down their parent. (e.g. https://bford.info/cachedir/). + pub ignore_contain: Vec, } impl Config { diff --git a/src/walk.rs b/src/walk.rs index 0ff6ebe..a07b725 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -498,15 +498,22 @@ impl WorkerState { } }; + // Check the depth & name first, since they don't require metadata. + let entry_path = entry.path(); + + // Filter out directories containing a given name. + if entry_path.is_dir() + && config.ignore_contain.iter().any(|ic| entry_path.join(ic).exists()) + { + return WalkState::Skip; + } + if let Some(min_depth) = config.min_depth && entry.depth().is_none_or(|d| d < min_depth) { return WalkState::Continue; } - // Check the name first, since it doesn't require metadata - let entry_path = entry.path(); - let search_str: Cow = if config.search_full_path { let path_abs_buf = filesystem::path_absolute_form(entry_path) .expect("Retrieving absolute path succeeds"); @@ -560,14 +567,6 @@ impl WorkerState { } } - // Filter out directories containing a given name. - if let Some(ref ignore_contain) = config.ignore_contain - && entry_path.is_dir() - && entry_path.join(ignore_contain).exists() - { - return WalkState::Skip; - } - // Filter out unwanted sizes if it is a file and we have been given size constraints. if !config.size_constraints.is_empty() { if entry_path.is_file() { diff --git a/tests/tests.rs b/tests/tests.rs index fe7dd68..59017b0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2710,6 +2710,25 @@ fn test_hyperlink() { #[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_has_highest_precedence() { let te = TestEnv::new( &["include", "exclude", "exclude/sub"], &[ @@ -2719,9 +2738,6 @@ fn test_ignore_contain() { "exclude/sub/nope", ], ); - let expected = "include/ - include/foo - symlink - top"; - te.assert_output(&["--ignore-contain=CACHEDIR.TAG", "."], expected); + let expected = "include/foo"; + te.assert_output(&["--ignore-contain=CACHEDIR.TAG", "--min-depth=2", "."], expected); }