diff --git a/Cargo.lock b/Cargo.lock index 6af4fe2515b..7e986db4455 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,11 +709,10 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.10" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if", "crossbeam-utils", ] @@ -2181,6 +2180,7 @@ dependencies = [ name = "gix-odb-tests" version = "0.0.0" dependencies = [ + "crossbeam-channel", "filetime", "gix-actor 0.31.2", "gix-date 0.8.6", diff --git a/gitoxide-core/src/repository/attributes/validate_baseline.rs b/gitoxide-core/src/repository/attributes/validate_baseline.rs index 77eeb258a49..686c668add6 100644 --- a/gitoxide-core/src/repository/attributes/validate_baseline.rs +++ b/gitoxide-core/src/repository/attributes/validate_baseline.rs @@ -305,13 +305,13 @@ pub(crate) mod function { } fn parse_exclude(line: &str) -> Option<(String, Baseline)> { - let (left, value) = line.split_at(line.find(|c| c == '\t')?); + let (left, value) = line.split_at(line.find('\t')?); let value = &value[1..]; let location = if left == "::" { None } else { - let mut tokens = left.split(|b| b == ':'); + let mut tokens = left.split(':'); let source = tokens.next()?; let line_number: usize = tokens.next()?.parse().ok()?; let pattern = tokens.next()?; @@ -363,8 +363,8 @@ pub(crate) mod function { "unspecified" => StateRef::Unspecified, _ => StateRef::from_bytes(info.as_bytes()), }; - path = path.trim_end_matches(|b| b == ':'); - let attr = attr.trim_end_matches(|b| b == ':'); + path = path.trim_end_matches(':'); + let attr = attr.trim_end_matches(':'); let assignment = gix::attrs::AssignmentRef { name: gix::attrs::NameRef::try_from(attr.as_bytes().as_bstr()).ok()?, state, diff --git a/gitoxide-core/src/repository/index/entries.rs b/gitoxide-core/src/repository/index/entries.rs index 4485bad5c3e..20a21e3a38b 100644 --- a/gitoxide-core/src/repository/index/entries.rs +++ b/gitoxide-core/src/repository/index/entries.rs @@ -319,7 +319,7 @@ pub(crate) mod function { #[cfg(feature = "serde")] fn to_json( - mut out: &mut impl std::io::Write, + out: &mut impl std::io::Write, index: &gix::index::File, entry: &gix::index::Entry, attrs: Option, @@ -338,7 +338,7 @@ pub(crate) mod function { } serde_json::to_writer( - &mut out, + &mut *out, &Entry { stat: &entry.stat, hex_id: entry.id.to_hex().to_string(), diff --git a/gix-config/src/file/section/mod.rs b/gix-config/src/file/section/mod.rs index f07a145e3d4..1dd4bd15ad3 100644 --- a/gix-config/src/file/section/mod.rs +++ b/gix-config/src/file/section/mod.rs @@ -75,7 +75,7 @@ impl<'a> Section<'a> { /// Stream ourselves to the given `out`, in order to reproduce this section mostly losslessly /// as it was parsed. pub fn write_to(&self, mut out: &mut dyn std::io::Write) -> std::io::Result<()> { - self.header.write_to(&mut out)?; + self.header.write_to(&mut *out)?; if self.body.0.is_empty() { return Ok(()); diff --git a/gix-config/src/file/write.rs b/gix-config/src/file/write.rs index 772054f9529..f08f5db57a4 100644 --- a/gix-config/src/file/write.rs +++ b/gix-config/src/file/write.rs @@ -18,7 +18,7 @@ impl File<'_> { pub fn write_to_filter( &self, mut out: &mut dyn std::io::Write, - mut filter: &mut dyn FnMut(&Section<'_>) -> bool, + filter: &mut dyn FnMut(&Section<'_>) -> bool, ) -> std::io::Result<()> { let nl = self.detect_newline_style(); @@ -27,7 +27,8 @@ impl File<'_> { event.write_to(&mut out)?; } - if !ends_with_newline(self.frontmatter_events.as_ref(), nl, true) && self.sections.values().any(&mut filter) + if !ends_with_newline(self.frontmatter_events.as_ref(), nl, true) + && self.sections.values().any(&mut *filter) { out.write_all(nl)?; } diff --git a/gix-config/src/parse/event.rs b/gix-config/src/parse/event.rs index d88ace7ce6c..653a0c67751 100644 --- a/gix-config/src/parse/event.rs +++ b/gix-config/src/parse/event.rs @@ -33,7 +33,7 @@ impl Event<'_> { /// Stream ourselves to the given `out`, in order to reproduce this event mostly losslessly /// as it was parsed. - pub fn write_to(&self, mut out: &mut dyn std::io::Write) -> std::io::Result<()> { + pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> { match self { Self::ValueNotDone(e) => { out.write_all(e.as_ref())?; @@ -42,8 +42,8 @@ impl Event<'_> { Self::Whitespace(e) | Self::Newline(e) | Self::Value(e) | Self::ValueDone(e) => out.write_all(e.as_ref()), Self::KeyValueSeparator => out.write_all(b"="), Self::SectionKey(k) => out.write_all(k.0.as_ref()), - Self::SectionHeader(h) => h.write_to(&mut out), - Self::Comment(c) => c.write_to(&mut out), + Self::SectionHeader(h) => h.write_to(out), + Self::Comment(c) => c.write_to(out), } } diff --git a/gix-config/src/parse/events.rs b/gix-config/src/parse/events.rs index 5e0c8f06bf1..feda7a7ca6c 100644 --- a/gix-config/src/parse/events.rs +++ b/gix-config/src/parse/events.rs @@ -28,26 +28,26 @@ pub type FrontMatterEvents<'a> = SmallVec<[Event<'a>; 8]>; /// /// For concrete examples, some notable differences are: /// - `git-config` sections permit subsections via either a quoted string -/// (`[some-section "subsection"]`) or via the deprecated dot notation -/// (`[some-section.subsection]`). Successful parsing these section names is not -/// well defined in typical `.ini` parsers. This parser will handle these cases -/// perfectly. +/// (`[some-section "subsection"]`) or via the deprecated dot notation +/// (`[some-section.subsection]`). Successful parsing these section names is not +/// well defined in typical `.ini` parsers. This parser will handle these cases +/// perfectly. /// - Comment markers are not strictly defined either. This parser will always -/// and only handle a semicolon or octothorpe (also known as a hash or number -/// sign). +/// and only handle a semicolon or octothorpe (also known as a hash or number +/// sign). /// - Global properties may be allowed in `.ini` parsers, but is strictly -/// disallowed by this parser. +/// disallowed by this parser. /// - Only `\t`, `\n`, `\b` `\\` are valid escape characters. /// - Quoted and semi-quoted values will be parsed (but quotes will be included -/// in event outputs). An example of a semi-quoted value is `5"hello world"`, -/// which should be interpreted as `5hello world` after -/// [normalization][crate::value::normalize()]. +/// in event outputs). An example of a semi-quoted value is `5"hello world"`, +/// which should be interpreted as `5hello world` after +/// [normalization][crate::value::normalize()]. /// - Line continuations via a `\` character is supported (inside or outside of quotes) /// - Whitespace handling similarly follows the `git-config` specification as -/// closely as possible, where excess whitespace after a non-quoted value are -/// trimmed, and line continuations onto a new line with excess spaces are kept. +/// closely as possible, where excess whitespace after a non-quoted value are +/// trimmed, and line continuations onto a new line with excess spaces are kept. /// - Only equal signs (optionally padded by spaces) are valid name/value -/// delimiters. +/// delimiters. /// /// Note that things such as case-sensitivity or duplicate sections are /// _not_ handled. This parser is a low level _syntactic_ interpreter @@ -62,8 +62,8 @@ pub type FrontMatterEvents<'a> = SmallVec<[Event<'a>; 8]>; /// # Trait Implementations /// /// - This struct does _not_ implement [`FromStr`] due to lifetime -/// constraints implied on the required `from_str` method. Instead, it provides -/// [`From<&'_ str>`]. +/// constraints implied on the required `from_str` method. Instead, it provides +/// [`From<&'_ str>`]. /// /// # Idioms /// diff --git a/gix-fs/src/symlink.rs b/gix-fs/src/symlink.rs index 8dd64406cbb..ce639c48f65 100644 --- a/gix-fs/src/symlink.rs +++ b/gix-fs/src/symlink.rs @@ -1,17 +1,17 @@ use std::{io, io::ErrorKind::AlreadyExists, path::Path}; -#[cfg(not(windows))] /// Create a new symlink at `link` which points to `original`. /// /// Note that `original` doesn't have to exist. +#[cfg(not(windows))] pub fn create(original: &Path, link: &Path) -> io::Result<()> { std::os::unix::fs::symlink(original, link) } -#[cfg(not(windows))] /// Remove a symlink. /// /// Note that on only on windows this is special. +#[cfg(not(windows))] pub fn remove(path: &Path) -> io::Result<()> { std::fs::remove_file(path) } @@ -31,12 +31,12 @@ pub fn remove(path: &Path) -> io::Result<()> { } } -#[cfg(windows)] /// Create a new symlink at `link` which points to `original`. /// /// Note that if a symlink target (the `original`) isn't present on disk, it's assumed to be a /// file, creating a dangling file symlink. This is similar to a dangling symlink on Unix, /// which doesn't have to care about the target type though. +#[cfg(windows)] pub fn create(original: &Path, link: &Path) -> io::Result<()> { use std::os::windows::fs::{symlink_dir, symlink_file}; // TODO: figure out if links to links count as files or whatever they point at @@ -53,20 +53,20 @@ pub fn create(original: &Path, link: &Path) -> io::Result<()> { } } -#[cfg(not(windows))] /// Return true if `err` indicates that a file collision happened, i.e. a symlink couldn't be created as the `link` /// already exists as filesystem object. +#[cfg(not(windows))] pub fn is_collision_error(err: &std::io::Error) -> bool { // TODO: use ::IsDirectory as well when stabilized instead of raw_os_error(), and ::FileSystemLoop respectively err.kind() == AlreadyExists - || err.raw_os_error() == Some(if cfg!(windows) { 5 } else { 21 }) + || err.raw_os_error() == Some(21) || err.raw_os_error() == Some(62) // no-follow on symlnk on mac-os || err.raw_os_error() == Some(40) // no-follow on symlnk on ubuntu } -#[cfg(windows)] /// Return true if `err` indicates that a file collision happened, i.e. a symlink couldn't be created as the `link` /// already exists as filesystem object. +#[cfg(windows)] pub fn is_collision_error(err: &std::io::Error) -> bool { err.kind() == AlreadyExists || err.kind() == std::io::ErrorKind::PermissionDenied } diff --git a/gix-ignore/src/search.rs b/gix-ignore/src/search.rs index a527b447221..9b5a2766b61 100644 --- a/gix-ignore/src/search.rs +++ b/gix-ignore/src/search.rs @@ -55,7 +55,7 @@ impl Search { .transpose()?, ); group.patterns.extend(pattern::List::::from_file( - &git_dir.join("info").join("exclude"), + git_dir.join("info").join("exclude"), None, follow_symlinks, buf, diff --git a/gix-odb/tests/Cargo.toml b/gix-odb/tests/Cargo.toml index 2c26eb6b801..d42d1775af6 100644 --- a/gix-odb/tests/Cargo.toml +++ b/gix-odb/tests/Cargo.toml @@ -24,9 +24,10 @@ gix-date = { path = "../../gix-date" } gix-object = { path = "../../gix-object" } gix-pack = { path = "../../gix-pack" } -gix-testtools = { path = "../../tests/tools"} +gix-testtools = { path = "../../tests/tools" } gix-actor = { path = "../../gix-actor" } pretty_assertions = "1.0.0" filetime = "0.2.15" maplit = "1.0.2" +crossbeam-channel = "0.5.13" diff --git a/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz b/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz index 2489b1a3bc0..03c2e9c071f 100644 Binary files a/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz and b/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz differ diff --git a/gix-odb/tests/odb/regression/mod.rs b/gix-odb/tests/odb/regression/mod.rs index 1b8f5de0b0a..c6bffa61125 100644 --- a/gix-odb/tests/odb/regression/mod.rs +++ b/gix-odb/tests/odb/regression/mod.rs @@ -17,12 +17,13 @@ mod repo_with_small_packs { } #[test] - #[cfg(feature = "internal-testing-gix-features-parallel")] + #[cfg(feature = "gix-features-parallel")] fn multi_threaded_access_will_not_panic() -> crate::Result { for arg in ["no", "without-multi-index"] { - let base = gix_testtools::scripted_fixture_read_only_with_args("make_repo_multi_index.sh", Some(arg))? - .join(".git") - .join("objects"); + let base = + gix_testtools::scripted_fixture_read_only_with_args_standalone("make_repo_multi_index.sh", Some(arg))? + .join(".git") + .join("objects"); let store = gix_odb::at(base)?; let (tx, barrier) = crossbeam_channel::unbounded::<()>(); let handles = (0..std::thread::available_parallelism()?.get()).map(|tid| { @@ -36,7 +37,7 @@ mod repo_with_small_packs { for id in store.iter()? { let id = id?; assert!( - store.try_find(id, &mut buf).is_ok(), + store.try_find(&id, &mut buf).is_ok(), "Thread {} could not find {}", tid, id diff --git a/gix-pack/src/bundle/write/mod.rs b/gix-pack/src/bundle/write/mod.rs index 9ddd1b9d3a5..88a56858b57 100644 --- a/gix-pack/src/bundle/write/mod.rs +++ b/gix-pack/src/bundle/write/mod.rs @@ -51,8 +51,8 @@ impl crate::Bundle { /// * `progress` provides detailed progress information which can be discarded with [`gix_features::progress::Discard`]. /// * `should_interrupt` is checked regularly and when true, the whole operation will stop. /// * `thin_pack_base_object_lookup` If set, we expect to see a thin-pack with objects that reference their base object by object id which is - /// expected to exist in the object database the bundle is contained within. - /// `options` further configure how the task is performed. + /// expected to exist in the object database the bundle is contained within. + /// `options` further configure how the task is performed. /// /// # Note /// @@ -300,31 +300,37 @@ impl crate::Bundle { )?; drop(pack_entries_iter); - let data_path = directory.join(format!("pack-{}.pack", outcome.data_hash.to_hex())); - let index_path = data_path.with_extension("idx"); - let keep_path = data_path.with_extension("keep"); + if outcome.num_objects == 0 { + WriteOutcome { + outcome, + data_path: None, + index_path: None, + keep_path: None, + } + } else { + let data_path = directory.join(format!("pack-{}.pack", outcome.data_hash.to_hex())); + let index_path = data_path.with_extension("idx"); + let keep_path = data_path.with_extension("keep"); - std::fs::write(&keep_path, b"")?; - Arc::try_unwrap(data_file) - .expect("only one handle left after pack was consumed") - .into_inner() - .into_inner() - .map_err(|err| Error::from(err.into_error()))? - .persist(&data_path)?; - index_file - .persist(&index_path) - .map_err(|err| { - progress.info(format!( - "pack file at {} is retained despite failing to move the index file into place. You can use plumbing to make it usable.", - data_path.display() - )); - err - })?; - WriteOutcome { - outcome, - data_path: Some(data_path), - index_path: Some(index_path), - keep_path: Some(keep_path), + std::fs::write(&keep_path, b"")?; + Arc::try_unwrap(data_file) + .expect("only one handle left after pack was consumed") + .into_inner() + .into_inner() + .map_err(|err| Error::from(err.into_error()))? + .persist(&data_path)?; + index_file + .persist(&index_path) + .map_err(|err| { + gix_features::trace::warn!("pack file at \"{}\" is retained despite failing to move the index file into place. You can use plumbing to make it usable.",data_path.display()); + err + })?; + WriteOutcome { + outcome, + data_path: Some(data_path), + index_path: Some(index_path), + keep_path: Some(keep_path), + } } } None => WriteOutcome { diff --git a/gix-pack/src/cache/delta/from_offsets.rs b/gix-pack/src/cache/delta/from_offsets.rs index fc807264d77..4b05bed4e56 100644 --- a/gix-pack/src/cache/delta/from_offsets.rs +++ b/gix-pack/src/cache/delta/from_offsets.rs @@ -31,13 +31,13 @@ const PACK_HEADER_LEN: usize = 12; impl Tree { /// Create a new `Tree` from any data sorted by offset, ascending as returned by the `data_sorted_by_offsets` iterator. /// * `get_pack_offset(item: &T) -> data::Offset` is a function returning the pack offset of the given item, which can be used - /// for obtaining the objects entry within the pack. + /// for obtaining the objects entry within the pack. /// * `pack_path` is the path to the pack file itself and from which to read the entry data, which is a pack file matching the offsets - /// returned by `get_pack_offset(…)`. + /// returned by `get_pack_offset(…)`. /// * `progress` is used to track progress when creating the tree. /// * `resolve_in_pack_id(gix_hash::oid) -> Option` takes an object ID and tries to resolve it to an object within this pack if - /// possible. Failing to do so aborts the operation, and this function is not expected to be called in usual packs. It's a theoretical - /// possibility though as old packs might have referred to their objects using the 20 bytes hash, instead of their encoded offset from the base. + /// possible. Failing to do so aborts the operation, and this function is not expected to be called in usual packs. It's a theoretical + /// possibility though as old packs might have referred to their objects using the 20 bytes hash, instead of their encoded offset from the base. /// /// Note that the sort order is ascending. The given pack file path must match the provided offsets. pub fn from_offsets_in_pack( diff --git a/gix-pack/src/data/output/entry/iter_from_counts.rs b/gix-pack/src/data/output/entry/iter_from_counts.rs index c65f29ced49..5760bbb22fe 100644 --- a/gix-pack/src/data/output/entry/iter_from_counts.rs +++ b/gix-pack/src/data/output/entry/iter_from_counts.rs @@ -40,7 +40,7 @@ pub(crate) mod function { /// /// * ~~currently there is no way to easily write the pack index, even though the state here is uniquely positioned to do /// so with minimal overhead (especially compared to `gix index-from-pack`)~~ Probably works now by chaining Iterators - /// or keeping enough state to write a pack and then generate an index with recorded data. + /// or keeping enough state to write a pack and then generate an index with recorded data. /// pub fn iter_from_counts( mut counts: Vec, diff --git a/gix-pack/src/index/write/mod.rs b/gix-pack/src/index/write/mod.rs index 2247d8a1ebe..ca3d8348088 100644 --- a/gix-pack/src/index/write/mod.rs +++ b/gix-pack/src/index/write/mod.rs @@ -78,9 +78,9 @@ impl crate::index::File { /// /// * neither in-pack nor out-of-pack Ref Deltas are supported here, these must have been resolved beforehand. /// * `make_resolver()` will only be called after the iterator stopped returning elements and produces a function that - /// provides all bytes belonging to a pack entry writing them to the given mutable output `Vec`. - /// It should return `None` if the entry cannot be resolved from the pack that produced the `entries` iterator, causing - /// the write operation to fail. + /// provides all bytes belonging to a pack entry writing them to the given mutable output `Vec`. + /// It should return `None` if the entry cannot be resolved from the pack that produced the `entries` iterator, causing + /// the write operation to fail. #[allow(clippy::too_many_arguments)] pub fn write_data_iter_to_stream( version: crate::index::Version, diff --git a/gix-pack/tests/pack/data/output/count_and_entries.rs b/gix-pack/tests/pack/data/output/count_and_entries.rs index c20bdf52727..edb4783224a 100644 --- a/gix-pack/tests/pack/data/output/count_and_entries.rs +++ b/gix-pack/tests/pack/data/output/count_and_entries.rs @@ -322,13 +322,18 @@ fn traversals() -> crate::Result { #[test] fn empty_pack_is_allowed() { - write_and_verify( - db(DbKind::DeterministicGeneratedContent).unwrap(), - vec![], - hex_to_id("029d08823bd8a8eab510ad6ac75c823cfd3ed31e"), - None, - ) - .unwrap(); + assert_eq!( + write_and_verify( + db(DbKind::DeterministicGeneratedContent).unwrap(), + vec![], + hex_to_id("029d08823bd8a8eab510ad6ac75c823cfd3ed31e"), + None, + ) + .unwrap_err() + .to_string(), + "pack data directory should be set", + "empty packs are not actually written as they would be useless" + ); } fn write_and_verify( @@ -392,7 +397,7 @@ fn write_and_verify( pack::bundle::write::Options::default(), )? .data_path - .expect("directory set"), + .ok_or("pack data directory should be set")?, object_hash, )?; // TODO: figure out why these hashes change, also depending on the machine, even though they are indeed stable. diff --git a/gix-pack/tests/pack/index.rs b/gix-pack/tests/pack/index.rs index e554f50611d..67f991d0f8d 100644 --- a/gix-pack/tests/pack/index.rs +++ b/gix-pack/tests/pack/index.rs @@ -108,7 +108,7 @@ mod version { } } - #[cfg(feature = "internal-testing-gix-features-parallel")] + #[cfg(feature = "gix-features-parallel")] mod any { use std::{fs, io, sync::atomic::AtomicBool}; @@ -133,7 +133,7 @@ mod version { index_path: &&str, data_path: &&str, ) -> Result<(), Box> { - let pack_iter = pack::data::input::BytesToEntriesIter::new_from_header( + let mut pack_iter = pack::data::input::BytesToEntriesIter::new_from_header( io::BufReader::new(fs::File::open(fixture_path(data_path))?), *mode, *compressed, @@ -148,12 +148,12 @@ mod version { desired_kind, || { let file = std::fs::File::open(fixture_path(data_path))?; - let map = unsafe { memmap2::MmapOptions::map_copy_read_only(&file)? }; + let map = unsafe { memmap2::MmapOptions::new().map_copy_read_only(&file)? }; Ok((slice_map, map)) }, - pack_iter, + &mut pack_iter, None, - progress::Discard, + &mut progress::Discard, &mut actual, &AtomicBool::new(false), gix_hash::Kind::Sha1, @@ -210,7 +210,7 @@ mod version { assert_eq!(outcome.index_version, desired_kind); assert_eq!( outcome.index_hash, - gix_hash::ObjectId::from(&expected[end_of_pack_hash..end_of_index_hash]) + gix_hash::ObjectId::try_from(&expected[end_of_pack_hash..end_of_index_hash])? ); Ok(()) } @@ -227,7 +227,7 @@ mod version { #[test] fn lookup_missing() { let file = index::File::at(&fixture_path(INDEX_V2), gix_hash::Kind::Sha1).unwrap(); - let prefix = gix_hash::Prefix::new(gix_hash::ObjectId::null(gix_hash::Kind::Sha1), 7).unwrap(); + let prefix = gix_hash::Prefix::new(&gix_hash::Kind::Sha1.null(), 7).unwrap(); assert!(file.lookup_prefix(prefix, None).is_none()); let mut candidates = 1..1; diff --git a/gix-ref/fuzz/fuzz_targets/fuzz_log.rs b/gix-ref/fuzz/fuzz_targets/fuzz_log.rs index 423da5479de..204ab6e7ba2 100644 --- a/gix-ref/fuzz/fuzz_targets/fuzz_log.rs +++ b/gix-ref/fuzz/fuzz_targets/fuzz_log.rs @@ -20,10 +20,10 @@ fn fuzz(ctx: Ctx) -> Result<()> { let mut buf = [0u8; 1024]; let read = std::io::Cursor::new(ctx.multi_line_reverse); - let mut iter = gix_ref::file::log::iter::reverse(read, &mut buf)?; + let iter = gix_ref::file::log::iter::reverse(read, &mut buf)?; _ = black_box(iter.map(|x| black_box(x)).count()); - let mut iter = gix_ref::file::log::iter::forward(ctx.multi_line_forward); + let iter = gix_ref::file::log::iter::forward(ctx.multi_line_forward); _ = black_box(iter.map(|x| black_box(x)).count()); Ok(()) diff --git a/gix-ref/fuzz/fuzz_targets/fuzz_names.rs b/gix-ref/fuzz/fuzz_targets/fuzz_names.rs index 651a28fb23e..6729f2d5899 100644 --- a/gix-ref/fuzz/fuzz_targets/fuzz_names.rs +++ b/gix-ref/fuzz/fuzz_targets/fuzz_names.rs @@ -3,7 +3,7 @@ use anyhow::Result; use arbitrary::Arbitrary; use bstr::{BStr, BString}; -use gix_ref::{namespace::expand, FullName, FullNameRef, PartialName, PartialNameCow, PartialNameRef}; +use gix_ref::{namespace::expand, FullName, FullNameRef, PartialName}; use libfuzzer_sys::fuzz_target; use std::hint::black_box; diff --git a/gix-ref/src/lib.rs b/gix-ref/src/lib.rs index 29028b38d62..6059fd2d675 100644 --- a/gix-ref/src/lib.rs +++ b/gix-ref/src/lib.rs @@ -23,8 +23,6 @@ #![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))] #![deny(missing_docs, rust_2018_idioms, unsafe_code)] -use std::borrow::Cow; - use gix_hash::{oid, ObjectId}; pub use gix_object::bstr; use gix_object::bstr::{BStr, BString}; @@ -133,10 +131,6 @@ pub struct FullName(pub(crate) BString); #[repr(transparent)] pub struct FullNameRef(BStr); -/// A validated and potentially partial reference name, safe to use for common operations. -#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] -pub struct PartialNameCow<'a>(Cow<'a, BStr>); - /// A validated and potentially partial reference name, safe to use for common operations. #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd)] #[repr(transparent)] diff --git a/gix-ref/tests/store/mod.rs b/gix-ref/tests/store/mod.rs index d6263358735..9f6ae14b7d9 100644 --- a/gix-ref/tests/store/mod.rs +++ b/gix-ref/tests/store/mod.rs @@ -1,12 +1,15 @@ #[test] -#[cfg(feature = "internal-testing-gix-features-parallel")] +#[cfg(feature = "gix-features-parallel")] fn is_send_and_sync() { pub fn store_at(name: &str) -> crate::Result { let path = gix_testtools::scripted_fixture_read_only_standalone(name)?; Ok(gix_ref::file::Store::at( path.join(".git"), - gix_ref::store::WriteReflog::Normal, - gix_hash::Kind::Sha1, + gix_ref::store::init::Options { + write_reflog: gix_ref::store::WriteReflog::Normal, + object_hash: gix_hash::Kind::Sha1, + ..Default::default() + }, )) } diff --git a/gix-status/src/index_as_worktree_with_renames/types.rs b/gix-status/src/index_as_worktree_with_renames/types.rs index 76e9287e6d2..84ba2e24ec5 100644 --- a/gix-status/src/index_as_worktree_with_renames/types.rs +++ b/gix-status/src/index_as_worktree_with_renames/types.rs @@ -325,10 +325,10 @@ pub struct Context<'a> { /// /// * `attr_stack` /// - A stack pre-configured to allow accessing attributes for each entry, as required for `filter` - /// and possibly pathspecs. - /// It *may* also allow accessing `.gitignore` information for use in the directory walk. - /// If no excludes information is present, the directory walk will identify ignored files as untracked, which - /// might be desirable under certain circumstances. + /// and possibly pathspecs. + /// It *may* also allow accessing `.gitignore` information for use in the directory walk. + /// If no excludes information is present, the directory walk will identify ignored files as untracked, which + /// might be desirable under certain circumstances. /// * `filter` /// - A filter to be able to perform conversions from and to the worktree format. /// It is needed to potentially refresh the index with data read from the worktree, which needs to be converted back diff --git a/gix/src/remote/connection/fetch/error.rs b/gix/src/remote/connection/fetch/error.rs index 29326b3a763..129a2e6d345 100644 --- a/gix/src/remote/connection/fetch/error.rs +++ b/gix/src/remote/connection/fetch/error.rs @@ -47,6 +47,11 @@ pub enum Error { NegotiationAlgorithmConfig(#[from] config::key::GenericErrorWithValue), #[error("Failed to read remaining bytes in stream")] ReadRemainingBytes(#[source] std::io::Error), + #[error("None of the refspec(s) {} matched any of the {num_remote_refs} refs on the remote", refspecs.iter().map(|r| r.to_ref().instruction().to_bstring().to_string()).collect::>().join(", "))] + NoMapping { + refspecs: Vec, + num_remote_refs: usize, + }, } impl gix_protocol::transport::IsSpuriousError for Error { diff --git a/gix/src/remote/connection/fetch/negotiate.rs b/gix/src/remote/connection/fetch/negotiate.rs index f5b6a031c4c..42b578ee490 100644 --- a/gix/src/remote/connection/fetch/negotiate.rs +++ b/gix/src/remote/connection/fetch/negotiate.rs @@ -61,7 +61,7 @@ pub(crate) enum Action { /// Finally, we also mark tips in the `negotiator` in one go to avoid traversing all refs twice, since we naturally encounter all tips during /// our own walk. /// -/// Return whether or not we should negotiate, along with a queue for later use. +/// Return whether we should negotiate, along with a queue for later use. pub(crate) fn mark_complete_and_common_ref( repo: &crate::Repository, negotiator: &mut dyn gix_negotiate::Negotiator, @@ -71,6 +71,9 @@ pub(crate) fn mark_complete_and_common_ref( mapping_is_ignored: impl Fn(&fetch::Mapping) -> bool, ) -> Result { let _span = gix_trace::detail!("mark_complete_and_common_ref", mappings = ref_map.mappings.len()); + if ref_map.mappings.is_empty() { + return Ok(Action::NoChange); + } if let fetch::Shallow::Deepen(0) = shallow { // Avoid deepening (relative) with zero as it seems to upset the server. Git also doesn't actually // perform the negotiation for some reason (couldn't find it in code). diff --git a/gix/src/remote/connection/fetch/receive_pack.rs b/gix/src/remote/connection/fetch/receive_pack.rs index e0231f51a0a..ac48473e1bc 100644 --- a/gix/src/remote/connection/fetch/receive_pack.rs +++ b/gix/src/remote/connection/fetch/receive_pack.rs @@ -91,6 +91,15 @@ where let _span = gix_trace::coarse!("fetch::Prepare::receive()"); let mut con = self.con.take().expect("receive() can only be called once"); + if self.ref_map.mappings.is_empty() && !self.ref_map.remote_refs.is_empty() { + let mut specs = con.remote.fetch_specs.clone(); + specs.extend(self.ref_map.extra_refspecs.clone()); + return Err(Error::NoMapping { + refspecs: specs, + num_remote_refs: self.ref_map.remote_refs.len(), + }); + } + let handshake = &self.ref_map.handshake; let protocol_version = handshake.server_protocol_version; diff --git a/gix/src/remote/connection/ref_map.rs b/gix/src/remote/connection/ref_map.rs index bf53cf35fb0..c33471d5416 100644 --- a/gix/src/remote/connection/ref_map.rs +++ b/gix/src/remote/connection/ref_map.rs @@ -145,6 +145,7 @@ where } })) .validated()?; + let mappings = res.mappings; let mappings = mappings .into_iter() diff --git a/src/porcelain/main.rs b/src/porcelain/main.rs index 9024c193775..95645d843d5 100644 --- a/src/porcelain/main.rs +++ b/src/porcelain/main.rs @@ -62,11 +62,11 @@ pub fn main() -> Result<()> { progress, progress_keep_open, crate::shared::STANDARD_RANGE, - move |mut progress, out, mut err| { + move |mut progress, out, err| { let engine = query::prepare( &repo_dir, &mut progress, - &mut err, + &mut *err, query::Options { object_cache_size_mb, find_copies_harder,