Enable shallow clones and fetches for registry and git dependencies.

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.
This commit is contained in:
Sebastian Thiel 2023-03-13 11:33:09 +01:00
parent 0ad289b264
commit c7ff94fce8
No known key found for this signature in database
GPG key ID: 9CB5EE7895E8268B
8 changed files with 953 additions and 90 deletions

130
Cargo.lock generated
View file

@ -284,6 +284,7 @@ dependencies = [
"pathdiff",
"pretty_env_logger",
"rand",
"rustc-workspace-hack",
"rustfix",
"same-file",
"semver",
@ -920,7 +921,7 @@ checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"redox_syscall 0.2.16",
"windows-sys 0.48.0",
]
@ -1028,9 +1029,9 @@ dependencies = [
[[package]]
name = "gix"
version = "0.39.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dabfac58aecb4a38cdd2568de66eb1f0d968fd6726f5a80cb8bea7944ef10cc0"
checksum = "fd5e0d9c5df90c9b4d325ec716762beb7d6c1465a4049fec5c4f6b72e7824656"
dependencies = [
"gix-actor",
"gix-attributes",
@ -1129,9 +1130,9 @@ dependencies = [
[[package]]
name = "gix-config"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52c62e26ce11f607712e4f49a0a192ed87675d30187fd61be070abbd607d12f1"
checksum = "6aa7d7dd60256b7a0c0506a1d708ec92767c2662ee57b3301b538eaa3e064f8a"
dependencies = [
"bstr",
"gix-config-value",
@ -1163,9 +1164,9 @@ dependencies = [
[[package]]
name = "gix-credentials"
version = "0.11.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be32b5fe339a31b8e53fa854081dc914c45020dcb64637f3c21baf69c96fc1b"
checksum = "750b684197374518ea057e0a0594713e07683faa0a3f43c0f93d97f64130ad8d"
dependencies = [
"bstr",
"gix-command",
@ -1203,9 +1204,9 @@ dependencies = [
[[package]]
name = "gix-discover"
version = "0.15.0"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91c204adba5ebd211c74735cbb65817d277e154486bac0dffa3701f163b80350"
checksum = "6eba8ba458cb8f4a6c33409b0fe650b1258655175a7ffd1d24fafd3ed31d880b"
dependencies = [
"bstr",
"dunce",
@ -1269,9 +1270,9 @@ dependencies = [
[[package]]
name = "gix-index"
version = "0.14.0"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c12caf7886c7ba06f2b28835cdc2be1dca86bd047d00299d2d49e707ce1c2616"
checksum = "717ab601ece7921f59fe86849dbe27d44a46ebb883b5885732c4f30df4996177"
dependencies = [
"bitflags",
"bstr",
@ -1291,9 +1292,9 @@ dependencies = [
[[package]]
name = "gix-lock"
version = "4.0.0"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66119ff8a4a395d0ea033fef718bc85f8b4f0855874f4ce1e005fc16cfe1f66e"
checksum = "41b80172055c5d8017a48ddac5cc7a95421c00211047db0165c97853c4f05194"
dependencies = [
"fastrand",
"gix-tempfile",
@ -1332,9 +1333,9 @@ dependencies = [
[[package]]
name = "gix-odb"
version = "0.42.0"
version = "0.43.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9a5f9e1afbd509761977a2ea02869cedaaba500b4e783deb2e4de5179a55a80"
checksum = "e83af2e3e36005bfe010927f0dff41fb5acc3e3d89c6f1174135b3a34086bda2"
dependencies = [
"arc-swap",
"gix-features",
@ -1350,9 +1351,9 @@ dependencies = [
[[package]]
name = "gix-pack"
version = "0.32.0"
version = "0.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51db84e1459a8022e518d40a8778028d793dbb28e4d35c9a5eaf92658fb0775"
checksum = "9401911c7fe032ad7b31c6a6b5be59cb283d1d6c999417a8215056efe6d635f3"
dependencies = [
"clru",
"gix-chunk",
@ -1372,9 +1373,9 @@ dependencies = [
[[package]]
name = "gix-packetline"
version = "0.14.3"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d63e5e5a9a92d4fc6b63ff9d94954d25c779ce25c98d5bbe2e4399aa42f7073c"
checksum = "3841ff51b395ab481c05d77551f5b41956022f50c1efba9d84fcd422f579ecd4"
dependencies = [
"bstr",
"hex",
@ -1406,9 +1407,9 @@ dependencies = [
[[package]]
name = "gix-protocol"
version = "0.28.0"
version = "0.30.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d372ab11d5d28ac21800e3f1a6603a67c1ead57f6f5fab07e1e73e960f331c1"
checksum = "7bd38498bfdd5cd6dffa4477f78d43ac2b921bccf0aa7158994d252aaf825f40"
dependencies = [
"bstr",
"btoi",
@ -1434,9 +1435,9 @@ dependencies = [
[[package]]
name = "gix-ref"
version = "0.26.0"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90a0ed29e581f04b904ecd0c32b11f33b8209b5a0af9c43f415249a4f2fba632"
checksum = "e4e909396ed3b176823991ccc391c276ae2a015e54edaafa3566d35123cfac9d"
dependencies = [
"gix-actor",
"gix-features",
@ -1494,9 +1495,9 @@ dependencies = [
[[package]]
name = "gix-tempfile"
version = "4.1.1"
version = "5.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88751f247234b1f73c8e8056fd835a0999b04e596e052302cb71186005dc4b27"
checksum = "c2ceb30a610e3f5f2d5f9a5114689fde507ba9417705a8cf3429604275b2153c"
dependencies = [
"libc",
"once_cell",
@ -1508,9 +1509,9 @@ dependencies = [
[[package]]
name = "gix-transport"
version = "0.27.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d633947b36a2fbbc089195bdc71621158f1660c2ff2a6b12b0279c16e2f764bc"
checksum = "a6c9094646b467be7198cf7663cb532ea2adbc05cc114da7097f8682ac4ca739"
dependencies = [
"base64",
"bstr",
@ -1539,9 +1540,9 @@ dependencies = [
[[package]]
name = "gix-url"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "044072b7ce8601b62dcec841b92129f5cc677072823324121b395d766ac5f528"
checksum = "b6a22b4b32ad14d68f7b7fb6458fa58d44b01797d94c1b8f4db2d9c7b3c366b5"
dependencies = [
"bstr",
"gix-features",
@ -1563,9 +1564,9 @@ dependencies = [
[[package]]
name = "gix-worktree"
version = "0.14.0"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cb9af6e56152953d8fe113c4f9d7cf60cf7a982362711e9200a255579b49cb"
checksum = "54ec9a000b4f24af706c3cc680c7cda235656cbe3216336522f5692773b8a301"
dependencies = [
"bstr",
"gix-attributes",
@ -1892,9 +1893,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.142"
version = "0.2.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]]
name = "libgit2-sys"
@ -1954,9 +1955,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.3.2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f508063cc7bb32987c71511216bd5a32be15bccb6a80b52df8b9d7f01fc3aa2"
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
[[package]]
name = "lock_api"
@ -2162,9 +2163,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "111.25.3+1.1.1t"
version = "111.25.2+1.1.1t"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924757a6a226bf60da5f7dd0311a34d2b52283dd82ddeb103208ddc66362f80c"
checksum = "320708a054ad9b3bf314688b5db87cf4d6683d64cfc835e2337924ae62bf4431"
dependencies = [
"cc",
]
@ -2252,7 +2253,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"redox_syscall 0.2.16",
"smallvec",
"windows-sys 0.45.0",
]
@ -2321,9 +2322,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pest"
version = "2.5.7"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122"
checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70"
dependencies = [
"thiserror",
"ucd-trie",
@ -2331,9 +2332,9 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.5.7"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15"
checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb"
dependencies = [
"pest",
"pest_generator",
@ -2341,9 +2342,9 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.5.7"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e"
checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e"
dependencies = [
"pest",
"pest_meta",
@ -2354,9 +2355,9 @@ dependencies = [
[[package]]
name = "pest_meta"
version = "2.5.7"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e"
checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411"
dependencies = [
"once_cell",
"pest",
@ -2594,6 +2595,15 @@ dependencies = [
"bitflags",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.3"
@ -2601,7 +2611,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom",
"redox_syscall",
"redox_syscall 0.2.16",
"thiserror",
]
@ -2628,15 +2638,6 @@ version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "resolver-tests"
version = "0.0.0"
@ -2665,6 +2666,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-workspace-hack"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb"
[[package]]
name = "rustfix"
version = "0.6.1"
@ -3060,16 +3067,15 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.3.0"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
"redox_syscall 0.3.5",
"rustix",
"windows-sys 0.45.0",
]
[[package]]

View file

@ -42,7 +42,7 @@ filetime = "0.2.9"
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
git2 = "0.17.0"
git2-curl = "0.18.0"
gix = { version = "0.39.0", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] }
gix = { version = "0.42.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"
@ -63,6 +63,7 @@ libgit2-sys = "0.15.0"
log = "0.4.6"
memchr = "2.1.3"
opener = "0.5"
openssl = { version = '0.10.11', optional = true }
os_info = "3.5.0"
pasetors = { version = "0.6.4", features = ["v3", "paserk", "std", "serde"] }
pathdiff = "0.2"
@ -88,8 +89,10 @@ unicode-xid = "0.2.0"
url = "2.2.2"
walkdir = "2.2"
[target.'cfg(not(windows))'.dependencies]
openssl = { version = "0.10.50", optional = true }
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
# for more information.
rustc-workspace-hack = "1.0.0"
[target.'cfg(windows)'.dependencies]
fwdansi = "1.1.0"
@ -126,5 +129,3 @@ doc = false
vendored-openssl = ["openssl/vendored"]
vendored-libgit2 = ["libgit2-sys/vendored"]
pretty-env-logger = ["pretty_env_logger"]
# This is primarily used by rust-lang/rust distributing cargo the executable.
all-static = ['vendored-openssl', 'curl/static-curl', 'curl/force-system-lib-on-osx']

View file

@ -6,5 +6,66 @@ mod source;
mod utils;
pub mod fetch {
use crate::core::features::GitoxideFeatures;
use crate::Config;
/// 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 git dependency, and due to usage of checking out specific revisions we can't
/// use shallow clones.
GitDependencyForbidShallow,
/// A repository belongs to a Cargo registry.
Registry,
}
#[derive(Debug, Clone)]
pub enum History {
Shallow(gix::remote::fetch::Shallow),
Unshallow,
}
impl From<History> for gix::remote::fetch::Shallow {
fn from(value: History) -> Self {
match value {
History::Unshallow => gix::remote::fetch::Shallow::undo(),
History::Shallow(how) => how,
}
}
}
impl RemoteKind {
/// Obtain the kind of history we would want for a fetch from our remote knowing if the target repo is already shallow
/// via `repo_is_shallow` along with gitoxide-specific feature configuration via `config`.
pub(crate) fn to_history(&self, repo_is_shallow: bool, config: &Config) -> History {
let has_feature = |cb: &dyn Fn(GitoxideFeatures) -> bool| {
config
.cli_unstable()
.gitoxide
.map_or(false, |features| cb(features))
};
let how = if repo_is_shallow {
if matches!(self, RemoteKind::GitDependencyForbidShallow) {
return History::Unshallow;
} else {
gix::remote::fetch::Shallow::NoChange
}
} else {
match self {
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,
}
};
History::Shallow(how)
}
}
pub type Error = gix::env::collate::fetch::Error<gix::refspec::parse::Error>;
}

View file

@ -29,6 +29,10 @@ pub fn with_retry_and_progress(
) -> CargoResult<()> {
std::thread::scope(|s| {
let mut progress_bar = Progress::new("Fetch", config);
let is_shallow = config
.cli_unstable()
.gitoxide
.map_or(false, |gix| gix.shallow_deps || gix.shallow_index);
network::retry::with_retry(config, || {
let progress_root: Arc<gix::progress::tree::Root> =
gix::progress::tree::root::Options {
@ -50,7 +54,7 @@ pub fn with_retry_and_progress(
);
amend_authentication_hints(res, urls.get_mut().take())
});
translate_progress_to_bar(&mut progress_bar, root)?;
translate_progress_to_bar(&mut progress_bar, root, is_shallow)?;
thread.join().expect("no panic in scoped thread")
})
})
@ -59,7 +63,9 @@ pub fn with_retry_and_progress(
fn translate_progress_to_bar(
progress_bar: &mut Progress<'_>,
root: Weak<gix::progress::tree::Root>,
is_shallow: bool,
) -> CargoResult<()> {
let remote_progress: gix::progress::Id = gix::remote::fetch::ProgressId::RemoteProgress.into();
let read_pack_bytes: gix::progress::Id =
gix::odb::pack::bundle::write::ProgressId::ReadPackBytes.into();
let delta_index_objects: gix::progress::Id =
@ -88,6 +94,7 @@ fn translate_progress_to_bar(
"progress should be smoother by keeping these as multiples of each other"
);
let num_phases = if is_shallow { 3 } else { 2 }; // indexing + delta-resolution, both with same amount of objects to handle
while let Some(root) = root.upgrade() {
std::thread::sleep(sleep_interval);
let needs_update = last_fast_update.elapsed() >= fast_check_interval;
@ -102,31 +109,37 @@ fn translate_progress_to_bar(
fn progress_by_id(
id: gix::progress::Id,
task: &gix::progress::Task,
) -> Option<&gix::progress::Value> {
(task.id == id).then(|| task.progress.as_ref()).flatten()
) -> Option<(&str, &gix::progress::Value)> {
(task.id == id)
.then(|| task.progress.as_ref())
.flatten()
.map(|value| (task.name.as_str(), value))
}
fn find_in<K>(
tasks: &[(K, gix::progress::Task)],
cb: impl Fn(&gix::progress::Task) -> Option<&gix::progress::Value>,
) -> Option<&gix::progress::Value> {
cb: impl Fn(&gix::progress::Task) -> Option<(&str, &gix::progress::Value)>,
) -> Option<(&str, &gix::progress::Value)> {
tasks.iter().find_map(|(_, t)| cb(t))
}
const NUM_PHASES: usize = 2; // indexing + delta-resolution, both with same amount of objects to handle
if let Some(objs) = find_in(&tasks, |t| progress_by_id(resolve_objects, t)) {
// Resolving deltas.
if let Some((_, objs)) = find_in(&tasks, |t| progress_by_id(resolve_objects, t)) {
// Phase 3: Resolving deltas.
let objects = objs.step.load(Ordering::Relaxed);
let total_objects = objs.done_at.expect("known amount of objects");
let msg = format!(", ({objects}/{total_objects}) resolving deltas");
progress_bar.tick(total_objects + objects, total_objects * NUM_PHASES, &msg)?;
progress_bar.tick(
(total_objects * (num_phases - 1)) + objects,
total_objects * num_phases,
&msg,
)?;
} else if let Some((objs, read_pack)) =
find_in(&tasks, |t| progress_by_id(read_pack_bytes, t)).and_then(|read| {
find_in(&tasks, |t| progress_by_id(delta_index_objects, t))
.map(|delta| (delta, read))
.map(|delta| (delta.1, read.1))
})
{
// Receiving objects.
// Phase 2: Receiving objects.
let objects = objs.step.load(Ordering::Relaxed);
let total_objects = objs.done_at.expect("known amount of objects");
let received_bytes = read_pack.step.load(Ordering::Relaxed);
@ -139,7 +152,25 @@ fn translate_progress_to_bar(
let (rate, unit) = human_readable_bytes(counter.rate() as u64);
let msg = format!(", {rate:.2}{unit}/s");
progress_bar.tick(objects, total_objects * NUM_PHASES, &msg)?;
progress_bar.tick(
(total_objects * (num_phases - 2)) + objects,
total_objects * num_phases,
&msg,
)?;
} else if let Some((action, remote)) =
find_in(&tasks, |t| progress_by_id(remote_progress, t))
{
if !is_shallow {
continue;
}
// phase 1: work on the remote side
// Resolving deltas.
let objects = remote.step.load(Ordering::Relaxed);
if let Some(total_objects) = remote.done_at {
let msg = format!(", ({objects}/{total_objects}) {action}");
progress_bar.tick(objects, total_objects * num_phases, &msg)?;
}
}
}
Ok(())

View file

@ -2,6 +2,7 @@
//! authentication/cloning.
use crate::core::{GitReference, Verbosity};
use crate::sources::git::fetch::{History, RemoteKind};
use crate::sources::git::oxide;
use crate::sources::git::oxide::cargo_config_to_gitoxide_overrides;
use crate::util::errors::CargoResult;
@ -96,9 +97,21 @@ 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 = if locked_rev.is_some() || matches!(reference, GitReference::Rev(_)) {
RemoteKind::GitDependencyForbidShallow
} else {
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()))?;
let history = remote_kind.to_history(db.repo.is_shallow(), cargo_config);
fetch(
&mut db.repo,
self.url.as_str(),
reference,
cargo_config,
history,
)
.context(format!("failed to fetch into: {}", into.display()))?;
match locked_rev {
Some(rev) => {
if db.contains(rev) {
@ -121,8 +134,15 @@ 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()))?;
let history = remote_kind.to_history(repo.is_shallow(), cargo_config);
fetch(
&mut repo,
self.url.as_str(),
reference,
cargo_config,
history,
)
.context(format!("failed to clone into: {}", into.display()))?;
let rev = match locked_rev {
Some(rev) => rev,
None => reference.resolve(&repo)?,
@ -282,6 +302,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(())
})?;
@ -432,7 +458,8 @@ impl<'a> GitCheckout<'a> {
cargo_config
.shell()
.status("Updating", format!("git submodule `{}`", url))?;
fetch(&mut repo, &url, &reference, cargo_config).with_context(|| {
let history = RemoteKind::GitDependency.to_history(repo.is_shallow(), cargo_config);
fetch(&mut repo, &url, &reference, cargo_config, history).with_context(|| {
format!(
"failed to fetch submodule `{}` from {}",
child.name().unwrap_or(""),
@ -803,11 +830,15 @@ pub fn with_fetch_options(
})
}
/// Note that `history` is a complex computed value to determine whether it's acceptable to perform shallow clones
/// at all. It's needed to allow the caller to determine the correct position of the destination repository or move it
/// into place should its position change.
pub fn fetch(
repo: &mut git2::Repository,
orig_url: &str,
reference: &GitReference,
config: &Config,
history: History,
) -> CargoResult<()> {
if config.frozen() {
anyhow::bail!(
@ -952,6 +983,7 @@ pub fn fetch(
);
let outcome = connection
.prepare_fetch(gix::remote::ref_map::Options::default())?
.with_shallow(history.clone().into())
.receive(should_interrupt)?;
Ok(outcome)
});
@ -967,6 +999,7 @@ pub fn fetch(
// folder before writing files into it, or else not even open a directory as git repository (which is
// also handled here).
&& err.is_corrupted()
|| has_shallow_lock_file(&err)
{
repo_reinitialized.store(true, Ordering::Relaxed);
debug!(
@ -1005,6 +1038,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
@ -1036,6 +1075,17 @@ pub fn fetch(
}
}
/// `gitoxide` uses shallow locks to assure consistency when fetching to and to avoid races, and to write
/// files atomically.
/// Cargo has its own lock files and doesn't need that mechanism for race protection, so a stray lock means
/// a signal interrupted a previous shallow fetch and doesn't mean a race is happening.
fn has_shallow_lock_file(err: &crate::sources::git::fetch::Error) -> bool {
matches!(
err,
gix::env::collate::fetch::Error::Fetch(gix::remote::fetch::Error::LockShallowFile(_))
)
}
fn fetch_with_cli(
repo: &mut git2::Repository,
url: &str,

View file

@ -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};
@ -304,8 +305,15 @@ 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))?;
let history = RemoteKind::Registry.to_history(repo.is_shallow(), self.config);
git::fetch(
repo,
url.as_str(),
&self.index_git_ref,
self.config,
history,
)
.with_context(|| format!("failed to fetch `{}`", url))?;
// Create a dummy file to record the mtime for when we updated the
// index.

View file

@ -9,6 +9,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use crate::git_gc::find_index;
use cargo_test_support::git::cargo_uses_gitoxide;
use cargo_test_support::paths::{self, CargoPathExt};
use cargo_test_support::registry::Package;
@ -548,7 +549,11 @@ Caused by:
}
#[cargo_test]
fn two_revs_same_deps() {
fn gitoxide_clones_shallow_two_revs_same_deps() {
perform_two_revs_same_deps(true)
}
fn perform_two_revs_same_deps(shallow: bool) {
let bar = git::new("meta-dep", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.0.0"))
@ -626,11 +631,23 @@ fn two_revs_same_deps() {
)
.build();
foo.cargo("build -v").run();
let args = if shallow {
"build -v -Zgitoxide=fetch,shallow-deps"
} else {
"build -v"
};
foo.cargo(args)
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert!(foo.bin("foo").is_file());
foo.process(&foo.bin("foo")).run();
}
#[cargo_test]
fn two_revs_same_deps() {
perform_two_revs_same_deps(false)
}
#[cargo_test]
fn recompilation() {
let git_project = git::new("bar", |project| {
@ -1828,6 +1845,680 @@ fn fetch_downloads() {
p.cargo("fetch").with_stdout("").run();
}
#[cargo_test]
fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_with_git2_fetch(
) -> anyhow::Result<()> {
Package::new("bar", "1.0.0").publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("fetch")
.arg("-Zgitoxide=fetch,shallow-index")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?;
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
1,
"shallow clones always start at depth of 1 to minimize download size"
);
assert!(repo.is_shallow());
Package::new("bar", "1.1.0").publish();
p.cargo("update")
.env("__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2", "0")
.run();
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
3,
"the repo was forcefully reinitialized and fetch again with full history - that way we take control and know the state of the repo \
instead of allowing a non-shallow aware implementation to cause trouble later"
);
assert!(!repo.is_shallow());
Ok(())
}
#[cargo_test]
fn gitoxide_clones_git_dependency_with_shallow_protocol_and_git2_is_used_for_followup_fetches(
) -> anyhow::Result<()> {
// Example where an old lockfile with an explicit branch="master" in Cargo.toml.
Package::new("bar", "1.0.0").publish();
let (bar, bar_repo) = git::new_repo("bar", |p| {
p.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
.file("src/lib.rs", "")
});
bar.change_file("src/lib.rs", "// change");
git::add(&bar_repo);
git::commit(&bar_repo);
{
let mut walk = bar_repo.revwalk()?;
walk.push_head()?;
assert_eq!(
walk.count(),
2,
"original repo has initial commit and change commit"
);
}
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = {{ version = "1.0", git = "{}", branch = "master" }}
"#,
bar.url()
),
)
.file("src/lib.rs", "")
.build();
p.cargo("update")
.arg("-Zgitoxide=fetch,shallow-deps")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let db_clone = gix::open_opts(
glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())?
.next()
.unwrap()?,
gix::open::Options::isolated(),
)?;
assert!(db_clone.is_shallow());
assert_eq!(
db_clone
.rev_parse_single("origin/master")?
.ancestors()
.all()?
.count(),
1,
"db clones are shallow and have a shortened history"
);
let dep_checkout = gix::open_opts(
glob::glob(
paths::home()
.join(".cargo/git/checkouts/bar-*/*/.git")
.to_str()
.unwrap(),
)?
.next()
.unwrap()?,
gix::open::Options::isolated(),
)?;
assert!(dep_checkout.is_shallow());
assert_eq!(
dep_checkout.head_id()?.ancestors().all()?.count(),
1,
"db checkouts are hard-linked clones with the shallow file copied separately."
);
bar.change_file("src/lib.rs", "// another change");
git::add(&bar_repo);
git::commit(&bar_repo);
{
let mut walk = bar_repo.revwalk()?;
walk.push_head()?;
assert_eq!(
walk.count(),
3,
"original repo has initial commit and change commit, and another change"
);
}
p.cargo("update")
.env("__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2", "0")
.run();
let db_clone = gix::open_opts(
glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())?
.next()
.unwrap()?,
gix::open::Options::isolated(),
)?;
assert_eq!(
db_clone
.rev_parse_single("origin/master")?
.ancestors()
.all()?
.count(),
3,
"the db clone was re-initialized and has all commits"
);
assert!(
!db_clone.is_shallow(),
"shallow-ness was removed as git2 does not support it"
);
assert_eq!(
dep_checkout.head_id()?.ancestors().all()?.count(),
1,
"the original dep checkout didn't change - there is a new one for each update we get locally"
);
let max_history_depth = glob::glob(
paths::home()
.join(".cargo/git/checkouts/bar-*/*/.git")
.to_str()
.unwrap(),
)?
.map(|path| -> anyhow::Result<usize> {
let dep_checkout = gix::open_opts(path?, gix::open::Options::isolated())?;
let depth = dep_checkout.head_id()?.ancestors().all()?.count();
assert_eq!(dep_checkout.is_shallow(), depth == 1, "the first checkout is done with gitoxide and shallow, the second one is git2 non-shallow");
Ok(depth)
})
.map(Result::unwrap)
.max()
.expect("two checkout repos");
assert_eq!(
max_history_depth, 3,
"the new checkout sees all commits of the non-shallow DB repository"
);
Ok(())
}
#[cargo_test]
fn gitoxide_clones_git_dependency_with_shallow_protocol_and_follow_up_fetch_maintains_shallowness(
) -> anyhow::Result<()> {
Package::new("bar", "1.0.0").publish();
let (bar, bar_repo) = git::new_repo("bar", |p| {
p.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
.file("src/lib.rs", "")
});
bar.change_file("src/lib.rs", "// change");
git::add(&bar_repo);
git::commit(&bar_repo);
{
let mut walk = bar_repo.revwalk()?;
walk.push_head()?;
assert_eq!(
walk.count(),
2,
"original repo has initial commit and change commit"
);
}
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = {{ version = "1.0", git = "{}", branch = "master" }}
"#,
bar.url()
),
)
.file("src/lib.rs", "")
.build();
p.cargo("update")
.arg("-Zgitoxide=fetch,shallow-deps")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let db_clone = gix::open_opts(
glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())?
.next()
.unwrap()?,
gix::open::Options::isolated(),
)?;
assert!(db_clone.is_shallow());
assert_eq!(
db_clone
.rev_parse_single("origin/master")?
.ancestors()
.all()?
.count(),
1,
"db clones are shallow and have a shortened history"
);
let dep_checkout = gix::open_opts(
glob::glob(
paths::home()
.join(".cargo/git/checkouts/bar-*/*/.git")
.to_str()
.unwrap(),
)?
.next()
.unwrap()?,
gix::open::Options::isolated(),
)?;
assert!(dep_checkout.is_shallow());
assert_eq!(
dep_checkout.head_id()?.ancestors().all()?.count(),
1,
"db checkouts are hard-linked clones with the shallow file copied separately."
);
bar.change_file("src/lib.rs", "// another change");
git::add(&bar_repo);
git::commit(&bar_repo);
{
let mut walk = bar_repo.revwalk()?;
walk.push_head()?;
assert_eq!(
walk.count(),
3,
"original repo has initial commit and change commit, and another change"
);
}
p.cargo("update")
.arg("-Zgitoxide=fetch") // shallow-deps is omitted intentionally
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert_eq!(
db_clone
.rev_parse_single("origin/master")?
.ancestors()
.all()?
.count(),
2,
"the new commit was fetched into our DB clone"
);
assert!(db_clone.is_shallow());
assert_eq!(
dep_checkout.head_id()?.ancestors().all()?.count(),
1,
"the original dep checkout didn't change - there is a new one for each update we get locally"
);
let max_history_depth = glob::glob(
paths::home()
.join(".cargo/git/checkouts/bar-*/*/.git")
.to_str()
.unwrap(),
)?
.map(|path| -> anyhow::Result<usize> {
let dep_checkout = gix::open_opts(path?, gix::open::Options::isolated())?;
assert!(dep_checkout.is_shallow());
let depth = dep_checkout.head_id()?.ancestors().all()?.count();
Ok(depth)
})
.map(Result::unwrap)
.max()
.expect("two checkout repos");
assert_eq!(
max_history_depth, 2,
"the new checkout sees all commits of the DB"
);
Ok(())
}
#[cargo_test]
fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_fetch_maintains_shallowness(
) -> anyhow::Result<()> {
Package::new("bar", "1.0.0").publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("fetch")
.arg("-Zgitoxide=fetch,shallow-index")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?;
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
1,
"shallow clones always start at depth of 1 to minimize download size"
);
assert!(repo.is_shallow());
Package::new("bar", "1.1.0").publish();
p.cargo("update")
.arg("-Zgitoxide=fetch") // NOTE: intentionally missing shallow flag
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
2,
"follow-up fetches maintain shallow-ness, whether it's specified or not, keeping shallow boundary where it is"
);
assert!(repo.is_shallow());
Package::new("bar", "1.2.0").publish();
Package::new("bar", "1.3.0").publish();
p.cargo("update")
.arg("-Zgitoxide=fetch,shallow-index")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
4,
"even if depth (at remote) is specified again, the current shallow boundary is maintained and not moved"
);
assert!(repo.is_shallow());
Ok(())
}
#[cargo_test]
fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_shallowness(
) -> anyhow::Result<()> {
Package::new("bar", "1.0.0").publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("fetch")
.arg("-Zgitoxide=fetch")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?;
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
2,
"initial commit and the first crate"
);
assert!(!repo.is_shallow());
Package::new("bar", "1.1.0").publish();
p.cargo("update")
.arg("-Zgitoxide=fetch,shallow-index")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
1,
"follow-up fetches maintain can shallow an existing unshallow repo - this doesn't have any benefit as we still have the objects locally"
);
assert!(repo.is_shallow());
Package::new("bar", "1.2.0").publish();
Package::new("bar", "1.3.0").publish();
p.cargo("update")
.arg("-Zgitoxide=fetch,shallow-index")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
3,
"even if depth (at remote) is specified again, the current shallow boundary is maintained and not moved"
);
assert!(repo.is_shallow());
Ok(())
}
#[cargo_test]
fn gitoxide_unshallows_git_dependencies_that_may_not_be_shallow_anymore() -> anyhow::Result<()> {
// db exists from previous build, then dependency changes to refer to revision that isn't
// available in the shallow clone.
let (bar, bar_repo) = git::new_repo("bar", |p| {
p.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
.file("src/lib.rs", "")
});
// this commit would not be available in a shallow clone.
let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap();
bar.change_file("src/lib.rs", "// change");
git::add(&bar_repo);
git::commit(&bar_repo);
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = {{ git = "{}", branch = "master" }}
"#,
bar.url(),
),
)
.file("src/lib.rs", "")
.build();
p.cargo("check")
.arg("-Zgitoxide=fetch,shallow-deps")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let db_clone = gix::open_opts(
glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())?
.next()
.unwrap()?,
gix::open::Options::isolated(),
)?;
assert!(db_clone.is_shallow());
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = {{ git = "{}", rev = "{}" }}
"#,
bar.url(),
first_commit_pre_change
),
)
.file("src/lib.rs", "")
.build();
p.cargo("check")
.arg("-Zgitoxide=fetch,shallow-deps")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert!(
!db_clone.is_shallow(),
"the clone was unshallowed as we need the entire history for revision based checkouts"
);
Ok(())
}
#[cargo_test]
fn gitoxide_uses_shallow_deps_only_when_no_revision_is_specified() -> anyhow::Result<()> {
let (bar, bar_repo) = git::new_repo("bar", |p| {
p.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
.file("src/lib.rs", "")
});
// this commit would not be available in a shallow clone.
let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap();
bar.change_file("src/lib.rs", "// change");
git::add(&bar_repo);
git::commit(&bar_repo);
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar-renamed = {{ package = "bar", git = "{}", rev = "{}" }}
bar = {{ git = "{}", branch = "master" }}
"#,
bar.url(),
first_commit_pre_change,
bar.url(),
),
)
.file("src/lib.rs", "")
.build();
p.cargo("check")
.arg("-Zgitoxide=fetch,shallow-deps")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let db_paths = glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())?
.map(Result::unwrap)
.collect::<Vec<_>>();
assert_eq!(
db_paths.len(),
1,
"only one db checkout source is used per dependency"
);
let db_clone = gix::open_opts(&db_paths[0], gix::open::Options::isolated())?;
assert!(
!db_clone.is_shallow(),
"despite there being two checkouts, it manages to see that the db must not be shallow"
);
Ok(())
}
#[cargo_test]
fn gitoxide_clones_registry_with_shallow_protocol_and_aborts_and_updates_again(
) -> anyhow::Result<()> {
Package::new("bar", "1.0.0").publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("fetch")
.arg("-Zgitoxide=fetch,shallow-index")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?;
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
1,
"shallow clones always start at depth of 1 to minimize download size"
);
assert!(repo.is_shallow());
let shallow_lock = repo.shallow_file().with_extension("lock");
// adding a lock file and deleting the original simulates a left-over clone that was aborted, leaving a lock file
// in place without ever having moved it to the right location.
std::fs::write(&shallow_lock, &[])?;
std::fs::remove_file(repo.shallow_file())?;
Package::new("bar", "1.1.0").publish();
p.cargo("update")
.arg("-Zgitoxide=fetch,shallow-index")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"])
.run();
assert!(!shallow_lock.is_file(), "the repository was re-initialized");
assert!(repo.is_shallow());
assert_eq!(
repo.rev_parse_single("origin/HEAD")?
.ancestors()
.all()?
.count(),
1,
"it's a fresh shallow clone - otherwise it would have 2 commits if the previous shallow clone would still be present"
);
Ok(())
}
#[cargo_test]
fn fetch_downloads_with_git2_first_then_with_gitoxide_and_vice_versa() {
let bar = git::new("bar", |project| {

View file

@ -2466,7 +2466,11 @@ fn can_update_with_alt_reg() {
}
#[cargo_test]
fn old_git_patch() {
fn gitoxide_clones_shallow_old_git_patch() {
perform_old_git_patch(true)
}
fn perform_old_git_patch(shallow: bool) {
// Example where an old lockfile with an explicit branch="master" in Cargo.toml.
Package::new("bar", "1.0.0").publish();
let (bar, bar_repo) = git::new_repo("bar", |p| {
@ -2524,7 +2528,13 @@ dependencies = [
git::commit(&bar_repo);
// This *should* keep the old lock.
p.cargo("tree")
let mut cargo = p.cargo("tree");
if shallow {
cargo
.arg("-Zgitoxide=fetch,shallow-deps")
.masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"]);
}
cargo
// .env("CARGO_LOG", "trace")
.with_stderr(
"\
@ -2542,6 +2552,11 @@ foo v0.1.0 [..]
.run();
}
#[cargo_test]
fn old_git_patch() {
perform_old_git_patch(false)
}
// From https://github.com/rust-lang/cargo/issues/7463
#[cargo_test]
fn patch_eq_conflict_panic() {