Skip to content

Commit 2784f08

Browse files
leno23cursoragent
andcommitted
fix: normalize --full-path matches for parent search paths
When searching from a subdirectory with a relative root like `..`, --full-path compared unnormalized paths (e.g. cwd/../foo) against absolute patterns. Normalize paths that contain `.` or `..` components. Fixes #1513. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7f1b147 commit 2784f08

3 files changed

Lines changed: 33 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
## Bugfixes
77
- Handle invalid working directories gracefully when using `--full-path`, see #1900 (@Xavrir).
8+
- Normalize `--full-path` matching for relative search paths such as `..`, see #1513 (@leno23).
89
- Fire the "search pattern contains a path separator" diagnostic for any pattern containing `/`, not just patterns that happen to name an existing directory. Preserves the legacy Windows behaviour that also flags native `\` separators when the pattern resolves to a real directory. See #1873.
910

1011
# 10.4.2

src/walk.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::borrow::Cow;
22
use std::ffi::OsStr;
33
use std::io::{self, Write};
44
use std::mem;
5-
use std::path::PathBuf;
5+
use std::path::{Component, PathBuf};
66
use std::sync::atomic::{AtomicBool, Ordering};
77
use std::sync::{Arc, Mutex, MutexGuard};
88
use std::thread;
@@ -13,6 +13,7 @@ use crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender, bounded};
1313
use etcetera::BaseStrategy;
1414
use ignore::overrides::{Override, OverrideBuilder};
1515
use ignore::{WalkBuilder, WalkParallel, WalkState};
16+
use normpath::PathExt;
1617
use regex::bytes::Regex;
1718

1819
use crate::config::Config;
@@ -674,7 +675,18 @@ fn search_str_for_entry<'a>(
674675
return Cow::Borrowed(entry_path.as_os_str());
675676
}
676677
let path = entry_path.strip_prefix(".").unwrap_or(entry_path);
677-
Cow::Owned(cwd.join(path).into())
678+
let absolute_path = cwd.join(path);
679+
if path
680+
.components()
681+
.any(|component| matches!(component, Component::CurDir | Component::ParentDir))
682+
{
683+
match absolute_path.normalize() {
684+
Ok(normalized_path) => Cow::Owned(normalized_path.into()),
685+
Err(_) => Cow::Owned(absolute_path.into()),
686+
}
687+
} else {
688+
Cow::Owned(absolute_path.into())
689+
}
678690
} else {
679691
match entry_path.file_name() {
680692
Some(filename) => Cow::Borrowed(filename),

tests/tests.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,24 @@ fn test_normalized_absolute_path() {
12991299
);
13001300
}
13011301

1302+
/// Full path matching should normalize relative search paths as well.
1303+
#[test]
1304+
fn test_full_path_normalizes_relative_search_path() {
1305+
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
1306+
1307+
te.assert_output_subdirectory(
1308+
"one/two",
1309+
&[
1310+
"--full-path",
1311+
"-t",
1312+
"f",
1313+
&format!("{abs_path}/one/b.foo"),
1314+
"..",
1315+
],
1316+
"../b.foo",
1317+
);
1318+
}
1319+
13021320
/// File type filter (--type)
13031321
#[test]
13041322
fn test_type() {

0 commit comments

Comments
 (0)