summaryrefslogtreecommitdiffstats
path: root/vendor/gix-pack/src/data/output/count
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-pack/src/data/output/count
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-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.rs49
-rw-r--r--vendor/gix-pack/src/data/output/count/objects/mod.rs405
-rw-r--r--vendor/gix-pack/src/data/output/count/objects/reduce.rs49
-rw-r--r--vendor/gix-pack/src/data/output/count/objects/tree.rs124
-rw-r--r--vendor/gix-pack/src/data/output/count/objects/types.rs105
-rw-r--r--vendor/gix-pack/src/data/output/count/objects/util.rs24
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)
+ }
+ }
+}