From 3e73307074b4a9190c9afa5b55daad525e1e299a Mon Sep 17 00:00:00 2001 From: Cibil Pankiras Date: Wed, 11 Mar 2026 18:37:39 +0100 Subject: [PATCH 1/2] test(git): Add test to fetch SCP-like submodule Signed-off-by: Cibil Pankiras --- tests/testsuite/git.rs | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 43698039533..c5242758647 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -4379,3 +4379,72 @@ fn dep_with_cached_submodule() { .collect::>(); assert_eq!(db_paths.len(), 1, "submodule db created once"); } + +#[cargo_test] +fn dep_with_scp_like_submodule_url() { + // Regression test for https://github.com/rust-lang/cargo/pull/16727 + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "pub fn dep() {}") + }); + let git_project2 = git::new("dep2", |project| project.file("lib.rs", "pub fn dep2() {}")); + + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let url = git_project2.root().to_url().to_string(); + git::add_submodule(&repo, &url, Path::new("submod")); + git::commit(&repo); + + git_project.change_file( + ".gitmodules", + "[submodule \"submod\"]\n\tpath = submod\n\turl = git@github.com:foo/bar.git", + ); + git::add(&repo); + git::commit(&repo); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + edition = "2015" + + [dependencies.dep1] + git = '{}' + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "extern crate dep1;") + .build(); + + // since Cargo can't parse SCP-like URL, it will fail earlier with invalid + // URL error before reaching the non-existing SSH server. + p.cargo("fetch") + .env( + "GIT_SSH_COMMAND", + "ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR", + ) + .with_status(101) + .with_stderr_data(str![[r#" +[UPDATING] git repository `[ROOTURL]/dep1` +[ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` + +Caused by: + failed to load source for dependency `dep1` + +Caused by: + unable to update [ROOTURL]/dep1 + +Caused by: + failed to update submodule `submod` + +Caused by: + invalid url `git@github.com:foo/bar.git`: relative URL without a base; try using `ssh://git@github.com/foo/bar.git` instead + +"#]]) + .run(); +} From dbb2b562caba7c1e5a86723b4bcbc5b6a4c12b2d Mon Sep 17 00:00:00 2001 From: Cibil Pankiras Date: Tue, 10 Mar 2026 13:50:36 +0100 Subject: [PATCH 2/2] fix(git): convert SCP-like submodule URLs to ssh:// format PR #16246 introduced a regression where submodules using SCP-like URLs fail because `child_remote_url.into_url()` requires WHATWG-parsable URLs. This commit fixes the issue by detecting SCP-like URLs in `absolute_submodule_url()` and converting them to the equivalent `ssh://` format. Signed-off-by: Cibil Pankiras --- src/cargo/sources/git/utils.rs | 30 ++++++++++++++++++++---------- tests/testsuite/git.rs | 10 ++++++---- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index e9e37066297..ac856134cd9 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -511,8 +511,9 @@ impl CheckoutGuard { /// (`git@github.com:rust-lang/cargo.git` is not a valid WHATWG URL) /// /// To overcome these, this patch always tries [`Url::parse`] first to normalize -/// the path. If it couldn't, append the relative path as the last resort and -/// pray the remote git service supports non-normalized URLs. +/// the path. If it couldn't, append the relative path and/or convert SCP-like URLs +/// to ssh:// format as the last resorts and pray the remote git service supports +/// non-normalized URLs. /// /// See also rust-lang/cargo#12404 and rust-lang/cargo#12295. /// @@ -546,6 +547,15 @@ fn absolute_submodule_url<'s>(base_url: &str, submodule_url: &'s str) -> CargoRe Cow::from(submodule_url) }; + let absolute_url = match gix::url::parse(gix::bstr::BStr::new(absolute_url.as_ref().as_bytes())) + { + Ok(mut url) if url.serialize_alternative_form && url.scheme == gix::url::Scheme::Ssh => { + url.serialize_alternative_form = false; + Cow::from(url.to_bstring().to_string()) + } + _ => absolute_url, + }; + Ok(absolute_url) } @@ -1623,7 +1633,7 @@ mod tests { ( "ssh://git@gitub.com/rust-lang/cargo", "git@github.com:rust-lang/cargo.git", - "git@github.com:rust-lang/cargo.git", + "ssh://git@github.com/rust-lang/cargo.git", ), ( "ssh://git@gitub.com/rust-lang/cargo", @@ -1668,37 +1678,37 @@ mod tests { ( "git@github.com:rust-lang/cargo.git", "./", - "git@github.com:rust-lang/cargo.git/./", + "ssh://git@github.com/rust-lang/cargo.git/./", ), ( "git@github.com:rust-lang/cargo.git", "../", - "git@github.com:rust-lang/cargo.git/../", + "ssh://git@github.com/rust-lang/cargo.git/../", ), ( "git@github.com:rust-lang/cargo.git", "./foo", - "git@github.com:rust-lang/cargo.git/./foo", + "ssh://git@github.com/rust-lang/cargo.git/./foo", ), ( "git@github.com:rust-lang/cargo.git/", "./foo", - "git@github.com:rust-lang/cargo.git/./foo", + "ssh://git@github.com/rust-lang/cargo.git/./foo", ), ( "git@github.com:rust-lang/cargo.git", "../foo", - "git@github.com:rust-lang/cargo.git/../foo", + "ssh://git@github.com/rust-lang/cargo.git/../foo", ), ( "git@github.com:rust-lang/cargo.git/", "../foo", - "git@github.com:rust-lang/cargo.git/../foo", + "ssh://git@github.com/rust-lang/cargo.git/../foo", ), ( "git@github.com:rust-lang/cargo.git", "../foo/bar/../baz", - "git@github.com:rust-lang/cargo.git/../foo/bar/../baz", + "ssh://git@github.com/rust-lang/cargo.git/../foo/bar/../baz", ), ]; diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index c5242758647..a7942b8337e 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -4421,8 +4421,9 @@ fn dep_with_scp_like_submodule_url() { .file("src/lib.rs", "extern crate dep1;") .build(); - // since Cargo can't parse SCP-like URL, it will fail earlier with invalid - // URL error before reaching the non-existing SSH server. + // With the SCP-like URL fix, Cargo converts `git@github.com:foo/bar.git` + // to `ssh://git@github.com/foo/bar.git` and tries to fetch, which fails + // with other errors like authentication failure or SSH server not reachable. p.cargo("fetch") .env( "GIT_SSH_COMMAND", @@ -4431,6 +4432,7 @@ fn dep_with_scp_like_submodule_url() { .with_status(101) .with_stderr_data(str![[r#" [UPDATING] git repository `[ROOTURL]/dep1` +[UPDATING] git submodule `ssh://git@github.com/foo/bar.git` [ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 ([ROOT]/foo)` Caused by: @@ -4443,8 +4445,8 @@ Caused by: failed to update submodule `submod` Caused by: - invalid url `git@github.com:foo/bar.git`: relative URL without a base; try using `ssh://git@github.com/foo/bar.git` instead - + failed to fetch submodule `submod` from ssh://git@github.com/foo/bar.git +... "#]]) .run(); }