Skip to content

Commit c33d6d2

Browse files
committed
Auto merge of #12244 - krs98:fix-relative-git-submodules, r=weihanglo
fix: git submodules with relative urls ### What does this PR try to resolve? Fixes #12151. When recursing submodules, the url of the parent remote was being passed to `update_submodules` instead of the child remote url. This caused Cargo to try to add the wrong submodule. ### How should we test and review this PR? A test case is added in the first commit. The second one renames the `url` variable as suggested in the issue. The third includes the changes to fix the issue. The last one includes a minor refactor where a redundant `match` expr is removed.
2 parents 9e33d31 + 56318e0 commit c33d6d2

File tree

2 files changed

+87
-15
lines changed

2 files changed

+87
-15
lines changed

src/cargo/sources/git/utils.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,9 @@ impl<'a> GitCheckout<'a> {
444444
// See [`git submodule add`] documentation.
445445
//
446446
// [`git submodule add`]: https://git-scm.com/docs/git-submodule
447-
let url = if child_url_str.starts_with("./") || child_url_str.starts_with("../") {
447+
let child_remote_url = if child_url_str.starts_with("./")
448+
|| child_url_str.starts_with("../")
449+
{
448450
let mut new_parent_remote_url = parent_remote_url.clone();
449451

450452
let mut new_path = Cow::from(parent_remote_url.path());
@@ -453,17 +455,19 @@ impl<'a> GitCheckout<'a> {
453455
}
454456
new_parent_remote_url.set_path(&new_path);
455457

456-
match new_parent_remote_url.join(child_url_str) {
457-
Ok(x) => x.to_string(),
458-
Err(err) => Err(err).with_context(|| {
458+
new_parent_remote_url.join(child_url_str).with_context(|| {
459+
format!(
460+
"failed to parse relative child submodule url `{child_url_str}` \
461+
using parent base url `{new_parent_remote_url}`"
462+
)
463+
})?
464+
} else {
465+
Url::parse(child_url_str).with_context(|| {
466+
let child_module_name = child.name().unwrap_or("");
459467
format!(
460-
"failed to parse relative child submodule url `{}` using parent base url `{}`",
461-
child_url_str, new_parent_remote_url
468+
"failed to parse url for submodule `{child_module_name}`: `{child_url_str}`"
462469
)
463-
})?,
464-
}
465-
} else {
466-
child_url_str.to_string()
470+
})?
467471
};
468472

469473
// A submodule which is listed in .gitmodules but not actually
@@ -484,7 +488,7 @@ impl<'a> GitCheckout<'a> {
484488
let mut repo = match head_and_repo {
485489
Ok((head, repo)) => {
486490
if child.head_id() == head {
487-
return update_submodules(&repo, cargo_config, parent_remote_url);
491+
return update_submodules(&repo, cargo_config, &child_remote_url);
488492
}
489493
repo
490494
}
@@ -498,10 +502,10 @@ impl<'a> GitCheckout<'a> {
498502
let reference = GitReference::Rev(head.to_string());
499503
cargo_config
500504
.shell()
501-
.status("Updating", format!("git submodule `{}`", url))?;
505+
.status("Updating", format!("git submodule `{}`", child_remote_url))?;
502506
fetch(
503507
&mut repo,
504-
&url,
508+
child_remote_url.as_str(),
505509
&reference,
506510
cargo_config,
507511
RemoteKind::GitDependency,
@@ -510,13 +514,13 @@ impl<'a> GitCheckout<'a> {
510514
format!(
511515
"failed to fetch submodule `{}` from {}",
512516
child.name().unwrap_or(""),
513-
url
517+
child_remote_url
514518
)
515519
})?;
516520

517521
let obj = repo.find_object(head, None)?;
518522
reset(&repo, &obj, cargo_config)?;
519-
update_submodules(&repo, cargo_config, parent_remote_url)
523+
update_submodules(&repo, cargo_config, &child_remote_url)
520524
}
521525
}
522526
}

tests/testsuite/git.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3619,3 +3619,71 @@ fn cleans_temp_pack_files() {
36193619
p.cargo("generate-lockfile").run();
36203620
assert!(!tmp_path.exists());
36213621
}
3622+
3623+
#[cargo_test]
3624+
fn different_user_relative_submodules() {
3625+
let user1_git_project = git::new("user1/dep1", |project| {
3626+
project
3627+
.file("Cargo.toml", &basic_lib_manifest("dep1"))
3628+
.file("src/lib.rs", "")
3629+
});
3630+
3631+
let user2_git_project = git::new("user2/dep1", |project| {
3632+
project
3633+
.file("Cargo.toml", &basic_lib_manifest("dep1"))
3634+
.file("src/lib.rs", "")
3635+
});
3636+
let user2_git_project2 = git::new("user2/dep2", |project| {
3637+
project
3638+
.file("Cargo.toml", &basic_lib_manifest("dep1"))
3639+
.file("src/lib.rs", "")
3640+
});
3641+
3642+
let user2_repo = git2::Repository::open(&user2_git_project.root()).unwrap();
3643+
let url = "../dep2";
3644+
git::add_submodule(&user2_repo, url, Path::new("dep2"));
3645+
git::commit(&user2_repo);
3646+
3647+
let user1_repo = git2::Repository::open(&user1_git_project.root()).unwrap();
3648+
let url = user2_git_project.url();
3649+
git::add_submodule(&user1_repo, url.as_str(), Path::new("user2/dep1"));
3650+
git::commit(&user1_repo);
3651+
3652+
let project = project()
3653+
.file(
3654+
"Cargo.toml",
3655+
&format!(
3656+
r#"
3657+
[package]
3658+
name = "foo"
3659+
version = "0.5.0"
3660+
3661+
[dependencies.dep1]
3662+
git = '{}'
3663+
"#,
3664+
user1_git_project.url()
3665+
),
3666+
)
3667+
.file("src/main.rs", &main_file(r#""hello""#, &[]))
3668+
.build();
3669+
3670+
project
3671+
.cargo("build")
3672+
.with_stderr(&format!(
3673+
"\
3674+
[UPDATING] git repository `{}`
3675+
[UPDATING] git submodule `{}`
3676+
[UPDATING] git submodule `{}`
3677+
[COMPILING] dep1 v0.5.0 ({}#[..])
3678+
[COMPILING] foo v0.5.0 ([CWD])
3679+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
3680+
",
3681+
path2url(&user1_git_project.root()),
3682+
path2url(&user2_git_project.root()),
3683+
path2url(&user2_git_project2.root()),
3684+
path2url(&user1_git_project.root()),
3685+
))
3686+
.run();
3687+
3688+
assert!(project.bin("foo").is_file());
3689+
}

0 commit comments

Comments
 (0)