diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-pack/src/data/output/count | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-pack/src/data/output/count')
-rw-r--r-- | vendor/gix-pack/src/data/output/count/mod.rs | 49 | ||||
-rw-r--r-- | vendor/gix-pack/src/data/output/count/objects/mod.rs | 405 | ||||
-rw-r--r-- | vendor/gix-pack/src/data/output/count/objects/reduce.rs | 49 | ||||
-rw-r--r-- | vendor/gix-pack/src/data/output/count/objects/tree.rs | 124 | ||||
-rw-r--r-- | vendor/gix-pack/src/data/output/count/objects/types.rs | 105 | ||||
-rw-r--r-- | vendor/gix-pack/src/data/output/count/objects/util.rs | 24 |
6 files changed, 756 insertions, 0 deletions
diff --git a/vendor/gix-pack/src/data/output/count/mod.rs b/vendor/gix-pack/src/data/output/count/mod.rs new file mode 100644 index 000000000..e7ee767de --- /dev/null +++ b/vendor/gix-pack/src/data/output/count/mod.rs @@ -0,0 +1,49 @@ +use gix_hash::ObjectId; + +use crate::data::output::Count; + +/// Specifies how the pack location was handled during counting +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub enum PackLocation { + /// We did not lookup this object + NotLookedUp, + /// The object was looked up and there may be a location in a pack, along with entry information + LookedUp(Option<crate::data::entry::Location>), +} + +impl PackLocation { + /// Directly go through to LookedUp variant, panic otherwise + pub fn is_none(&self) -> bool { + match self { + PackLocation::LookedUp(opt) => opt.is_none(), + PackLocation::NotLookedUp => unreachable!("must have been resolved"), + } + } + /// Directly go through to LookedUp variant, panic otherwise + pub fn as_ref(&self) -> Option<&crate::data::entry::Location> { + match self { + PackLocation::LookedUp(opt) => opt.as_ref(), + PackLocation::NotLookedUp => unreachable!("must have been resolved"), + } + } +} + +impl Count { + /// Create a new instance from the given `oid` and its corresponding git `obj`ect data. + pub fn from_data(oid: impl Into<ObjectId>, location: Option<crate::data::entry::Location>) -> Self { + Count { + id: oid.into(), + entry_pack_location: PackLocation::LookedUp(location), + } + } +} + +#[path = "objects/mod.rs"] +mod objects_impl; +pub use objects_impl::{objects, objects_unthreaded}; + +/// +pub mod objects { + pub use super::objects_impl::{Error, ObjectExpansion, Options, Outcome, Result}; +} diff --git a/vendor/gix-pack/src/data/output/count/objects/mod.rs b/vendor/gix-pack/src/data/output/count/objects/mod.rs new file mode 100644 index 000000000..d56bc9a5f --- /dev/null +++ b/vendor/gix-pack/src/data/output/count/objects/mod.rs @@ -0,0 +1,405 @@ +use std::{ + cell::RefCell, + sync::{atomic::AtomicBool, Arc}, +}; + +use gix_features::{parallel, progress::Progress}; +use gix_hash::ObjectId; + +use crate::{data::output, find}; + +pub(in crate::data::output::count::objects_impl) mod reduce; +mod util; + +mod types; +pub use types::{Error, ObjectExpansion, Options, Outcome}; + +mod tree; + +/// The return type used by [`objects()`]. +pub type Result<E1, E2> = std::result::Result<(Vec<output::Count>, Outcome), Error<E1, E2>>; + +/// Generate [`Count`][output::Count]s from input `objects` with object expansion based on [`options`][Options] +/// to learn which objects would would constitute a pack. This step is required to know exactly how many objects would +/// be in a pack while keeping data around to avoid minimize object database access. +/// +/// A [`Count`][output::Count] object maintains enough state to greatly accelerate future access of packed objects. +/// +/// * `db` - the object store to use for accessing objects. +/// * `objects_ids` +/// * A list of objects ids to add to the pack. Duplication checks are performed so no object is ever added to a pack twice. +/// * Objects may be expanded based on the provided [`options`][Options] +/// * `progress` +/// * a way to obtain progress information +/// * `should_interrupt` +/// * A flag that is set to true if the operation should stop +/// * `options` +/// * more configuration +pub fn objects<Find, Iter, IterErr, Oid>( + db: Find, + objects_ids: Iter, + progress: impl Progress, + should_interrupt: &AtomicBool, + Options { + thread_limit, + input_object_expansion, + chunk_size, + }: Options, +) -> Result<find::existing::Error<Find::Error>, IterErr> +where + Find: crate::Find + Send + Clone, + <Find as crate::Find>::Error: Send, + Iter: Iterator<Item = std::result::Result<Oid, IterErr>> + Send, + Oid: Into<ObjectId> + Send, + IterErr: std::error::Error + Send, +{ + let lower_bound = objects_ids.size_hint().0; + let (chunk_size, thread_limit, _) = parallel::optimize_chunk_size_and_thread_limit( + chunk_size, + if lower_bound == 0 { None } else { Some(lower_bound) }, + thread_limit, + None, + ); + let chunks = gix_features::iter::Chunks { + inner: objects_ids, + size: chunk_size, + }; + let seen_objs = gix_hashtable::sync::ObjectIdMap::default(); + let progress = Arc::new(parking_lot::Mutex::new(progress)); + + parallel::in_parallel( + chunks, + thread_limit, + { + let progress = Arc::clone(&progress); + move |n| { + ( + Vec::new(), // object data buffer + Vec::new(), // object data buffer 2 to hold two objects at a time + { + let mut p = progress + .lock() + .add_child_with_id(format!("thread {n}"), gix_features::progress::UNKNOWN); + p.init(None, gix_features::progress::count("objects")); + p + }, + ) + } + }, + { + let seen_objs = &seen_objs; + move |oids: Vec<std::result::Result<Oid, IterErr>>, (buf1, buf2, progress)| { + expand::this( + &db, + input_object_expansion, + seen_objs, + oids, + buf1, + buf2, + progress, + should_interrupt, + true, /*allow pack lookups*/ + ) + } + }, + reduce::Statistics::new(progress), + ) +} + +/// Like [`objects()`] but using a single thread only to mostly save on the otherwise required overhead. +pub fn objects_unthreaded<Find, IterErr, Oid>( + db: Find, + object_ids: impl Iterator<Item = std::result::Result<Oid, IterErr>>, + mut progress: impl Progress, + should_interrupt: &AtomicBool, + input_object_expansion: ObjectExpansion, +) -> Result<find::existing::Error<Find::Error>, IterErr> +where + Find: crate::Find, + Oid: Into<ObjectId>, + IterErr: std::error::Error, +{ + let seen_objs = RefCell::new(gix_hashtable::HashSet::default()); + + let (mut buf1, mut buf2) = (Vec::new(), Vec::new()); + expand::this( + &db, + input_object_expansion, + &seen_objs, + object_ids, + &mut buf1, + &mut buf2, + &mut progress, + should_interrupt, + false, /*allow pack lookups*/ + ) +} + +mod expand { + use std::sync::atomic::{AtomicBool, Ordering}; + + use gix_features::progress::Progress; + use gix_hash::{oid, ObjectId}; + use gix_object::{CommitRefIter, TagRefIter}; + + use super::{ + tree, + types::{Error, ObjectExpansion, Outcome}, + util, + }; + use crate::{ + data::{output, output::count::PackLocation}, + find, FindExt, + }; + + #[allow(clippy::too_many_arguments)] + pub fn this<Find, IterErr, Oid>( + db: &Find, + input_object_expansion: ObjectExpansion, + seen_objs: &impl util::InsertImmutable, + oids: impl IntoIterator<Item = std::result::Result<Oid, IterErr>>, + buf1: &mut Vec<u8>, + #[allow(clippy::ptr_arg)] buf2: &mut Vec<u8>, + progress: &mut impl Progress, + should_interrupt: &AtomicBool, + allow_pack_lookups: bool, + ) -> super::Result<find::existing::Error<Find::Error>, IterErr> + where + Find: crate::Find, + Oid: Into<ObjectId>, + IterErr: std::error::Error, + { + use ObjectExpansion::*; + + let mut out = Vec::new(); + let mut tree_traversal_state = gix_traverse::tree::breadthfirst::State::default(); + let mut tree_diff_state = gix_diff::tree::State::default(); + let mut parent_commit_ids = Vec::new(); + let mut traverse_delegate = tree::traverse::AllUnseen::new(seen_objs); + let mut changes_delegate = tree::changes::AllNew::new(seen_objs); + let mut outcome = Outcome::default(); + + let stats = &mut outcome; + for id in oids.into_iter() { + if should_interrupt.load(Ordering::Relaxed) { + return Err(Error::Interrupted); + } + + let id = id.map(|oid| oid.into()).map_err(Error::InputIteration)?; + let (obj, location) = db.find(id, buf1)?; + stats.input_objects += 1; + match input_object_expansion { + TreeAdditionsComparedToAncestor => { + use gix_object::Kind::*; + let mut obj = obj; + let mut location = location; + let mut id = id.to_owned(); + + loop { + push_obj_count_unique(&mut out, seen_objs, &id, location, progress, stats, false); + match obj.kind { + Tree | Blob => break, + Tag => { + id = TagRefIter::from_bytes(obj.data) + .target_id() + .expect("every tag has a target"); + let tmp = db.find(id, buf1)?; + + obj = tmp.0; + location = tmp.1; + + stats.expanded_objects += 1; + continue; + } + Commit => { + let current_tree_iter = { + let mut commit_iter = CommitRefIter::from_bytes(obj.data); + let tree_id = commit_iter.tree_id().expect("every commit has a tree"); + parent_commit_ids.clear(); + for token in commit_iter { + match token { + Ok(gix_object::commit::ref_iter::Token::Parent { id }) => { + parent_commit_ids.push(id) + } + Ok(_) => break, + Err(err) => return Err(Error::CommitDecode(err)), + } + } + let (obj, location) = db.find(tree_id, buf1)?; + push_obj_count_unique( + &mut out, seen_objs, &tree_id, location, progress, stats, true, + ); + gix_object::TreeRefIter::from_bytes(obj.data) + }; + + let objects = if parent_commit_ids.is_empty() { + traverse_delegate.clear(); + gix_traverse::tree::breadthfirst( + current_tree_iter, + &mut tree_traversal_state, + |oid, buf| { + stats.decoded_objects += 1; + match db.find(oid, buf).ok() { + Some((obj, location)) => { + progress.inc(); + stats.expanded_objects += 1; + out.push(output::Count::from_data(oid, location)); + obj.try_into_tree_iter() + } + None => None, + } + }, + &mut traverse_delegate, + ) + .map_err(Error::TreeTraverse)?; + &traverse_delegate.non_trees + } else { + for commit_id in &parent_commit_ids { + let parent_tree_id = { + let (parent_commit_obj, location) = db.find(commit_id, buf2)?; + + push_obj_count_unique( + &mut out, seen_objs, commit_id, location, progress, stats, true, + ); + CommitRefIter::from_bytes(parent_commit_obj.data) + .tree_id() + .expect("every commit has a tree") + }; + let parent_tree = { + let (parent_tree_obj, location) = db.find(parent_tree_id, buf2)?; + push_obj_count_unique( + &mut out, + seen_objs, + &parent_tree_id, + location, + progress, + stats, + true, + ); + gix_object::TreeRefIter::from_bytes(parent_tree_obj.data) + }; + + changes_delegate.clear(); + gix_diff::tree::Changes::from(Some(parent_tree)) + .needed_to_obtain( + current_tree_iter.clone(), + &mut tree_diff_state, + |oid, buf| { + stats.decoded_objects += 1; + db.find_tree_iter(oid, buf).map(|t| t.0) + }, + &mut changes_delegate, + ) + .map_err(Error::TreeChanges)?; + } + &changes_delegate.objects + }; + for id in objects.iter() { + out.push(id_to_count(db, buf2, id, progress, stats, allow_pack_lookups)); + } + break; + } + } + } + } + TreeContents => { + use gix_object::Kind::*; + let mut id = id; + let mut obj = (obj, location); + loop { + push_obj_count_unique(&mut out, seen_objs, &id, obj.1.clone(), progress, stats, false); + match obj.0.kind { + Tree => { + traverse_delegate.clear(); + gix_traverse::tree::breadthfirst( + gix_object::TreeRefIter::from_bytes(obj.0.data), + &mut tree_traversal_state, + |oid, buf| { + stats.decoded_objects += 1; + match db.find(oid, buf).ok() { + Some((obj, location)) => { + progress.inc(); + stats.expanded_objects += 1; + out.push(output::Count::from_data(oid, location)); + obj.try_into_tree_iter() + } + None => None, + } + }, + &mut traverse_delegate, + ) + .map_err(Error::TreeTraverse)?; + for id in traverse_delegate.non_trees.iter() { + out.push(id_to_count(db, buf1, id, progress, stats, allow_pack_lookups)); + } + break; + } + Commit => { + id = CommitRefIter::from_bytes(obj.0.data) + .tree_id() + .expect("every commit has a tree"); + stats.expanded_objects += 1; + obj = db.find(id, buf1)?; + continue; + } + Blob => break, + Tag => { + id = TagRefIter::from_bytes(obj.0.data) + .target_id() + .expect("every tag has a target"); + stats.expanded_objects += 1; + obj = db.find(id, buf1)?; + continue; + } + } + } + } + AsIs => push_obj_count_unique(&mut out, seen_objs, &id, location, progress, stats, false), + } + } + outcome.total_objects = out.len(); + Ok((out, outcome)) + } + + #[inline] + fn push_obj_count_unique( + out: &mut Vec<output::Count>, + all_seen: &impl util::InsertImmutable, + id: &oid, + location: Option<crate::data::entry::Location>, + progress: &mut impl Progress, + statistics: &mut Outcome, + count_expanded: bool, + ) { + let inserted = all_seen.insert(id.to_owned()); + if inserted { + progress.inc(); + statistics.decoded_objects += 1; + if count_expanded { + statistics.expanded_objects += 1; + } + out.push(output::Count::from_data(id, location)); + } + } + + #[inline] + fn id_to_count<Find: crate::Find>( + db: &Find, + buf: &mut Vec<u8>, + id: &oid, + progress: &mut impl Progress, + statistics: &mut Outcome, + allow_pack_lookups: bool, + ) -> output::Count { + progress.inc(); + statistics.expanded_objects += 1; + output::Count { + id: id.to_owned(), + entry_pack_location: if allow_pack_lookups { + PackLocation::LookedUp(db.location_by_oid(id, buf)) + } else { + PackLocation::NotLookedUp + }, + } + } +} diff --git a/vendor/gix-pack/src/data/output/count/objects/reduce.rs b/vendor/gix-pack/src/data/output/count/objects/reduce.rs new file mode 100644 index 000000000..c6a61d467 --- /dev/null +++ b/vendor/gix-pack/src/data/output/count/objects/reduce.rs @@ -0,0 +1,49 @@ +use std::{marker::PhantomData, sync::Arc}; + +use gix_features::{parallel, progress::Progress}; + +use super::Outcome; +use crate::data::output; + +pub struct Statistics<E, P> { + total: Outcome, + counts: Vec<output::Count>, + progress: Arc<parking_lot::Mutex<P>>, + _err: PhantomData<E>, +} + +impl<E, P> Statistics<E, P> +where + P: Progress, +{ + pub fn new(progress: Arc<parking_lot::Mutex<P>>) -> Self { + Statistics { + total: Default::default(), + counts: Default::default(), + progress, + _err: PhantomData::default(), + } + } +} + +impl<E, P> parallel::Reduce for Statistics<E, P> +where + P: Progress, +{ + type Input = Result<(Vec<output::Count>, Outcome), E>; + type FeedProduce = (); + type Output = (Vec<output::Count>, Outcome); + type Error = E; + + fn feed(&mut self, item: Self::Input) -> Result<Self::FeedProduce, Self::Error> { + let (counts, stats) = item?; + self.total.aggregate(stats); + self.progress.lock().inc_by(counts.len()); + self.counts.extend(counts); + Ok(()) + } + + fn finalize(self) -> Result<Self::Output, Self::Error> { + Ok((self.counts, self.total)) + } +} diff --git a/vendor/gix-pack/src/data/output/count/objects/tree.rs b/vendor/gix-pack/src/data/output/count/objects/tree.rs new file mode 100644 index 000000000..d3f4f6b9a --- /dev/null +++ b/vendor/gix-pack/src/data/output/count/objects/tree.rs @@ -0,0 +1,124 @@ +pub mod changes { + use gix_diff::tree::{ + visit::{Action, Change}, + Visit, + }; + use gix_hash::ObjectId; + use gix_object::{bstr::BStr, tree::EntryMode}; + + use crate::data::output::count::objects_impl::util::InsertImmutable; + + pub struct AllNew<'a, H> { + pub objects: Vec<ObjectId>, + all_seen: &'a H, + } + + impl<'a, H> AllNew<'a, H> + where + H: InsertImmutable, + { + pub fn new(all_seen: &'a H) -> Self { + AllNew { + objects: Default::default(), + all_seen, + } + } + pub fn clear(&mut self) { + self.objects.clear(); + } + } + + impl<'a, H> Visit for AllNew<'a, H> + where + H: InsertImmutable, + { + fn pop_front_tracked_path_and_set_current(&mut self) {} + + fn push_back_tracked_path_component(&mut self, _component: &BStr) {} + + fn push_path_component(&mut self, _component: &BStr) {} + + fn pop_path_component(&mut self) {} + + fn visit(&mut self, change: Change) -> Action { + match change { + Change::Addition { oid, entry_mode } | Change::Modification { oid, entry_mode, .. } => { + if entry_mode == EntryMode::Commit { + return Action::Continue; + } + let inserted = self.all_seen.insert(oid); + if inserted { + self.objects.push(oid); + } + } + Change::Deletion { .. } => {} + }; + Action::Continue + } + } +} + +pub mod traverse { + use gix_hash::ObjectId; + use gix_object::{ + bstr::BStr, + tree::{EntryMode, EntryRef}, + }; + use gix_traverse::tree::{visit::Action, Visit}; + + use crate::data::output::count::objects_impl::util::InsertImmutable; + + pub struct AllUnseen<'a, H> { + pub non_trees: Vec<ObjectId>, + all_seen: &'a H, + } + + impl<'a, H> AllUnseen<'a, H> + where + H: InsertImmutable, + { + pub fn new(all_seen: &'a H) -> Self { + AllUnseen { + non_trees: Default::default(), + all_seen, + } + } + pub fn clear(&mut self) { + self.non_trees.clear(); + } + } + + impl<'a, H> Visit for AllUnseen<'a, H> + where + H: InsertImmutable, + { + fn pop_front_tracked_path_and_set_current(&mut self) {} + + fn push_back_tracked_path_component(&mut self, _component: &BStr) {} + + fn push_path_component(&mut self, _component: &BStr) {} + + fn pop_path_component(&mut self) {} + + fn visit_tree(&mut self, entry: &EntryRef<'_>) -> Action { + let inserted = self.all_seen.insert(entry.oid.to_owned()); + if inserted { + Action::Continue + } else { + Action::Skip + } + } + + fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> Action { + if entry.mode == EntryMode::Commit { + // links don't have a representation + return Action::Continue; + } + let inserted = self.all_seen.insert(entry.oid.to_owned()); + if inserted { + self.non_trees.push(entry.oid.to_owned()); + } + Action::Continue + } + } +} diff --git a/vendor/gix-pack/src/data/output/count/objects/types.rs b/vendor/gix-pack/src/data/output/count/objects/types.rs new file mode 100644 index 000000000..8c8c939df --- /dev/null +++ b/vendor/gix-pack/src/data/output/count/objects/types.rs @@ -0,0 +1,105 @@ +/// Information gathered during the run of [`iter_from_objects()`][super::objects()]. +#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub struct Outcome { + /// The amount of objects provided to start the iteration. + pub input_objects: usize, + /// The amount of objects that have been expanded from the input source. + /// It's desirable to do that as expansion happens on multiple threads, allowing the amount of input objects to be small. + /// `expanded_objects - decoded_objects` is the 'cheap' object we found without decoding the object itself. + pub expanded_objects: usize, + /// The amount of fully decoded objects. These are the most expensive as they are fully decoded + pub decoded_objects: usize, + /// The total amount of encountered objects. Should be `expanded_objects + input_objects`. + pub total_objects: usize, +} + +impl Outcome { + pub(in crate::data::output::count) fn aggregate( + &mut self, + Outcome { + input_objects, + decoded_objects, + expanded_objects, + total_objects, + }: Self, + ) { + self.input_objects += input_objects; + self.decoded_objects += decoded_objects; + self.expanded_objects += expanded_objects; + self.total_objects += total_objects; + } +} + +/// The way input objects are handled +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub enum ObjectExpansion { + /// Don't do anything with the input objects except for transforming them into pack entries + AsIs, + /// If the input object is a Commit then turn it into a pack entry. Additionally obtain its tree, turn it into a pack entry + /// along with all of its contents, that is nested trees, and any other objects reachable from it. + /// Otherwise, the same as [`AsIs`][ObjectExpansion::AsIs]. + /// + /// This mode is useful if all reachable objects should be added, as in cloning a repository. + TreeContents, + /// If the input is a commit, obtain its ancestors and turn them into pack entries. Obtain the ancestor trees along with the commits + /// tree and turn them into pack entries. Finally obtain the added/changed objects when comparing the ancestor trees with the + /// current tree and turn them into entries as well. + /// Otherwise, the same as [`AsIs`][ObjectExpansion::AsIs]. + /// + /// This mode is useful to build a pack containing only new objects compared to a previous state. + TreeAdditionsComparedToAncestor, +} + +impl Default for ObjectExpansion { + fn default() -> Self { + ObjectExpansion::AsIs + } +} + +/// Configuration options for the pack generation functions provided in [this module][crate::data::output]. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub struct Options { + /// The amount of threads to use at most when resolving the pack. If `None`, all logical cores are used. + /// If more than one thread is used, the order of returned [counts][crate::data::output::Count] is not deterministic anymore + /// especially when tree traversal is involved. Thus deterministic ordering requires `Some(1)` to be set. + pub thread_limit: Option<usize>, + /// The amount of objects per chunk or unit of work to be sent to threads for processing + pub chunk_size: usize, + /// The way input objects are handled + pub input_object_expansion: ObjectExpansion, +} + +impl Default for Options { + fn default() -> Self { + Options { + thread_limit: None, + chunk_size: 10, + input_object_expansion: Default::default(), + } + } +} + +/// The error returned by the pack generation iterator [bytes::FromEntriesIter][crate::data::output::bytes::FromEntriesIter]. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error<FindErr, IterErr> +where + FindErr: std::error::Error + 'static, + IterErr: std::error::Error + 'static, +{ + #[error(transparent)] + CommitDecode(gix_object::decode::Error), + #[error(transparent)] + FindExisting(#[from] FindErr), + #[error(transparent)] + InputIteration(IterErr), + #[error(transparent)] + TreeTraverse(gix_traverse::tree::breadthfirst::Error), + #[error(transparent)] + TreeChanges(gix_diff::tree::changes::Error), + #[error("Operation interrupted")] + Interrupted, +} diff --git a/vendor/gix-pack/src/data/output/count/objects/util.rs b/vendor/gix-pack/src/data/output/count/objects/util.rs new file mode 100644 index 000000000..a80841313 --- /dev/null +++ b/vendor/gix-pack/src/data/output/count/objects/util.rs @@ -0,0 +1,24 @@ +pub trait InsertImmutable { + fn insert(&self, id: gix_hash::ObjectId) -> bool; +} + +mod trait_impls { + use gix_hash::ObjectId; + use std::cell::RefCell; + + use gix_hashtable::HashSet; + + use super::InsertImmutable; + + impl InsertImmutable for gix_hashtable::sync::ObjectIdMap<()> { + fn insert(&self, id: ObjectId) -> bool { + self.insert(id, ()).is_none() + } + } + + impl InsertImmutable for RefCell<HashSet<ObjectId>> { + fn insert(&self, item: ObjectId) -> bool { + self.borrow_mut().insert(item) + } + } +} |