Skip to content

Commit c46e57b

Browse files
committed
Fix corrupted git checkout recovery.
1 parent ca190ac commit c46e57b

File tree

2 files changed

+72
-32
lines changed

2 files changed

+72
-32
lines changed

src/cargo/sources/git/utils.rs

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -151,30 +151,16 @@ impl GitDatabase {
151151
dest: &Path,
152152
cargo_config: &Config,
153153
) -> CargoResult<GitCheckout<'_>> {
154-
let mut checkout = None;
155-
if let Ok(repo) = git2::Repository::open(dest) {
156-
let mut co = GitCheckout::new(dest, self, rev, repo);
157-
if !co.is_fresh() {
158-
// After a successful fetch operation the subsequent reset can
159-
// fail sometimes for corrupt repositories where the fetch
160-
// operation succeeds but the object isn't actually there in one
161-
// way or another. In these situations just skip the error and
162-
// try blowing away the whole repository and trying with a
163-
// clone.
164-
co.fetch(cargo_config)?;
165-
match co.reset(cargo_config) {
166-
Ok(()) => {
167-
assert!(co.is_fresh());
168-
checkout = Some(co);
169-
}
170-
Err(e) => debug!("failed reset after fetch {:?}", e),
171-
}
172-
} else {
173-
checkout = Some(co);
174-
}
175-
};
176-
let checkout = match checkout {
177-
Some(c) => c,
154+
// If the existing checkout exists, and it is fresh, use it.
155+
// A non-fresh checkout can happen if the checkout operation was
156+
// interrupted. In that case, the checkout gets deleted and a new
157+
// clone is created.
158+
let checkout = match git2::Repository::open(dest)
159+
.ok()
160+
.map(|repo| GitCheckout::new(dest, self, rev, repo))
161+
.filter(|co| co.is_fresh())
162+
{
163+
Some(co) => co,
178164
None => GitCheckout::clone_into(dest, self, rev, cargo_config)?,
179165
};
180166
checkout.update_submodules(cargo_config)?;
@@ -311,14 +297,6 @@ impl<'a> GitCheckout<'a> {
311297
}
312298
}
313299

314-
fn fetch(&mut self, cargo_config: &Config) -> CargoResult<()> {
315-
info!("fetch {}", self.repo.path().display());
316-
let url = self.database.path.into_url()?;
317-
let reference = GitReference::Rev(self.revision.to_string());
318-
fetch(&mut self.repo, url.as_str(), &reference, cargo_config)?;
319-
Ok(())
320-
}
321-
322300
fn reset(&self, config: &Config) -> CargoResult<()> {
323301
// If we're interrupted while performing this reset (e.g., we die because
324302
// of a signal) Cargo needs to be sure to try to check out this repo

tests/testsuite/git.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,3 +3510,65 @@ fn git_with_force_push() {
35103510
amend_commit("awesome-four");
35113511
verify("awesome-four");
35123512
}
3513+
3514+
#[cargo_test]
3515+
fn corrupted_checkout() {
3516+
// Test what happens if the checkout is corrupted somehow.
3517+
_corrupted_checkout(false);
3518+
}
3519+
3520+
#[cargo_test]
3521+
fn corrupted_checkout_with_cli() {
3522+
// Test what happens if the checkout is corrupted somehow with git cli.
3523+
if disable_git_cli() {
3524+
return;
3525+
}
3526+
_corrupted_checkout(true);
3527+
}
3528+
3529+
fn _corrupted_checkout(with_cli: bool) {
3530+
let git_project = git::new("dep1", |project| {
3531+
project
3532+
.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
3533+
.file("src/lib.rs", "")
3534+
});
3535+
let p = project()
3536+
.file(
3537+
"Cargo.toml",
3538+
&format!(
3539+
r#"
3540+
[package]
3541+
name = "foo"
3542+
version = "0.1.0"
3543+
3544+
[dependencies]
3545+
dep1 = {{ git = "{}" }}
3546+
"#,
3547+
git_project.url()
3548+
),
3549+
)
3550+
.file("src/lib.rs", "")
3551+
.build();
3552+
3553+
p.cargo("fetch").run();
3554+
3555+
let mut paths = t!(glob::glob(
3556+
paths::home()
3557+
.join(".cargo/git/checkouts/dep1-*/*")
3558+
.to_str()
3559+
.unwrap()
3560+
));
3561+
let path = paths.next().unwrap().unwrap();
3562+
let ok = path.join(".cargo-ok");
3563+
3564+
// Deleting this file simulates an interrupted checkout.
3565+
t!(fs::remove_file(&ok));
3566+
3567+
// This should refresh the checkout.
3568+
let mut e = p.cargo("fetch");
3569+
if with_cli {
3570+
e.env("CARGO_NET_GIT_FETCH_WITH_CLI", "true");
3571+
}
3572+
e.run();
3573+
assert!(ok.exists());
3574+
}

0 commit comments

Comments
 (0)