Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
fix: Normalize filesystem paths with `dunce`
Merged lorenz opened 8 months ago

Microsoft Windows has Universal Naming Convention (UNC) paths, see https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths

The Rust standard library normalizes paths to this form when calling std::fs::canonicalize, see https://doc.rust-lang.org/std/fs/fn.canonicalize.html

However, some programs, do not parse these paths correctly, including our ally git:

$ git clone \\?\C:\Users\lorenz\tmp
Cloning into 'tmp'...
ssh: Could not resolve hostname \\\\?\\c: No such host is known.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

The dunce crate solves the problem of having to deal with normalization and avoiding weird UNC paths for us. Note that on non-Windows platforms, it behaves exactly like std::fs::canonicalize!

9 files changed +18 -10 31039bbc 5229fb8a
modified Cargo.lock
@@ -759,6 +759,12 @@ dependencies = [
]

[[package]]
+
name = "dunce"
+
version = "1.0.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+

+
[[package]]
name = "dyn-clone"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2662,6 +2668,7 @@ dependencies = [
 "colored",
 "crossbeam-channel",
 "cyphernet",
+
 "dunce",
 "emojis",
 "fast-glob",
 "fastrand",
@@ -2698,6 +2705,7 @@ version = "0.15.0"
dependencies = [
 "anyhow",
 "chrono",
+
 "dunce",
 "git-ref-format",
 "human-panic",
 "lexopt",
@@ -2901,6 +2909,7 @@ dependencies = [
name = "radicle-remote-helper"
version = "0.12.0"
dependencies = [
+
 "dunce",
 "log",
 "radicle",
 "radicle-cli",
modified Cargo.toml
@@ -27,6 +27,7 @@ chrono = { version = "0.4.26", default-features = false }
colored = "2.1.0"
crossbeam-channel = "0.5.6"
cyphernet = "0.5.2"
+
dunce = "1.0.5"
fastrand = { version = "2.0.0", default-features = false }
git2 = { version = "0.19.0", default-features = false }
human-panic = "2"
modified crates/radicle-cli/Cargo.toml
@@ -16,6 +16,7 @@ path = "src/main.rs"
[dependencies]
anyhow = { workspace = true }
chrono = { workspace = true, features = ["clock", "std"] }
+
dunce = { workspace = true }
git-ref-format = { version = "0.3.0", features = ["macro"] }
human-panic.workspace = true
lexopt = { workspace = true }
modified crates/radicle-cli/src/commands/init.rs
@@ -212,10 +212,7 @@ pub fn init(
    options: Options,
    profile: &profile::Profile,
) -> anyhow::Result<()> {
-
    let path = repo
-
        .workdir()
-
        .unwrap_or_else(|| repo.path())
-
        .canonicalize()?;
+
    let path = dunce::canonicalize(repo.workdir().unwrap_or_else(|| repo.path()))?;
    let interactive = options.interactive;

    let default_branch = match find_default_branch(&repo) {
modified crates/radicle-remote-helper/Cargo.toml
@@ -14,6 +14,7 @@ name = "git-remote-rad"
path = "src/git-remote-rad.rs"

[dependencies]
+
dunce = { workspace = true }
log = { workspace = true }
radicle = { workspace = true }
radicle-cli = { workspace = true }
modified crates/radicle-remote-helper/src/lib.rs
@@ -162,7 +162,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {

                // N.b. `working` is the `.git` folder and `fetch::run`
                // requires the working directory.
-
                let working = working.map_err(|_| Error::NoGitDir)?.canonicalize()?;
+
                let working = dunce::canonicalize(working.map_err(|_| Error::NoGitDir)?)?;
                let working = working.parent().ok_or_else(|| Error::NoWorkingCopy {
                    path: working.clone(),
                })?;
modified crates/radicle/Cargo.toml
@@ -22,6 +22,7 @@ chrono = { workspace = true, features = ["clock"], optional = true }
colored = { workspace = true, optional = true }
crossbeam-channel = { workspace = true }
cyphernet = { workspace = true, features = ["tor", "dns", "p2p-ed25519"] }
+
dunce = { workspace = true }
fast-glob = { version = "0.3.2" }
fastrand = { workspace = true }
git2 = { workspace = true, features = ["vendored-libgit2"] }
modified crates/radicle/src/profile.rs
@@ -541,7 +541,7 @@ impl Home {
            fs::create_dir_all(path.clone())?;
        }
        let home = Self {
-
            path: path.canonicalize()?,
+
            path: dunce::canonicalize(path)?,
        };

        for dir in &[home.storage(), home.keys(), home.node(), home.cobs()] {
modified crates/radicle/src/rad.rs
@@ -127,7 +127,7 @@ where
        })?,
        [
            "push",
-
            &format!("{}", stored.path().canonicalize()?.display()),
+
            &format!("{}", dunce::canonicalize(stored.path())?.display()),
            &pushspec.to_string(),
        ],
        [],
@@ -295,9 +295,7 @@ pub fn checkout<P: AsRef<Path>, S: storage::ReadStorage>(
            "fetch",
            &format!(
                "{}",
-
                stored
-
                    .path()
-
                    .canonicalize()
+
                dunce::canonicalize(stored.path())
                    .map_err(CheckoutError::Fetch)?
                    .display()
            ),