Skip to content

Commit

Permalink
fix: make sure that refs/heads/HEAD isn't considered conflicting (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Jul 9, 2024
1 parent 3965ed8 commit 7953829
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 17 deletions.
61 changes: 44 additions & 17 deletions gix/src/clone/fetch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ impl PrepareFetch {

// Add HEAD after the remote was written to config, we need it to know what to check out later, and assure
// the ref that HEAD points to is present no matter what.
let head_local_tracking_branch = format!("refs/remotes/{remote_name}/HEAD");
let head_refspec = gix_refspec::parse(
format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(),
format!("HEAD:{head_local_tracking_branch}").as_str().into(),
gix_refspec::parse::Operation::Fetch,
)
.expect("valid")
Expand All @@ -139,22 +140,48 @@ impl PrepareFetch {
if let Some(f) = self.configure_connection.as_mut() {
f(&mut connection).map_err(Error::RemoteConnection)?;
}
connection
.prepare_fetch(&mut *progress, {
let mut opts = self.fetch_options.clone();
if !opts.extra_refspecs.contains(&head_refspec) {
opts.extra_refspecs.push(head_refspec)
}
if let Some(ref_name) = &self.ref_name {
opts.extra_refspecs.push(
gix_refspec::parse(ref_name.as_ref().as_bstr(), gix_refspec::parse::Operation::Fetch)
.expect("partial names are valid refspecs")
.to_owned(),
);
}
opts
})
.await?
let mut fetch_opts = {
let mut opts = self.fetch_options.clone();
if !opts.extra_refspecs.contains(&head_refspec) {
opts.extra_refspecs.push(head_refspec.clone())
}
if let Some(ref_name) = &self.ref_name {
opts.extra_refspecs.push(
gix_refspec::parse(ref_name.as_ref().as_bstr(), gix_refspec::parse::Operation::Fetch)
.expect("partial names are valid refspecs")
.to_owned(),
);
}
opts
};
match connection.prepare_fetch(&mut *progress, fetch_opts.clone()).await {
Ok(prepare) => prepare,
Err(remote::fetch::prepare::Error::RefMap(remote::ref_map::Error::MappingValidation(err)))
if err.issues.len() == 1
&& fetch_opts.extra_refspecs.contains(&head_refspec)
&& matches!(
err.issues.first(),
Some(gix_refspec::match_group::validate::Issue::Conflict {
destination_full_ref_name,
..
}) if *destination_full_ref_name == head_local_tracking_branch
) =>
{
let head_refspec_idx = fetch_opts
.extra_refspecs
.iter()
.enumerate()
.find_map(|(idx, spec)| (*spec == head_refspec).then_some(idx))
.expect("it's contained");
// On the very special occasion that we fail as there is a remote `refs/heads/HEAD` reference that clashes
// with our implicit refspec, retry without it. Maybe this tells us that we shouldn't have that implicit
// refspec, as git can do this without connecting twice.
let connection = remote.connect(remote::Direction::Fetch).await?;
fetch_opts.extra_refspecs.remove(head_refspec_idx);
connection.prepare_fetch(&mut *progress, fetch_opts).await?
}
Err(err) => return Err(err.into()),
}
};

// Assure problems with custom branch names fail early, not after getting the pack or during negotiation.
Expand Down
18 changes: 18 additions & 0 deletions gix/tests/clone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,24 @@ mod blocking_io {
Ok(())
}

#[test]
fn fetch_succeeds_despite_remote_head_ref() -> crate::Result {
let tmp = gix_testtools::tempfile::TempDir::new()?;
let remote_repo = remote::repo("head-ref");
let mut prepare = gix::clone::PrepareFetch::new(
remote_repo.path(),
tmp.path(),
gix::create::Kind::WithWorktree,
Default::default(),
restricted(),
)?;

let (mut checkout, _out) = prepare.fetch_then_checkout(gix::progress::Discard, &AtomicBool::default())?;
let (repo, _) = checkout.main_worktree(gix::progress::Discard, &AtomicBool::default())?;
assert!(repo.head().is_ok(), "we could handle the HEAD normaller");
Ok(())
}

#[test]
fn fetch_and_checkout_specific_annotated_tag() -> crate::Result {
let tmp = gix_testtools::tempfile::TempDir::new()?;
Expand Down
5 changes: 5 additions & 0 deletions gix/tests/fixtures/make_remote_repos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ git clone --shared base clone
git remote add myself .
)

git clone --shared base head-ref
(cd head-ref
git rev-parse @ > .git/refs/heads/HEAD
)

git clone --no-tags --shared base clone-no-tags
(cd clone-no-tags
git remote add --no-tags myself-no-tags .
Expand Down

0 comments on commit 7953829

Please sign in to comment.