Fix updating submodules past failures
authorAlex Crichton <alex@alexcrichton.com>
Tue, 19 Dec 2017 15:34:14 +0000 (07:34 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 20 Dec 2017 15:00:43 +0000 (07:00 -0800)
If a submodule-of-a-submodule failed to update then Cargo the next time
around wouldn't automatically retry updating the next submodule. This commit
fixes that by ensuring that if a parent git repository looks updated we still
recurse into its own submodules to ensure they're all updated.

cc #4071

src/cargo/sources/git/utils.rs
tests/git.rs

index 4fac25bb2c25445cb872b3484405fa0b8ff42c15..f29bdb8fdb992c4c0c44089946b83d39204ce93c 100644 (file)
@@ -282,11 +282,10 @@ impl<'a> GitCheckout<'a> {
 
             for mut child in repo.submodules()? {
                 update_submodule(repo, &mut child, cargo_config)
-                    .map_err(Internal::new)
                     .chain_err(|| {
                         format!("failed to update submodule `{}`",
                                 child.name().unwrap_or(""))
-                })?;
+                    })?;
             }
             Ok(())
         }
@@ -308,8 +307,8 @@ impl<'a> GitCheckout<'a> {
 
             // If the submodule hasn't been checked out yet, we need to
             // clone it. If it has been checked out and the head is the same
-            // as the submodule's head, then we can bail out and go to the
-            // next submodule.
+            // as the submodule's head, then we can skip an update and keep
+            // recursing.
             let head_and_repo = child.open().and_then(|repo| {
                 let target = repo.head()?.target();
                 Ok((target, repo))
@@ -317,7 +316,7 @@ impl<'a> GitCheckout<'a> {
             let mut repo = match head_and_repo {
                 Ok((head, repo)) => {
                     if child.head_id() == head {
-                        return Ok(())
+                        return update_submodules(&repo, cargo_config)
                     }
                     repo
                 }
index c76c67f16b2a553026002ec017bb51613b2901d0..38c72a78ceec12344c3764ab138b1317e6450e22 100644 (file)
@@ -5,7 +5,9 @@ extern crate hamcrest;
 
 use std::fs::{self, File};
 use std::io::prelude::*;
+use std::net::TcpListener;
 use std::path::Path;
+use std::thread;
 
 use cargo::util::process;
 use cargotest::sleep_ms;
@@ -865,7 +867,9 @@ Caused by:
 Caused by:
   failed to update submodule `src`
 
-To learn more, run the command again with --verbose.\n", path2url(git_project.root()));
+Caused by:
+  object not found - no match for id [..]
+", path2url(git_project.root()));
 
     assert_that(p.cargo("build"),
                 execs().with_stderr(expected).with_status(101));
@@ -2174,3 +2178,75 @@ fn invalid_git_dependency_manifest() {
                              path2url(git_root),
                              )));
 }
+
+#[test]
+fn failed_submodule_checkout() {
+    let project = project("foo");
+    let git_project = git::new("dep1", |project| {
+        project
+            .file("Cargo.toml", r#"
+                [package]
+                name = "dep1"
+                version = "0.5.0"
+                authors = [""]
+            "#)
+    }).unwrap();
+
+    let git_project2 = git::new("dep2", |project| {
+        project.file("lib.rs", "")
+    }).unwrap();
+
+    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+    let addr = listener.local_addr().unwrap();
+
+    let t = thread::spawn(move || {
+        let a = listener.accept().unwrap();
+        drop(a);
+        let a = listener.accept().unwrap();
+        drop(a);
+    });
+
+    let repo = git2::Repository::open(&git_project2.root()).unwrap();
+    let url = format!("http://{}:{}/", addr.ip(), addr.port());
+    {
+        let mut s = repo.submodule(&url, Path::new("bar"), false).unwrap();
+        let subrepo = s.open().unwrap();
+        let mut cfg = subrepo.config().unwrap();
+        cfg.set_str("user.email", "foo@bar.com").unwrap();
+        cfg.set_str("user.name", "Foo Bar").unwrap();
+        git::commit(&subrepo);
+        s.add_finalize().unwrap();
+    }
+    git::commit(&repo);
+    drop((repo, url));
+
+    let repo = git2::Repository::open(&git_project.root()).unwrap();
+    let url = path2url(git_project2.root()).to_string();
+    git::add_submodule(&repo, &url, Path::new("src"));
+    git::commit(&repo);
+    drop(repo);
+
+    let project = project
+        .file("Cargo.toml", &format!(r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+
+            [dependencies]
+            dep1 = {{ git = '{}' }}
+        "#, git_project.url()))
+        .file("src/lib.rs", "")
+        .build();
+
+    assert_that(project.cargo("build"),
+                execs().with_status(101)
+                       .with_stderr_contains("  failed to update submodule `src`")
+                       .with_stderr_contains("  failed to update submodule `bar`"));
+    assert_that(project.cargo("build"),
+                execs().with_status(101)
+                       .with_stderr_contains("  failed to update submodule `src`")
+                       .with_stderr_contains("  failed to update submodule `bar`"));
+
+    t.join().unwrap();
+}