Skip to content

Commit 689752e

Browse files
committed
Merge branch 'discovery-fix'
2 parents 932c353 + 745d926 commit 689752e

File tree

2 files changed

+49
-15
lines changed

2 files changed

+49
-15
lines changed

git-path/src/convert.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::{
22
borrow::Cow,
33
ffi::OsStr,
4-
iter::FromIterator,
54
path::{Path, PathBuf},
65
};
76

@@ -220,6 +219,7 @@ pub fn to_windows_separators<'a>(path: impl Into<Cow<'a, BStr>>) -> Cow<'a, BStr
220219

221220
/// Resolve relative components virtually without accessing the file system, e.g. turn `a/./b/c/.././..` into `a`,
222221
/// without keeping intermediate `..` and `/a/../b/..` becomes `/`.
222+
/// If the input path was relative and ends up being the `current_dir`, `.` is returned instead of the full path to `current_dir`.
223223
///
224224
/// This is particularly useful when manipulating paths that are based on user input, and not resolving intermediate
225225
/// symlinks keeps the path similar to what the user provided. If that's not desirable, use `[realpath()][crate::realpath()`
@@ -238,28 +238,27 @@ pub fn normalize<'a>(path: impl Into<Cow<'a, Path>>, current_dir: impl AsRef<Pat
238238
}
239239
let current_dir = current_dir.as_ref();
240240
let mut current_dir_opt = Some(current_dir);
241-
let mut components = path.components();
242-
let mut path = PathBuf::from_iter(components.next());
241+
let was_relative = path.is_relative();
242+
let components = path.components();
243+
let mut path = PathBuf::new();
243244
for component in components {
244245
if let ParentDir = component {
245246
let path_was_dot = path == Path::new(".");
247+
if path.as_os_str().is_empty() || path_was_dot {
248+
path.push(current_dir_opt.take()?);
249+
}
246250
if !path.pop() {
247251
return None;
248252
}
249-
if path.as_os_str().is_empty() {
250-
path.clear();
251-
path.push(current_dir_opt.take()?);
252-
if path_was_dot && !path.pop() {
253-
return None;
254-
}
255-
}
256253
} else {
257254
path.push(component)
258255
}
259256
}
260-
Some(if path.as_os_str().is_empty() || path == current_dir {
261-
PathBuf::from(".").into()
257+
258+
if (path.as_os_str().is_empty() || path == current_dir) && was_relative {
259+
Cow::Borrowed(Path::new("."))
262260
} else {
263261
path.into()
264-
})
262+
}
263+
.into()
265264
}

git-path/tests/convert/normalize.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ fn special_cases_around_cwd() -> crate::Result {
4242
"absolute CWDs are always shortened…"
4343
);
4444
assert_eq!(normalize(p("./a/.."), &cwd).unwrap(), p("."), "…like this as well…");
45+
assert_eq!(
46+
normalize(&cwd, &cwd).unwrap(),
47+
cwd,
48+
"…but only if there were relative to begin with."
49+
);
50+
assert_eq!(
51+
normalize(p("."), &cwd).unwrap(),
52+
p("."),
53+
"and this means that `.`. stays `.`"
54+
);
55+
{
56+
let mut path = cwd.clone();
57+
let last_component = path.file_name().expect("directory").to_owned();
58+
path.push("..");
59+
path.push(last_component);
60+
61+
assert_eq!(
62+
normalize(path, &cwd).unwrap(),
63+
cwd,
64+
"absolute input paths stay absolute"
65+
);
66+
}
4567
Ok(())
4668
}
4769

@@ -53,6 +75,18 @@ fn parent_dirs_cause_the_cwd_to_be_used() {
5375
);
5476
}
5577

78+
#[test]
79+
fn multiple_parent_dir_movements_eat_into_the_current_dir() {
80+
assert_eq!(
81+
normalize(p("../../../d/e"), "/users/name/a/b/c").unwrap().as_ref(),
82+
p("/users/name/d/e")
83+
);
84+
assert_eq!(
85+
normalize(p("c/../../../d/e"), "/users/name/a/b").unwrap().as_ref(),
86+
p("/users/name/d/e")
87+
);
88+
}
89+
5690
#[test]
5791
fn walking_up_too_much_yield_none() {
5892
let cwd = "/users/name";
@@ -71,14 +105,15 @@ fn trailing_directories_after_too_numereous_parent_dirs_yield_none() {
71105

72106
#[test]
73107
fn trailing_relative_components_are_resolved() {
74-
let cwd = std::env::current_dir().unwrap();
108+
let cwd = Path::new("/a/b/c");
75109
for (input, expected) in [
76110
("./a/b/./c/../d/..", "./a/b"),
77111
("a/./b/c/.././..", "a"),
78112
("/a/b/c/.././../.", "/a"),
79113
("./a/..", "."),
80114
("a/..", "."),
81115
("./a", "./a"),
116+
("./a/./b", "./a/./b"),
82117
("./a/./b/..", "./a/."),
83118
("/a/./b/c/.././../.", "/a"),
84119
("/a/./b", "/a/./b"),
@@ -90,7 +125,7 @@ fn trailing_relative_components_are_resolved() {
90125
] {
91126
let path = p(input);
92127
assert_eq!(
93-
normalize(path, &cwd).unwrap_or_else(|| panic!("{path:?}")),
128+
normalize(path, cwd).unwrap_or_else(|| panic!("{path:?}")),
94129
Cow::Borrowed(p(expected)),
95130
"'{}' got an unexpected result",
96131
input

0 commit comments

Comments
 (0)