summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/clone/checkout.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/clone/checkout.rs')
-rw-r--r--vendor/gix/src/clone/checkout.rs161
1 files changed, 161 insertions, 0 deletions
diff --git a/vendor/gix/src/clone/checkout.rs b/vendor/gix/src/clone/checkout.rs
new file mode 100644
index 000000000..50d235f13
--- /dev/null
+++ b/vendor/gix/src/clone/checkout.rs
@@ -0,0 +1,161 @@
+use crate::{clone::PrepareCheckout, Repository};
+
+///
+pub mod main_worktree {
+ use std::{path::PathBuf, sync::atomic::AtomicBool};
+
+ use gix_odb::FindExt;
+
+ use crate::{clone::PrepareCheckout, Progress, Repository};
+
+ /// The error returned by [`PrepareCheckout::main_worktree()`].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Repository at \"{}\" is a bare repository and cannot have a main worktree checkout", git_dir.display())]
+ BareRepository { git_dir: PathBuf },
+ #[error("The object pointed to by HEAD is not a treeish")]
+ NoHeadTree(#[from] crate::object::peel::to_kind::Error),
+ #[error("Could not create index from tree at {id}")]
+ IndexFromTree {
+ id: gix_hash::ObjectId,
+ source: gix_traverse::tree::breadthfirst::Error,
+ },
+ #[error(transparent)]
+ WriteIndex(#[from] gix_index::file::write::Error),
+ #[error(transparent)]
+ CheckoutOptions(#[from] crate::config::checkout_options::Error),
+ #[error(transparent)]
+ IndexCheckout(
+ #[from]
+ gix_worktree::index::checkout::Error<gix_odb::find::existing_object::Error<gix_odb::store::find::Error>>,
+ ),
+ #[error("Failed to reopen object database as Arc (only if thread-safety wasn't compiled in)")]
+ OpenArcOdb(#[from] std::io::Error),
+ #[error("The HEAD reference could not be located")]
+ FindHead(#[from] crate::reference::find::existing::Error),
+ #[error("The HEAD reference could not be located")]
+ PeelHeadToId(#[from] crate::head::peel::Error),
+ }
+
+ /// The progress ids used in [`PrepareCheckout::main_worktree()`].
+ ///
+ /// Use this information to selectively extract the progress of interest in case the parent application has custom visualization.
+ #[derive(Debug, Copy, Clone)]
+ pub enum ProgressId {
+ /// The amount of files checked out thus far.
+ CheckoutFiles,
+ /// The amount of bytes written in total, the aggregate of the size of the content of all files thus far.
+ BytesWritten,
+ }
+
+ impl From<ProgressId> for gix_features::progress::Id {
+ fn from(v: ProgressId) -> Self {
+ match v {
+ ProgressId::CheckoutFiles => *b"CLCF",
+ ProgressId::BytesWritten => *b"CLCB",
+ }
+ }
+ }
+
+ /// Modification
+ impl PrepareCheckout {
+ /// Checkout the main worktree, determining how many threads to use by looking at `checkout.workers`, defaulting to using
+ /// on thread per logical core.
+ ///
+ /// Note that this is a no-op if the remote was empty, leaving this repository empty as well. This can be validated by checking
+ /// if the `head()` of the returned repository is not unborn.
+ pub fn main_worktree(
+ &mut self,
+ mut progress: impl crate::Progress,
+ should_interrupt: &AtomicBool,
+ ) -> Result<(Repository, gix_worktree::index::checkout::Outcome), Error> {
+ let repo = self
+ .repo
+ .as_ref()
+ .expect("still present as we never succeeded the worktree checkout yet");
+ let workdir = repo.work_dir().ok_or_else(|| Error::BareRepository {
+ git_dir: repo.git_dir().to_owned(),
+ })?;
+ let root_tree = match repo.head()?.peel_to_id_in_place().transpose()? {
+ Some(id) => id.object().expect("downloaded from remote").peel_to_tree()?.id,
+ None => {
+ return Ok((
+ self.repo.take().expect("still present"),
+ gix_worktree::index::checkout::Outcome::default(),
+ ))
+ }
+ };
+ let index = gix_index::State::from_tree(&root_tree, |oid, buf| repo.objects.find_tree_iter(oid, buf).ok())
+ .map_err(|err| Error::IndexFromTree {
+ id: root_tree,
+ source: err,
+ })?;
+ let mut index = gix_index::File::from_state(index, repo.index_path());
+
+ let mut opts = repo.config.checkout_options(repo.git_dir())?;
+ opts.destination_is_initially_empty = true;
+
+ let mut files = progress.add_child_with_id("checkout", ProgressId::CheckoutFiles.into());
+ let mut bytes = progress.add_child_with_id("writing", ProgressId::BytesWritten.into());
+
+ files.init(Some(index.entries().len()), crate::progress::count("files"));
+ bytes.init(None, crate::progress::bytes());
+
+ let start = std::time::Instant::now();
+ let outcome = gix_worktree::index::checkout(
+ &mut index,
+ workdir,
+ {
+ let objects = repo.objects.clone().into_arc()?;
+ move |oid, buf| objects.find_blob(oid, buf)
+ },
+ &mut files,
+ &mut bytes,
+ should_interrupt,
+ opts,
+ )?;
+ files.show_throughput(start);
+ bytes.show_throughput(start);
+
+ index.write(Default::default())?;
+ Ok((self.repo.take().expect("still present"), outcome))
+ }
+ }
+}
+
+/// Access
+impl PrepareCheckout {
+ /// Get access to the repository while the checkout isn't yet completed.
+ ///
+ /// # Panics
+ ///
+ /// If the checkout is completed and the [`Repository`] was already passed on to the caller.
+ pub fn repo(&self) -> &Repository {
+ self.repo
+ .as_ref()
+ .expect("present as checkout operation isn't complete")
+ }
+}
+
+/// Consumption
+impl PrepareCheckout {
+ /// Persist the contained repository as is even if an error may have occurred when checking out the main working tree.
+ pub fn persist(mut self) -> Repository {
+ self.repo.take().expect("present and consumed once")
+ }
+}
+
+impl Drop for PrepareCheckout {
+ fn drop(&mut self) {
+ if let Some(repo) = self.repo.take() {
+ std::fs::remove_dir_all(repo.work_dir().unwrap_or_else(|| repo.path())).ok();
+ }
+ }
+}
+
+impl From<PrepareCheckout> for Repository {
+ fn from(prep: PrepareCheckout) -> Self {
+ prep.persist()
+ }
+}