Skip to content

Commit 67c06d9

Browse files
committed
fix: make sure empty packs in shallow clones are working as well.
1 parent db69e31 commit 67c06d9

File tree

2 files changed

+68
-30
lines changed

2 files changed

+68
-30
lines changed

gix/src/remote/connection/fetch/negotiate.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub(crate) fn mark_complete_and_common_ref(
9090
let mut cutoff_date = None::<SecondsSinceUnixEpoch>;
9191
let mut num_mappings_with_change = 0;
9292
let mut remote_ref_target_known: Vec<bool> = std::iter::repeat(false).take(ref_map.mappings.len()).collect();
93+
let mut remote_ref_included: Vec<bool> = std::iter::repeat(false).take(ref_map.mappings.len()).collect();
9394

9495
for (mapping_idx, mapping) in ref_map.mappings.iter().enumerate() {
9596
let want_id = mapping.remote.as_id();
@@ -99,7 +100,9 @@ pub(crate) fn mark_complete_and_common_ref(
99100
r.target().try_id().map(ToOwned::to_owned)
100101
});
101102

103+
// Even for ignored mappings we want to know if the `want` is already present locally, so skip nothing else.
102104
if !mapping_is_ignored(mapping) {
105+
remote_ref_included[mapping_idx] = true;
103106
// Like git, we don't let known unchanged mappings participate in the tree traversal
104107
if want_id.zip(have_id).map_or(true, |(want, have)| want != have) {
105108
num_mappings_with_change += 1;
@@ -120,7 +123,12 @@ pub(crate) fn mark_complete_and_common_ref(
120123
if matches!(shallow, Shallow::NoChange) {
121124
if num_mappings_with_change == 0 {
122125
return Ok(Action::NoChange);
123-
} else if remote_ref_target_known.iter().all(|known| *known) {
126+
} else if remote_ref_target_known
127+
.iter()
128+
.zip(remote_ref_included)
129+
.filter_map(|(known, included)| included.then_some(known))
130+
.all(|known| *known)
131+
{
124132
return Ok(Action::SkipToRefUpdate);
125133
}
126134
}

gix/tests/remote/fetch.rs

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -335,38 +335,68 @@ mod blocking_and_async_io {
335335
async(feature = "async-network-client-async-std", async_std::test)
336336
)]
337337
async fn fetch_empty_pack() -> crate::Result {
338-
let daemon = spawn_git_daemon_if_async(repo_path("base"))?;
339-
for (fetch_tags, expected_ref_count) in [
340-
(fetch::Tags::None, 1),
341-
(fetch::Tags::Included, 7),
342-
(fetch::Tags::All, 7),
338+
for version in [
339+
gix::protocol::transport::Protocol::V1,
340+
gix::protocol::transport::Protocol::V2,
343341
] {
344-
let (repo, _tmp) = repo_rw("two-origins"); // TODO: also try shallow clones
345-
let mut remote = into_daemon_remote_if_async(
346-
repo.head()?
347-
.into_remote(Fetch)
348-
.expect("present")?
349-
.with_fetch_tags(fetch_tags),
350-
daemon.as_ref(),
351-
None,
352-
);
353-
remote.replace_refspecs(Some("HEAD:refs/remotes/origin/does-not-yet-exist"), Fetch)?;
354-
355-
let res = remote
356-
.connect(Fetch)
357-
.await?
358-
.prepare_fetch(gix::progress::Discard, Default::default())
359-
.await?
360-
.receive(gix::progress::Discard, &AtomicBool::default())
361-
.await?;
342+
for (shallow_args, expected) in [(None, [1, 7, 7]), (Some("--depth=2"), [1, 2, 2])] {
343+
if version == gix::protocol::transport::Protocol::V1 && shallow_args.is_some() {
344+
// TODO: We cannot yet handle shallow mode for V1 as it will send shallow-info as part of the handshake :/.
345+
// It's probaby not the most important thing to be able to clone from a shallow remote anyway.
346+
continue;
347+
}
348+
for (fetch_tags, expected_ref_count) in [fetch::Tags::None, fetch::Tags::Included, fetch::Tags::All]
349+
.into_iter()
350+
.zip(expected)
351+
{
352+
let (mut repo, _tmp) = try_repo_rw_args(
353+
"two-origins",
354+
shallow_args,
355+
if shallow_args.is_some() {
356+
Mode::CloneWithShallowSupport
357+
} else {
358+
Mode::FastClone
359+
},
360+
)?;
361+
let daemon = spawn_git_daemon_if_async(
362+
repo.work_dir()
363+
.expect("non-bare")
364+
.ancestors()
365+
.nth(1)
366+
.expect("parent")
367+
.join("base"),
368+
)?;
369+
repo.config_snapshot_mut().set_value(
370+
&gix::config::tree::Protocol::VERSION,
371+
(version as u8).to_string().as_str(),
372+
)?;
373+
let mut remote = into_daemon_remote_if_async(
374+
repo.head()?
375+
.into_remote(Fetch)
376+
.expect("present")?
377+
.with_fetch_tags(fetch_tags),
378+
daemon.as_ref(),
379+
None,
380+
);
381+
remote.replace_refspecs(Some("HEAD:refs/remotes/origin/does-not-yet-exist"), Fetch)?;
382+
383+
let res = remote
384+
.connect(Fetch)
385+
.await?
386+
.prepare_fetch(gix::progress::Discard, Default::default())
387+
.await?
388+
.receive(gix::progress::Discard, &AtomicBool::default())
389+
.await?;
362390

363-
match res.status {
364-
fetch::Status::NoPackReceived { update_refs } => {
365-
assert_eq!(update_refs.edits.len(), expected_ref_count);
391+
match res.status {
392+
fetch::Status::NoPackReceived { update_refs } => {
393+
assert_eq!(update_refs.edits.len(), expected_ref_count, "{shallow_args:?}|{fetch_tags:?}");
394+
},
395+
_ => unreachable!(
396+
"{shallow_args:?}|{fetch_tags:?}: default negotiation is able to realize nothing is required and doesn't get to receiving a pack"
397+
),
398+
}
366399
}
367-
_ => unreachable!(
368-
"default negotiation is able to realize nothing is required and doesn't get to receiving a pack"
369-
),
370400
}
371401
}
372402
Ok(())

0 commit comments

Comments
 (0)