Skip to content

Commit

Permalink
Enable shallow clones and fetches for registry and git dependencies.
Browse files Browse the repository at this point in the history
The implementation hinges on passing information about the kind of clone
and fetch to the `fetch()` method, which then configures the fetch accordingly.

Note that it doesn't differentiate between initial clones and fetches as
the shallow-ness of the repository is maintained nonetheless.
  • Loading branch information
Byron committed Mar 14, 2023
1 parent 41412a1 commit 45f874a
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ filetime = "0.2.9"
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
git2 = "0.16.0"
git2-curl = "0.17.0"
gix = { version = "0.41.0", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] }
gix = { git = "https://github.com/Byron/gitoxide", branch = "main", version = "0.41.0", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] }
gix-features-for-configuration-only = { version = "0.28.0", package = "gix-features", features = [ "parallel" ] }
glob = "0.3.0"
hex = "0.4"
Expand Down
9 changes: 9 additions & 0 deletions src/cargo/sources/git/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,14 @@ mod source;
mod utils;

pub mod fetch {
/// The kind remote repository to fetch.
#[derive(Debug, Copy, Clone)]
pub enum RemoteKind {
/// A repository belongs to a git dependency.
GitDependency,
/// A repository belongs to a Cargo registry.
Registry,
}

pub type Error = gix::env::collate::fetch::Error<gix::refspec::parse::Error>;
}
67 changes: 62 additions & 5 deletions src/cargo/sources/git/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Utilities for handling git repositories, mainly around
//! authentication/cloning.

use crate::core::features::GitoxideFeatures;
use crate::core::{GitReference, Verbosity};
use crate::sources::git::fetch::RemoteKind;
use crate::sources::git::oxide;
use crate::sources::git::oxide::cargo_config_to_gitoxide_overrides;
use crate::util::errors::CargoResult;
Expand Down Expand Up @@ -96,9 +98,16 @@ impl GitRemote {
// if we can. If that can successfully load our revision then we've
// populated the database with the latest version of `reference`, so
// return that database and the rev we resolve to.
let remote_kind = RemoteKind::GitDependency;
if let Some(mut db) = db {
fetch(&mut db.repo, self.url.as_str(), reference, cargo_config)
.context(format!("failed to fetch into: {}", into.display()))?;
fetch(
&mut db.repo,
self.url.as_str(),
reference,
cargo_config,
remote_kind,
)
.context(format!("failed to fetch into: {}", into.display()))?;
match locked_rev {
Some(rev) => {
if db.contains(rev) {
Expand All @@ -121,8 +130,14 @@ impl GitRemote {
}
paths::create_dir_all(into)?;
let mut repo = init(into, true)?;
fetch(&mut repo, self.url.as_str(), reference, cargo_config)
.context(format!("failed to clone into: {}", into.display()))?;
fetch(
&mut repo,
self.url.as_str(),
reference,
cargo_config,
remote_kind,
)
.context(format!("failed to clone into: {}", into.display()))?;
let rev = match locked_rev {
Some(rev) => rev,
None => reference.resolve(&repo)?,
Expand Down Expand Up @@ -282,6 +297,12 @@ impl<'a> GitCheckout<'a> {
.with_checkout(checkout)
.fetch_options(fopts)
.clone(url.as_str(), into)?;
if database.repo.is_shallow() {
std::fs::copy(
database.repo.path().join("shallow"),
r.path().join("shallow"),
)?;
}
repo = Some(r);
Ok(())
})?;
Expand Down Expand Up @@ -432,7 +453,14 @@ impl<'a> GitCheckout<'a> {
cargo_config
.shell()
.status("Updating", format!("git submodule `{}`", url))?;
fetch(&mut repo, &url, &reference, cargo_config).with_context(|| {
fetch(
&mut repo,
&url,
&reference,
cargo_config,
RemoteKind::GitDependency,
)
.with_context(|| {
format!(
"failed to fetch submodule `{}` from {}",
child.name().unwrap_or(""),
Expand Down Expand Up @@ -803,11 +831,14 @@ pub fn with_fetch_options(
})
}

/// Note that `kind` is only needed to know how to interpret `gitoxide` feature options to potentially shallow-clone
/// the repository.
pub fn fetch(
repo: &mut git2::Repository,
orig_url: &str,
reference: &GitReference,
config: &Config,
kind: RemoteKind,
) -> CargoResult<()> {
if config.frozen() {
anyhow::bail!(
Expand Down Expand Up @@ -893,6 +924,25 @@ pub fn fetch(
let git2_repo = repo;
let config_overrides = cargo_config_to_gitoxide_overrides(config)?;
let repo_reinitialized = AtomicBool::default();
let has_feature = |cb: &dyn Fn(GitoxideFeatures) -> bool| {
config
.cli_unstable()
.gitoxide
.map_or(false, |features| cb(features))
};
let shallow = if git2_repo.is_shallow() {
gix::remote::fetch::Shallow::NoChange
} else {
match kind {
RemoteKind::GitDependency if has_feature(&|git| git.shallow_deps) => {
gix::remote::fetch::Shallow::DepthAtRemote(1.try_into().expect("non-zero"))
}
RemoteKind::Registry if has_feature(&|git| git.shallow_index) => {
gix::remote::fetch::Shallow::DepthAtRemote(1.try_into().expect("non-zero"))
}
_ => gix::remote::fetch::Shallow::NoChange,
}
};
let res = oxide::with_retry_and_progress(
&git2_repo.path().to_owned(),
config,
Expand Down Expand Up @@ -952,6 +1002,7 @@ pub fn fetch(
);
let outcome = connection
.prepare_fetch(gix::remote::ref_map::Options::default())?
.with_shallow(shallow.clone())
.receive(should_interrupt)?;
Ok(outcome)
});
Expand Down Expand Up @@ -1005,6 +1056,12 @@ pub fn fetch(
// again. If it looks like any other kind of error, or if we've already
// blown away the repository, then we want to return the error as-is.
let mut repo_reinitialized = false;
// while shallow repos aren't officially supported, don't risk fetching them.
// We are in this situation only when `gitoxide` is cloning but then disabled to use `git2`
// for fetching.
if repo.is_shallow() {
reinitialize(repo)?;
}
loop {
debug!("initiating fetch of {:?} from {}", refspecs, orig_url);
let res = repo
Expand Down
11 changes: 9 additions & 2 deletions src/cargo/sources/registry/remote.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::git::fetch::RemoteKind;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
Expand Down Expand Up @@ -300,8 +301,14 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
// checkout.
let url = self.source_id.url();
let repo = self.repo.borrow_mut().unwrap();
git::fetch(repo, url.as_str(), &self.index_git_ref, self.config)
.with_context(|| format!("failed to fetch `{}`", url))?;
git::fetch(
repo,
url.as_str(),
&self.index_git_ref,
self.config,
RemoteKind::Registry,
)
.with_context(|| format!("failed to fetch `{}`", url))?;

// Create a dummy file to record the mtime for when we updated the
// index.
Expand Down
Loading

0 comments on commit 45f874a

Please sign in to comment.