diff --git a/CHANGELOG.md b/CHANGELOG.md index e0159bb..e37735e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ Bug fixes: Ignore a UTF-8 BOM marker at the start of `.gitignore` (and similar files). * [BUG #2944](https://github.com/BurntSushi/ripgrep/pull/2944): Fix a bug where the "bytes searched" in `--stats` output could be incorrect. +* [BUG #2990](https://github.com/BurntSushi/ripgrep/issues/2990): + Fix a bug where ripgrep would mishandle globs that ended with a `.`. Feature enhancements: diff --git a/crates/globset/src/pathutil.rs b/crates/globset/src/pathutil.rs index 8488e74..861f61b 100644 --- a/crates/globset/src/pathutil.rs +++ b/crates/globset/src/pathutil.rs @@ -4,21 +4,25 @@ use bstr::{ByteSlice, ByteVec}; /// The final component of the path, if it is a normal file. /// -/// If the path terminates in `.`, `..`, or consists solely of a root of -/// prefix, file_name will return None. +/// If the path terminates in `..`, or consists solely of a root of prefix, +/// file_name will return `None`. pub(crate) fn file_name<'a>(path: &Cow<'a, [u8]>) -> Option> { - if path.last_byte().map_or(true, |b| b == b'.') { + if path.is_empty() { return None; } let last_slash = path.rfind_byte(b'/').map(|i| i + 1).unwrap_or(0); - Some(match *path { + let got = match *path { Cow::Borrowed(path) => Cow::Borrowed(&path[last_slash..]), Cow::Owned(ref path) => { let mut path = path.clone(); path.drain_bytes(..last_slash); Cow::Owned(path) } - }) + }; + if got == &b".."[..] { + return None; + } + Some(got) } /// Return a file extension given a path's file name. diff --git a/tests/regression.rs b/tests/regression.rs index 4fd3c0f..ecc1205 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -1461,3 +1461,19 @@ rgtest!(r2944_incorrect_bytes_searched, |dir: Dir, mut cmd: TestCommand| { let got = cmd.args(&["--stats", "-m2", "foo", "."]).stdout(); assert!(got.contains("10 bytes searched\n")); }); + +// See: https://github.com/BurntSushi/ripgrep/issues/2990 +#[cfg(unix)] +rgtest!(r2990_trip_over_trailing_dot, |dir: Dir, _cmd: TestCommand| { + dir.create_dir("asdf"); + dir.create_dir("asdf."); + dir.create("asdf/foo", ""); + dir.create("asdf./foo", ""); + + let got = dir.command().args(&["--files", "-g", "!asdf/"]).stdout(); + eqnice!("asdf./foo\n", got); + + // This used to ignore the glob given and included `asdf./foo` in output. + let got = dir.command().args(&["--files", "-g", "!asdf./"]).stdout(); + eqnice!("asdf/foo\n", got); +});