mirror of
https://github.com/sharkdp/fd.git
synced 2026-10-12 02:22:28 +00:00
Make broken symlink depth robust under absolute paths
The depth for a broken symlink was derived by stripping the search root from its path and counting the remaining components. Compare the path and the roots in absolute form before stripping, so the match holds whether or not --absolute-path has already made the roots absolute, and pick the deepest matching root. This avoids an absolute path failing to match a relative root and falling back to an inflated depth. Add a regression test that exercises the broken symlink case together with --absolute-path.
This commit is contained in:
+25
-11
@@ -658,19 +658,33 @@ impl WorkerState {
|
||||
///
|
||||
/// `ignore::DirEntry::depth()` provides this for normal entries, but broken
|
||||
/// symlinks are surfaced as errors that carry no depth, so we derive it from the
|
||||
/// path components instead (see issue #1017). The matching root is the longest
|
||||
/// search root (by component count) that `path` starts with; if none matches we
|
||||
/// fall back to the full component count, which keeps the entry visible rather
|
||||
/// than silently dropping it.
|
||||
/// path instead (see issue #1017). The walker reports every entry's path
|
||||
/// prefixed by the search root it was found under, so we strip the matching root
|
||||
/// and count the remaining components. Both the path and the roots are put into
|
||||
/// absolute form first, so the comparison is correct regardless of whether
|
||||
/// `--absolute-path` has already made the roots absolute (otherwise an absolute
|
||||
/// path would fail to match a relative root and the depth would be wrong). When
|
||||
/// more than one root is a prefix, the deepest (most specific) one wins. If none
|
||||
/// matches, which should not happen, we fall back to the full component count,
|
||||
/// keeping the entry visible rather than silently dropping it.
|
||||
fn depth_relative_to_roots(path: &Path, roots: &[PathBuf]) -> usize {
|
||||
let components = path.components().count();
|
||||
let root_components = roots
|
||||
// `Path::join` ignores the base when its argument is already absolute, so
|
||||
// this leaves absolute inputs untouched and anchors relative ones at the cwd.
|
||||
let cwd = std::env::current_dir().ok();
|
||||
let absolute = |p: &Path| -> PathBuf {
|
||||
match &cwd {
|
||||
Some(cwd) => cwd.join(p),
|
||||
None => p.to_path_buf(),
|
||||
}
|
||||
};
|
||||
|
||||
let absolute_path = absolute(path);
|
||||
roots
|
||||
.iter()
|
||||
.filter(|root| path.starts_with(root))
|
||||
.map(|root| root.components().count())
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
components.saturating_sub(root_components)
|
||||
.filter_map(|root| absolute_path.strip_prefix(absolute(root)).ok())
|
||||
.map(|relative| relative.components().count())
|
||||
.min()
|
||||
.unwrap_or_else(|| path.components().count())
|
||||
}
|
||||
|
||||
fn search_str_for_entry<'a>(
|
||||
|
||||
@@ -1245,6 +1245,46 @@ fn test_min_depth_broken_symlink() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Minimum depth with a broken symlink combined with --absolute-path (#1017)
|
||||
///
|
||||
/// With --absolute-path the search root is made absolute before walking, so the
|
||||
/// broken symlink's depth must still be computed relative to that root rather
|
||||
/// than from the absolute path's full component count.
|
||||
#[test]
|
||||
fn test_min_depth_broken_symlink_absolute_path() {
|
||||
let (mut te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
|
||||
te.create_broken_symlink("one/two/broken_symlink")
|
||||
.expect("Failed to create broken symlink.");
|
||||
|
||||
// The broken symlink sits at depth 3 relative to the (absolute) root.
|
||||
te.assert_output(
|
||||
&[
|
||||
"--follow",
|
||||
"--absolute-path",
|
||||
"--type",
|
||||
"symlink",
|
||||
"--min-depth",
|
||||
"3",
|
||||
"broken_symlink",
|
||||
],
|
||||
&format!("{abs_path}/one/two/broken_symlink"),
|
||||
);
|
||||
|
||||
// A --min-depth beyond its actual depth must exclude it.
|
||||
te.assert_output(
|
||||
&[
|
||||
"--follow",
|
||||
"--absolute-path",
|
||||
"--type",
|
||||
"symlink",
|
||||
"--min-depth",
|
||||
"4",
|
||||
"broken_symlink",
|
||||
],
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
/// Exact depth (--exact-depth)
|
||||
#[test]
|
||||
fn test_exact_depth() {
|
||||
|
||||
Reference in New Issue
Block a user