diff --git a/crates/uv-distribution/src/metadata/lowering.rs b/crates/uv-distribution/src/metadata/lowering.rs index cd20645ef8a3..0e7cb1a5bbb3 100644 --- a/crates/uv-distribution/src/metadata/lowering.rs +++ b/crates/uv-distribution/src/metadata/lowering.rs @@ -317,10 +317,10 @@ impl LoweredRequirement { let source = if let Some(git_member) = &git_member { // If the workspace comes from a Git dependency, all workspace // members need to be Git dependencies, too. - let subdirectory = uv_fs::normalize_path( - &uv_fs::relative_to(member.root(), git_member.fetch_root) - .expect("Workspace member must be relative"), - ); + let subdirectory = + uv_fs::relative_to(member.root(), git_member.fetch_root) + .expect("Workspace member must be relative"); + let subdirectory = uv_fs::normalize_path_buf(subdirectory); RequirementSource::Git { repository: git_member.git_source.git.repository().clone(), reference: git_member.git_source.git.reference().clone(), @@ -711,10 +711,9 @@ fn path_source( }; if is_dir { if let Some(git_member) = git_member { - let subdirectory = uv_fs::normalize_path( - &uv_fs::relative_to(install_path, git_member.fetch_root) - .expect("Workspace member must be relative"), - ); + let subdirectory = uv_fs::relative_to(install_path, git_member.fetch_root) + .expect("Workspace member must be relative"); + let subdirectory = uv_fs::normalize_path_buf(subdirectory); return Ok(RequirementSource::Git { repository: git_member.git_source.git.repository().clone(), reference: git_member.git_source.git.reference().clone(), diff --git a/crates/uv-fs/src/path.rs b/crates/uv-fs/src/path.rs index 7ac6de59c556..7ce2ef42ab67 100644 --- a/crates/uv-fs/src/path.rs +++ b/crates/uv-fs/src/path.rs @@ -199,7 +199,33 @@ pub fn normalize_absolute_path(path: &Path) -> Result { Ok(ret) } -/// Normalize a path, removing things like `.` and `..`. +/// Normalize a [`Path`], removing things like `.` and `..`. +pub fn normalize_path(path: &Path) -> Cow { + // Fast path: if the path is already normalized, return it as-is. + if path.components().all(|component| match component { + Component::Prefix(_) | Component::RootDir | Component::Normal(_) => true, + Component::ParentDir | Component::CurDir => false, + }) { + Cow::Borrowed(path) + } else { + Cow::Owned(normalized(path)) + } +} + +/// Normalize a [`PathBuf`], removing things like `.` and `..`. +pub fn normalize_path_buf(path: PathBuf) -> PathBuf { + // Fast path: if the path is already normalized, return it as-is. + if path.components().all(|component| match component { + Component::Prefix(_) | Component::RootDir | Component::Normal(_) => true, + Component::ParentDir | Component::CurDir => false, + }) { + path + } else { + normalized(&path) + } +} + +/// Normalize a [`Path`]. /// /// Unlike [`normalize_absolute_path`], this works with relative paths and does never error. /// @@ -216,8 +242,7 @@ pub fn normalize_absolute_path(path: &Path) -> Result { /// Out: `workspace-git-path-dep-test/packages/d` /// /// In: `./a/../../b` -/// Out: `../b` -pub fn normalize_path(path: &Path) -> PathBuf { +fn normalized(path: &Path) -> PathBuf { let mut normalized = PathBuf::new(); for component in path.components() { match component { diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs index d965e8700f0a..f25e174117cf 100644 --- a/crates/uv-resolver/src/lock/mod.rs +++ b/crates/uv-resolver/src/lock/mod.rs @@ -3749,7 +3749,8 @@ fn normalize_requirement( ext, url: _, } => { - let install_path = uv_fs::normalize_path(&workspace.install_path().join(&install_path)); + let install_path = + uv_fs::normalize_path_buf(workspace.install_path().join(&install_path)); let url = VerbatimUrl::from_absolute_path(&install_path) .map_err(LockErrorKind::RequirementVerbatimUrl)?; @@ -3772,7 +3773,8 @@ fn normalize_requirement( r#virtual, url: _, } => { - let install_path = uv_fs::normalize_path(&workspace.install_path().join(&install_path)); + let install_path = + uv_fs::normalize_path_buf(workspace.install_path().join(&install_path)); let url = VerbatimUrl::from_absolute_path(&install_path) .map_err(LockErrorKind::RequirementVerbatimUrl)?; diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 025105040f6f..a27ce472234c 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -65,8 +65,7 @@ async fn run(mut cli: Cli) -> Result { .as_deref() .map(std::path::absolute) .transpose()? - .as_deref() - .map(uv_fs::normalize_path) + .map(uv_fs::normalize_path_buf) .map(Cow::Owned) .unwrap_or_else(|| Cow::Borrowed(&*CWD));