Skip to content

Commit

Permalink
Auto merge of #9988 - ehuss:beta-git-fetch-force, r=alexcrichton
Browse files Browse the repository at this point in the history
[beta] Fix fetching git repos after a force push.

Beta backport of #9979.
  • Loading branch information
bors committed Oct 21, 2021
2 parents c7957a7 + ffa597e commit b2e52d7
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 6 deletions.
15 changes: 9 additions & 6 deletions src/cargo/sources/git/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,29 +785,32 @@ pub fn fetch(
// which need to get fetched. Additionally record if we're fetching tags.
let mut refspecs = Vec::new();
let mut tags = false;
// The `+` symbol on the refspec means to allow a forced (fast-forward)
// update which is needed if there is ever a force push that requires a
// fast-forward.
match reference {
// For branches and tags we can fetch simply one reference and copy it
// locally, no need to fetch other branches/tags.
GitReference::Branch(b) => {
refspecs.push(format!("refs/heads/{0}:refs/remotes/origin/{0}", b));
refspecs.push(format!("+refs/heads/{0}:refs/remotes/origin/{0}", b));
}
GitReference::Tag(t) => {
refspecs.push(format!("refs/tags/{0}:refs/remotes/origin/tags/{0}", t));
refspecs.push(format!("+refs/tags/{0}:refs/remotes/origin/tags/{0}", t));
}

GitReference::DefaultBranch => {
refspecs.push(String::from("HEAD:refs/remotes/origin/HEAD"));
refspecs.push(String::from("+HEAD:refs/remotes/origin/HEAD"));
}

GitReference::Rev(rev) => {
if rev.starts_with("refs/") {
refspecs.push(format!("{0}:{0}", rev));
refspecs.push(format!("+{0}:{0}", rev));
} else {
// We don't know what the rev will point to. To handle this
// situation we fetch all branches and tags, and then we pray
// it's somewhere in there.
refspecs.push(String::from("refs/heads/*:refs/remotes/origin/*"));
refspecs.push(String::from("HEAD:refs/remotes/origin/HEAD"));
refspecs.push(String::from("+refs/heads/*:refs/remotes/origin/*"));
refspecs.push(String::from("+HEAD:refs/remotes/origin/HEAD"));
tags = true;
}
}
Expand Down
102 changes: 102 additions & 0 deletions tests/testsuite/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3280,3 +3280,105 @@ fn metadata_master_consistency() {
let bar_source = format!("git+{}", git_project.url());
p.cargo("metadata").with_json(&metadata(&bar_source)).run();
}

#[cargo_test]
fn git_with_force_push() {
// Checks that cargo can handle force-pushes to git repos.
// This works by having a git dependency that is updated with an amend
// commit, and tries with various forms (default branch, branch, rev,
// tag).
let main = |text| format!(r#"pub fn f() {{ println!("{}"); }}"#, text);
let (git_project, repo) = git::new_repo("dep1", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("dep1"))
.file("src/lib.rs", &main("one"))
});
let manifest = |extra| {
format!(
r#"
[project]
name = "foo"
version = "0.0.1"
edition = "2018"
[dependencies]
dep1 = {{ git = "{}"{} }}
"#,
git_project.url(),
extra
)
};
let p = project()
.file("Cargo.toml", &manifest(""))
.file("src/main.rs", "fn main() { dep1::f(); }")
.build();
// Download the original and make sure it is OK.
p.cargo("build").run();
p.rename_run("foo", "foo1").with_stdout("one").run();

let find_head = || t!(t!(repo.head()).peel_to_commit());

let amend_commit = |text| {
// commit --amend a change that will require a force fetch.
git_project.change_file("src/lib.rs", &main(text));
git::add(&repo);
let commit = find_head();
let tree_id = t!(t!(repo.index()).write_tree());
t!(commit.amend(
Some("HEAD"),
None,
None,
None,
None,
Some(&t!(repo.find_tree(tree_id)))
));
};

let mut rename_annoyance = 1;

let mut verify = |text: &str| {
// Perform the fetch.
p.cargo("update").run();
p.cargo("build").run();
rename_annoyance += 1;
p.rename_run("foo", &format!("foo{}", rename_annoyance))
.with_stdout(text)
.run();
};

amend_commit("two");
verify("two");

// Try with a rev.
let head1 = find_head().id().to_string();
let extra = format!(", rev = \"{}\"", head1);
p.change_file("Cargo.toml", &manifest(&extra));
verify("two");
amend_commit("three");
let head2 = find_head().id().to_string();
assert_ne!(&head1, &head2);
let extra = format!(", rev = \"{}\"", head2);
p.change_file("Cargo.toml", &manifest(&extra));
verify("three");

// Try with a tag.
git::tag(&repo, "my-tag");
p.change_file("Cargo.toml", &manifest(", tag = \"my-tag\""));
verify("three");
amend_commit("tag-three");
let head = t!(t!(repo.head()).peel(git2::ObjectType::Commit));
t!(repo.tag("my-tag", &head, &t!(repo.signature()), "move tag", true));
verify("tag-three");

// Try with a branch.
let br = t!(repo.branch("awesome-stuff", &find_head(), false));
t!(repo.checkout_tree(&t!(br.get().peel(git2::ObjectType::Tree)), None));
t!(repo.set_head("refs/heads/awesome-stuff"));
git_project.change_file("src/lib.rs", &main("awesome-three"));
git::add(&repo);
git::commit(&repo);
p.change_file("Cargo.toml", &manifest(", branch = \"awesome-stuff\""));
verify("awesome-three");
amend_commit("awesome-four");
verify("awesome-four");
}

0 comments on commit b2e52d7

Please sign in to comment.