mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Fix corrupted git checkout recovery.
This commit is contained in:
parent
ca190acce0
commit
c46e57b0cf
@ -151,30 +151,16 @@ impl GitDatabase {
|
|||||||
dest: &Path,
|
dest: &Path,
|
||||||
cargo_config: &Config,
|
cargo_config: &Config,
|
||||||
) -> CargoResult<GitCheckout<'_>> {
|
) -> CargoResult<GitCheckout<'_>> {
|
||||||
let mut checkout = None;
|
// If the existing checkout exists, and it is fresh, use it.
|
||||||
if let Ok(repo) = git2::Repository::open(dest) {
|
// A non-fresh checkout can happen if the checkout operation was
|
||||||
let mut co = GitCheckout::new(dest, self, rev, repo);
|
// interrupted. In that case, the checkout gets deleted and a new
|
||||||
if !co.is_fresh() {
|
// clone is created.
|
||||||
// After a successful fetch operation the subsequent reset can
|
let checkout = match git2::Repository::open(dest)
|
||||||
// fail sometimes for corrupt repositories where the fetch
|
.ok()
|
||||||
// operation succeeds but the object isn't actually there in one
|
.map(|repo| GitCheckout::new(dest, self, rev, repo))
|
||||||
// way or another. In these situations just skip the error and
|
.filter(|co| co.is_fresh())
|
||||||
// try blowing away the whole repository and trying with a
|
{
|
||||||
// clone.
|
Some(co) => co,
|
||||||
co.fetch(cargo_config)?;
|
|
||||||
match co.reset(cargo_config) {
|
|
||||||
Ok(()) => {
|
|
||||||
assert!(co.is_fresh());
|
|
||||||
checkout = Some(co);
|
|
||||||
}
|
|
||||||
Err(e) => debug!("failed reset after fetch {:?}", e),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
checkout = Some(co);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let checkout = match checkout {
|
|
||||||
Some(c) => c,
|
|
||||||
None => GitCheckout::clone_into(dest, self, rev, cargo_config)?,
|
None => GitCheckout::clone_into(dest, self, rev, cargo_config)?,
|
||||||
};
|
};
|
||||||
checkout.update_submodules(cargo_config)?;
|
checkout.update_submodules(cargo_config)?;
|
||||||
@ -311,14 +297,6 @@ impl<'a> GitCheckout<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, cargo_config: &Config) -> CargoResult<()> {
|
|
||||||
info!("fetch {}", self.repo.path().display());
|
|
||||||
let url = self.database.path.into_url()?;
|
|
||||||
let reference = GitReference::Rev(self.revision.to_string());
|
|
||||||
fetch(&mut self.repo, url.as_str(), &reference, cargo_config)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&self, config: &Config) -> CargoResult<()> {
|
fn reset(&self, config: &Config) -> CargoResult<()> {
|
||||||
// If we're interrupted while performing this reset (e.g., we die because
|
// If we're interrupted while performing this reset (e.g., we die because
|
||||||
// of a signal) Cargo needs to be sure to try to check out this repo
|
// of a signal) Cargo needs to be sure to try to check out this repo
|
||||||
|
@ -3510,3 +3510,65 @@ fn git_with_force_push() {
|
|||||||
amend_commit("awesome-four");
|
amend_commit("awesome-four");
|
||||||
verify("awesome-four");
|
verify("awesome-four");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn corrupted_checkout() {
|
||||||
|
// Test what happens if the checkout is corrupted somehow.
|
||||||
|
_corrupted_checkout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn corrupted_checkout_with_cli() {
|
||||||
|
// Test what happens if the checkout is corrupted somehow with git cli.
|
||||||
|
if disable_git_cli() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_corrupted_checkout(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _corrupted_checkout(with_cli: bool) {
|
||||||
|
let git_project = git::new("dep1", |project| {
|
||||||
|
project
|
||||||
|
.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
});
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"Cargo.toml",
|
||||||
|
&format!(
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dep1 = {{ git = "{}" }}
|
||||||
|
"#,
|
||||||
|
git_project.url()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("fetch").run();
|
||||||
|
|
||||||
|
let mut paths = t!(glob::glob(
|
||||||
|
paths::home()
|
||||||
|
.join(".cargo/git/checkouts/dep1-*/*")
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
));
|
||||||
|
let path = paths.next().unwrap().unwrap();
|
||||||
|
let ok = path.join(".cargo-ok");
|
||||||
|
|
||||||
|
// Deleting this file simulates an interrupted checkout.
|
||||||
|
t!(fs::remove_file(&ok));
|
||||||
|
|
||||||
|
// This should refresh the checkout.
|
||||||
|
let mut e = p.cargo("fetch");
|
||||||
|
if with_cli {
|
||||||
|
e.env("CARGO_NET_GIT_FETCH_WITH_CLI", "true");
|
||||||
|
}
|
||||||
|
e.run();
|
||||||
|
assert!(ok.exists());
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user