Skip to content

Commit 309b2ce

Browse files
committed
refactor(git): extracted fetch logic into different functions
1 parent f3edba9 commit 309b2ce

File tree

1 file changed

+169
-149
lines changed

1 file changed

+169
-149
lines changed

src/cargo/sources/git/utils.rs

Lines changed: 169 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,156 +1007,11 @@ pub fn fetch(
10071007
}
10081008

10091009
if let Some(true) = gctx.net_config()?.git_fetch_with_cli {
1010-
return fetch_with_cli(repo, remote_url, &refspecs, tags, gctx);
1011-
}
1012-
1013-
if gctx.cli_unstable().gitoxide.map_or(false, |git| git.fetch) {
1014-
let git2_repo = repo;
1015-
let config_overrides = cargo_config_to_gitoxide_overrides(gctx)?;
1016-
let repo_reinitialized = AtomicBool::default();
1017-
let res = oxide::with_retry_and_progress(
1018-
&git2_repo.path().to_owned(),
1019-
gctx,
1020-
&|repo_path,
1021-
should_interrupt,
1022-
mut progress,
1023-
url_for_authentication: &mut dyn FnMut(&gix::bstr::BStr)| {
1024-
// The `fetch` operation here may fail spuriously due to a corrupt
1025-
// repository. It could also fail, however, for a whole slew of other
1026-
// reasons (aka network related reasons). We want Cargo to automatically
1027-
// recover from corrupt repositories, but we don't want Cargo to stomp
1028-
// over other legitimate errors.
1029-
//
1030-
// Consequently we save off the error of the `fetch` operation and if it
1031-
// looks like a "corrupt repo" error then we blow away the repo and try
1032-
// again. If it looks like any other kind of error, or if we've already
1033-
// blown away the repository, then we want to return the error as-is.
1034-
loop {
1035-
let res = oxide::open_repo(
1036-
repo_path,
1037-
config_overrides.clone(),
1038-
oxide::OpenMode::ForFetch,
1039-
)
1040-
.map_err(crate::sources::git::fetch::Error::from)
1041-
.and_then(|repo| {
1042-
debug!("initiating fetch of {refspecs:?} from {remote_url}");
1043-
let url_for_authentication = &mut *url_for_authentication;
1044-
let remote = repo
1045-
.remote_at(remote_url)?
1046-
.with_fetch_tags(if tags {
1047-
gix::remote::fetch::Tags::All
1048-
} else {
1049-
gix::remote::fetch::Tags::Included
1050-
})
1051-
.with_refspecs(
1052-
refspecs.iter().map(|s| s.as_str()),
1053-
gix::remote::Direction::Fetch,
1054-
)
1055-
.map_err(crate::sources::git::fetch::Error::Other)?;
1056-
let url = remote
1057-
.url(gix::remote::Direction::Fetch)
1058-
.expect("set at init")
1059-
.to_owned();
1060-
let connection = remote.connect(gix::remote::Direction::Fetch)?;
1061-
let mut authenticate = connection.configured_credentials(url)?;
1062-
let connection = connection.with_credentials(
1063-
move |action: gix::protocol::credentials::helper::Action| {
1064-
if let Some(url) = action.context().and_then(|gctx| {
1065-
gctx.url.as_ref().filter(|url| *url != remote_url)
1066-
}) {
1067-
url_for_authentication(url.as_ref());
1068-
}
1069-
authenticate(action)
1070-
},
1071-
);
1072-
let outcome = connection
1073-
.prepare_fetch(&mut progress, gix::remote::ref_map::Options::default())?
1074-
.with_shallow(shallow.clone().into())
1075-
.receive(&mut progress, should_interrupt)?;
1076-
Ok(outcome)
1077-
});
1078-
let err = match res {
1079-
Ok(_) => break,
1080-
Err(e) => e,
1081-
};
1082-
debug!("fetch failed: {}", err);
1083-
1084-
if !repo_reinitialized.load(Ordering::Relaxed)
1085-
// We check for errors that could occur if the configuration, refs or odb files are corrupted.
1086-
// We don't check for errors related to writing as `gitoxide` is expected to create missing leading
1087-
// folder before writing files into it, or else not even open a directory as git repository (which is
1088-
// also handled here).
1089-
&& err.is_corrupted()
1090-
|| has_shallow_lock_file(&err)
1091-
{
1092-
repo_reinitialized.store(true, Ordering::Relaxed);
1093-
debug!(
1094-
"looks like this is a corrupt repository, reinitializing \
1095-
and trying again"
1096-
);
1097-
if oxide::reinitialize(repo_path).is_ok() {
1098-
continue;
1099-
}
1100-
}
1101-
1102-
return Err(err.into());
1103-
}
1104-
Ok(())
1105-
},
1106-
);
1107-
if repo_reinitialized.load(Ordering::Relaxed) {
1108-
*git2_repo = git2::Repository::open(git2_repo.path())?;
1109-
}
1110-
res
1010+
fetch_with_cli(repo, remote_url, &refspecs, tags, gctx)
1011+
} else if gctx.cli_unstable().gitoxide.map_or(false, |git| git.fetch) {
1012+
fetch_with_gitoxide(repo, remote_url, refspecs, tags, shallow, gctx)
11111013
} else {
1112-
debug!("doing a fetch for {remote_url}");
1113-
let git_config = git2::Config::open_default()?;
1114-
with_fetch_options(&git_config, remote_url, gctx, &mut |mut opts| {
1115-
if tags {
1116-
opts.download_tags(git2::AutotagOption::All);
1117-
}
1118-
if let gix::remote::fetch::Shallow::DepthAtRemote(depth) = shallow {
1119-
opts.depth(0i32.saturating_add_unsigned(depth.get()));
1120-
}
1121-
// The `fetch` operation here may fail spuriously due to a corrupt
1122-
// repository. It could also fail, however, for a whole slew of other
1123-
// reasons (aka network related reasons). We want Cargo to automatically
1124-
// recover from corrupt repositories, but we don't want Cargo to stomp
1125-
// over other legitimate errors.
1126-
//
1127-
// Consequently we save off the error of the `fetch` operation and if it
1128-
// looks like a "corrupt repo" error then we blow away the repo and try
1129-
// again. If it looks like any other kind of error, or if we've already
1130-
// blown away the repository, then we want to return the error as-is.
1131-
let mut repo_reinitialized = false;
1132-
loop {
1133-
debug!("initiating fetch of {refspecs:?} from {remote_url}");
1134-
let res =
1135-
repo.remote_anonymous(remote_url)?
1136-
.fetch(&refspecs, Some(&mut opts), None);
1137-
let err = match res {
1138-
Ok(()) => break,
1139-
Err(e) => e,
1140-
};
1141-
debug!("fetch failed: {}", err);
1142-
1143-
if !repo_reinitialized
1144-
&& matches!(err.class(), ErrorClass::Reference | ErrorClass::Odb)
1145-
{
1146-
repo_reinitialized = true;
1147-
debug!(
1148-
"looks like this is a corrupt repository, reinitializing \
1149-
and trying again"
1150-
);
1151-
if reinitialize(repo).is_ok() {
1152-
continue;
1153-
}
1154-
}
1155-
1156-
return Err(err.into());
1157-
}
1158-
Ok(())
1159-
})
1014+
fetch_with_libgit2(repo, remote_url, refspecs, tags, shallow, gctx)
11601015
}
11611016
}
11621017

@@ -1227,6 +1082,171 @@ fn fetch_with_cli(
12271082
Ok(())
12281083
}
12291084

1085+
fn fetch_with_gitoxide(
1086+
repo: &mut git2::Repository,
1087+
remote_url: &str,
1088+
refspecs: Vec<String>,
1089+
tags: bool,
1090+
shallow: gix::remote::fetch::Shallow,
1091+
gctx: &GlobalContext,
1092+
) -> CargoResult<()> {
1093+
let git2_repo = repo;
1094+
let config_overrides = cargo_config_to_gitoxide_overrides(gctx)?;
1095+
let repo_reinitialized = AtomicBool::default();
1096+
let res = oxide::with_retry_and_progress(
1097+
&git2_repo.path().to_owned(),
1098+
gctx,
1099+
&|repo_path,
1100+
should_interrupt,
1101+
mut progress,
1102+
url_for_authentication: &mut dyn FnMut(&gix::bstr::BStr)| {
1103+
// The `fetch` operation here may fail spuriously due to a corrupt
1104+
// repository. It could also fail, however, for a whole slew of other
1105+
// reasons (aka network related reasons). We want Cargo to automatically
1106+
// recover from corrupt repositories, but we don't want Cargo to stomp
1107+
// over other legitimate errors.
1108+
//
1109+
// Consequently we save off the error of the `fetch` operation and if it
1110+
// looks like a "corrupt repo" error then we blow away the repo and try
1111+
// again. If it looks like any other kind of error, or if we've already
1112+
// blown away the repository, then we want to return the error as-is.
1113+
loop {
1114+
let res = oxide::open_repo(
1115+
repo_path,
1116+
config_overrides.clone(),
1117+
oxide::OpenMode::ForFetch,
1118+
)
1119+
.map_err(crate::sources::git::fetch::Error::from)
1120+
.and_then(|repo| {
1121+
debug!("initiating fetch of {refspecs:?} from {remote_url}");
1122+
let url_for_authentication = &mut *url_for_authentication;
1123+
let remote = repo
1124+
.remote_at(remote_url)?
1125+
.with_fetch_tags(if tags {
1126+
gix::remote::fetch::Tags::All
1127+
} else {
1128+
gix::remote::fetch::Tags::Included
1129+
})
1130+
.with_refspecs(
1131+
refspecs.iter().map(|s| s.as_str()),
1132+
gix::remote::Direction::Fetch,
1133+
)
1134+
.map_err(crate::sources::git::fetch::Error::Other)?;
1135+
let url = remote
1136+
.url(gix::remote::Direction::Fetch)
1137+
.expect("set at init")
1138+
.to_owned();
1139+
let connection = remote.connect(gix::remote::Direction::Fetch)?;
1140+
let mut authenticate = connection.configured_credentials(url)?;
1141+
let connection = connection.with_credentials(
1142+
move |action: gix::protocol::credentials::helper::Action| {
1143+
if let Some(url) = action
1144+
.context()
1145+
.and_then(|gctx| gctx.url.as_ref().filter(|url| *url != remote_url))
1146+
{
1147+
url_for_authentication(url.as_ref());
1148+
}
1149+
authenticate(action)
1150+
},
1151+
);
1152+
let outcome = connection
1153+
.prepare_fetch(&mut progress, gix::remote::ref_map::Options::default())?
1154+
.with_shallow(shallow.clone().into())
1155+
.receive(&mut progress, should_interrupt)?;
1156+
Ok(outcome)
1157+
});
1158+
let err = match res {
1159+
Ok(_) => break,
1160+
Err(e) => e,
1161+
};
1162+
debug!("fetch failed: {}", err);
1163+
1164+
if !repo_reinitialized.load(Ordering::Relaxed)
1165+
// We check for errors that could occur if the configuration, refs or odb files are corrupted.
1166+
// We don't check for errors related to writing as `gitoxide` is expected to create missing leading
1167+
// folder before writing files into it, or else not even open a directory as git repository (which is
1168+
// also handled here).
1169+
&& err.is_corrupted()
1170+
|| has_shallow_lock_file(&err)
1171+
{
1172+
repo_reinitialized.store(true, Ordering::Relaxed);
1173+
debug!(
1174+
"looks like this is a corrupt repository, reinitializing \
1175+
and trying again"
1176+
);
1177+
if oxide::reinitialize(repo_path).is_ok() {
1178+
continue;
1179+
}
1180+
}
1181+
1182+
return Err(err.into());
1183+
}
1184+
Ok(())
1185+
},
1186+
);
1187+
if repo_reinitialized.load(Ordering::Relaxed) {
1188+
*git2_repo = git2::Repository::open(git2_repo.path())?;
1189+
}
1190+
res
1191+
}
1192+
1193+
fn fetch_with_libgit2(
1194+
repo: &mut git2::Repository,
1195+
remote_url: &str,
1196+
refspecs: Vec<String>,
1197+
tags: bool,
1198+
shallow: gix::remote::fetch::Shallow,
1199+
gctx: &GlobalContext,
1200+
) -> CargoResult<()> {
1201+
debug!("doing a fetch for {remote_url}");
1202+
let git_config = git2::Config::open_default()?;
1203+
with_fetch_options(&git_config, remote_url, gctx, &mut |mut opts| {
1204+
if tags {
1205+
opts.download_tags(git2::AutotagOption::All);
1206+
}
1207+
if let gix::remote::fetch::Shallow::DepthAtRemote(depth) = shallow {
1208+
opts.depth(0i32.saturating_add_unsigned(depth.get()));
1209+
}
1210+
// The `fetch` operation here may fail spuriously due to a corrupt
1211+
// repository. It could also fail, however, for a whole slew of other
1212+
// reasons (aka network related reasons). We want Cargo to automatically
1213+
// recover from corrupt repositories, but we don't want Cargo to stomp
1214+
// over other legitimate errors.
1215+
//
1216+
// Consequently we save off the error of the `fetch` operation and if it
1217+
// looks like a "corrupt repo" error then we blow away the repo and try
1218+
// again. If it looks like any other kind of error, or if we've already
1219+
// blown away the repository, then we want to return the error as-is.
1220+
let mut repo_reinitialized = false;
1221+
loop {
1222+
debug!("initiating fetch of {refspecs:?} from {remote_url}");
1223+
let res = repo
1224+
.remote_anonymous(remote_url)?
1225+
.fetch(&refspecs, Some(&mut opts), None);
1226+
let err = match res {
1227+
Ok(()) => break,
1228+
Err(e) => e,
1229+
};
1230+
debug!("fetch failed: {}", err);
1231+
1232+
if !repo_reinitialized && matches!(err.class(), ErrorClass::Reference | ErrorClass::Odb)
1233+
{
1234+
repo_reinitialized = true;
1235+
debug!(
1236+
"looks like this is a corrupt repository, reinitializing \
1237+
and trying again"
1238+
);
1239+
if reinitialize(repo).is_ok() {
1240+
continue;
1241+
}
1242+
}
1243+
1244+
return Err(err.into());
1245+
}
1246+
Ok(())
1247+
})
1248+
}
1249+
12301250
/// Attempts to `git gc` a repository.
12311251
///
12321252
/// Cargo has a bunch of long-lived git repositories in its global cache and

0 commit comments

Comments
 (0)