Skip to content

Commit caae926

Browse files
committed
Merge branch 'fix-1428'
2 parents 0162447 + 40d1881 commit caae926

File tree

5 files changed

+68
-42
lines changed

5 files changed

+68
-42
lines changed

.github/workflows/cifuzz.yml

Lines changed: 0 additions & 24 deletions
This file was deleted.

gitoxide-core/src/repository/remote.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ mod refs_impl {
222222
}
223223
}
224224
if refspecs.is_empty() {
225-
bail!("Without refspecs there is nothing to show here. Add refspecs as arguments or configure them in gix-config.")
225+
bail!("Without refspecs there is nothing to show here. Add refspecs as arguments or configure them in .git/config.")
226226
}
227227
Ok(())
228228
}

gix/src/clone/fetch/mod.rs

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ impl PrepareFetch {
128128

129129
// Add HEAD after the remote was written to config, we need it to know what to check out later, and assure
130130
// the ref that HEAD points to is present no matter what.
131+
let head_local_tracking_branch = format!("refs/remotes/{remote_name}/HEAD");
131132
let head_refspec = gix_refspec::parse(
132-
format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(),
133+
format!("HEAD:{head_local_tracking_branch}").as_str().into(),
133134
gix_refspec::parse::Operation::Fetch,
134135
)
135136
.expect("valid")
@@ -139,22 +140,48 @@ impl PrepareFetch {
139140
if let Some(f) = self.configure_connection.as_mut() {
140141
f(&mut connection).map_err(Error::RemoteConnection)?;
141142
}
142-
connection
143-
.prepare_fetch(&mut *progress, {
144-
let mut opts = self.fetch_options.clone();
145-
if !opts.extra_refspecs.contains(&head_refspec) {
146-
opts.extra_refspecs.push(head_refspec)
147-
}
148-
if let Some(ref_name) = &self.ref_name {
149-
opts.extra_refspecs.push(
150-
gix_refspec::parse(ref_name.as_ref().as_bstr(), gix_refspec::parse::Operation::Fetch)
151-
.expect("partial names are valid refspecs")
152-
.to_owned(),
153-
);
154-
}
155-
opts
156-
})
157-
.await?
143+
let mut fetch_opts = {
144+
let mut opts = self.fetch_options.clone();
145+
if !opts.extra_refspecs.contains(&head_refspec) {
146+
opts.extra_refspecs.push(head_refspec.clone())
147+
}
148+
if let Some(ref_name) = &self.ref_name {
149+
opts.extra_refspecs.push(
150+
gix_refspec::parse(ref_name.as_ref().as_bstr(), gix_refspec::parse::Operation::Fetch)
151+
.expect("partial names are valid refspecs")
152+
.to_owned(),
153+
);
154+
}
155+
opts
156+
};
157+
match connection.prepare_fetch(&mut *progress, fetch_opts.clone()).await {
158+
Ok(prepare) => prepare,
159+
Err(remote::fetch::prepare::Error::RefMap(remote::ref_map::Error::MappingValidation(err)))
160+
if err.issues.len() == 1
161+
&& fetch_opts.extra_refspecs.contains(&head_refspec)
162+
&& matches!(
163+
err.issues.first(),
164+
Some(gix_refspec::match_group::validate::Issue::Conflict {
165+
destination_full_ref_name,
166+
..
167+
}) if *destination_full_ref_name == head_local_tracking_branch
168+
) =>
169+
{
170+
let head_refspec_idx = fetch_opts
171+
.extra_refspecs
172+
.iter()
173+
.enumerate()
174+
.find_map(|(idx, spec)| (*spec == head_refspec).then_some(idx))
175+
.expect("it's contained");
176+
// On the very special occasion that we fail as there is a remote `refs/heads/HEAD` reference that clashes
177+
// with our implicit refspec, retry without it. Maybe this tells us that we shouldn't have that implicit
178+
// refspec, as git can do this without connecting twice.
179+
let connection = remote.connect(remote::Direction::Fetch).await?;
180+
fetch_opts.extra_refspecs.remove(head_refspec_idx);
181+
connection.prepare_fetch(&mut *progress, fetch_opts).await?
182+
}
183+
Err(err) => return Err(err.into()),
184+
}
158185
};
159186

160187
// Assure problems with custom branch names fail early, not after getting the pack or during negotiation.

gix/tests/clone/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,24 @@ mod blocking_io {
577577
Ok(())
578578
}
579579

580+
#[test]
581+
fn fetch_succeeds_despite_remote_head_ref() -> crate::Result {
582+
let tmp = gix_testtools::tempfile::TempDir::new()?;
583+
let remote_repo = remote::repo("head-ref");
584+
let mut prepare = gix::clone::PrepareFetch::new(
585+
remote_repo.path(),
586+
tmp.path(),
587+
gix::create::Kind::WithWorktree,
588+
Default::default(),
589+
restricted(),
590+
)?;
591+
592+
let (mut checkout, _out) = prepare.fetch_then_checkout(gix::progress::Discard, &AtomicBool::default())?;
593+
let (repo, _) = checkout.main_worktree(gix::progress::Discard, &AtomicBool::default())?;
594+
assert!(repo.head().is_ok(), "we could handle the HEAD normaller");
595+
Ok(())
596+
}
597+
580598
#[test]
581599
fn fetch_and_checkout_specific_annotated_tag() -> crate::Result {
582600
let tmp = gix_testtools::tempfile::TempDir::new()?;

gix/tests/fixtures/make_remote_repos.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ git clone --shared base clone
116116
git remote add myself .
117117
)
118118

119+
git clone --shared base head-ref
120+
(cd head-ref
121+
git rev-parse @ > .git/refs/heads/HEAD
122+
)
123+
119124
git clone --no-tags --shared base clone-no-tags
120125
(cd clone-no-tags
121126
git remote add --no-tags myself-no-tags .

0 commit comments

Comments
 (0)