ignore: fix problem with searching whitelisted hidden files

... specifically, when the whitelist comes from a _parent_ gitignore
file.

Our handling of parent gitignores is pretty ham-fisted and has been a
source of some unfortunate bugs. The problem is that we need to strip
the parent path from the path we're searching in order to correctly
apply the globs. But getting this stripping correct seems to be a subtle
affair.

Fixes #3173
This commit is contained in:
Andrew Gallant
2025-10-08 21:09:21 -04:00
parent bb88a1ac45
commit 0407e104f6
3 changed files with 37 additions and 10 deletions

View File

@@ -37,6 +37,8 @@ Bug fixes:
Ensure hyphens in flag names are escaped in the roff text for the man page.
* [BUG #3155](https://github.com/BurntSushi/ripgrep/issues/3155):
Statically compile PCRE2 into macOS release artifacts on `aarch64`.
* [BUG #3173](https://github.com/BurntSushi/ripgrep/issues/3173):
Fix ancestor ignore filter bug when searching whitelisted hidden files.
Feature enhancements:

View File

@@ -467,16 +467,20 @@ impl Ignore {
.take_while(|ig| !ig.0.is_absolute_parent)
.last()
.map_or(path, |ig| {
strip_if_is_prefix(
"/",
strip_if_is_prefix(
strip_if_is_prefix(
"./",
ig.0.dir.as_path(),
),
path,
),
)
// This is a weird special case when ripgrep users
// search with just a `.`, as some tools do
// automatically (like consult). In this case, if
// we don't bail out now, the code below will strip
// a leading `.` from `path`, which might mangle
// a hidden file name!
if ig.0.dir.as_path() == Path::new(".") {
return path;
}
let without_dot_slash =
strip_if_is_prefix("./", ig.0.dir.as_path());
let relative_base =
strip_if_is_prefix(without_dot_slash, path);
strip_if_is_prefix("/", relative_base)
}),
);

View File

@@ -1586,3 +1586,24 @@ YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY\n \
eqnice!("test\n", got);
}
);
// See: https://github.com/BurntSushi/ripgrep/issues/3173
rgtest!(r3173_hidden_whitelist_only_dot, |dir: Dir, _: TestCommand| {
dir.create_dir("subdir");
dir.create("subdir/.foo.txt", "text");
dir.create(".ignore", "!.foo.txt");
let cmd = || dir.command();
eqnice!(cmd().args(&["--files"]).stdout(), "subdir/.foo.txt\n");
eqnice!(cmd().args(&["--files", "."]).stdout(), "./subdir/.foo.txt\n");
eqnice!(cmd().args(&["--files", "./"]).stdout(), "./subdir/.foo.txt\n");
let cmd = || {
let mut cmd = dir.command();
cmd.current_dir(dir.path().join("subdir"));
cmd
};
eqnice!(cmd().args(&["--files"]).stdout(), ".foo.txt\n");
eqnice!(cmd().args(&["--files", "."]).stdout(), "./.foo.txt\n");
eqnice!(cmd().args(&["--files", "./"]).stdout(), "./.foo.txt\n");
});