]> git.proxmox.com Git - rustc.git/blame - vendor/gix/src/clone/checkout.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / vendor / gix / src / clone / checkout.rs
CommitLineData
0a29b90c
FG
1use crate::{clone::PrepareCheckout, Repository};
2
3///
4pub mod main_worktree {
5 use std::{path::PathBuf, sync::atomic::AtomicBool};
6
0a29b90c
FG
7 use crate::{clone::PrepareCheckout, Progress, Repository};
8
9 /// The error returned by [`PrepareCheckout::main_worktree()`].
10 #[derive(Debug, thiserror::Error)]
11 #[allow(missing_docs)]
12 pub enum Error {
13 #[error("Repository at \"{}\" is a bare repository and cannot have a main worktree checkout", git_dir.display())]
14 BareRepository { git_dir: PathBuf },
15 #[error("The object pointed to by HEAD is not a treeish")]
16 NoHeadTree(#[from] crate::object::peel::to_kind::Error),
17 #[error("Could not create index from tree at {id}")]
18 IndexFromTree {
19 id: gix_hash::ObjectId,
20 source: gix_traverse::tree::breadthfirst::Error,
21 },
22 #[error(transparent)]
23 WriteIndex(#[from] gix_index::file::write::Error),
24 #[error(transparent)]
25 CheckoutOptions(#[from] crate::config::checkout_options::Error),
26 #[error(transparent)]
4b012472 27 IndexCheckout(#[from] gix_worktree_state::checkout::Error),
0a29b90c
FG
28 #[error("Failed to reopen object database as Arc (only if thread-safety wasn't compiled in)")]
29 OpenArcOdb(#[from] std::io::Error),
30 #[error("The HEAD reference could not be located")]
31 FindHead(#[from] crate::reference::find::existing::Error),
32 #[error("The HEAD reference could not be located")]
33 PeelHeadToId(#[from] crate::head::peel::Error),
34 }
35
36 /// The progress ids used in [`PrepareCheckout::main_worktree()`].
37 ///
38 /// Use this information to selectively extract the progress of interest in case the parent application has custom visualization.
39 #[derive(Debug, Copy, Clone)]
40 pub enum ProgressId {
41 /// The amount of files checked out thus far.
42 CheckoutFiles,
43 /// The amount of bytes written in total, the aggregate of the size of the content of all files thus far.
44 BytesWritten,
45 }
46
47 impl From<ProgressId> for gix_features::progress::Id {
48 fn from(v: ProgressId) -> Self {
49 match v {
50 ProgressId::CheckoutFiles => *b"CLCF",
51 ProgressId::BytesWritten => *b"CLCB",
52 }
53 }
54 }
55
56 /// Modification
57 impl PrepareCheckout {
58 /// Checkout the main worktree, determining how many threads to use by looking at `checkout.workers`, defaulting to using
59 /// on thread per logical core.
60 ///
61 /// Note that this is a no-op if the remote was empty, leaving this repository empty as well. This can be validated by checking
62 /// if the `head()` of the returned repository is not unborn.
781aab86 63 pub fn main_worktree<P>(
0a29b90c 64 &mut self,
781aab86 65 mut progress: P,
0a29b90c 66 should_interrupt: &AtomicBool,
781aab86
FG
67 ) -> Result<(Repository, gix_worktree_state::checkout::Outcome), Error>
68 where
69 P: gix_features::progress::NestedProgress,
70 P::SubProgress: gix_features::progress::NestedProgress + 'static,
71 {
72 self.main_worktree_inner(&mut progress, should_interrupt)
73 }
74
75 fn main_worktree_inner(
76 &mut self,
77 progress: &mut dyn gix_features::progress::DynNestedProgress,
78 should_interrupt: &AtomicBool,
79 ) -> Result<(Repository, gix_worktree_state::checkout::Outcome), Error> {
80 let _span = gix_trace::coarse!("gix::clone::PrepareCheckout::main_worktree()");
0a29b90c
FG
81 let repo = self
82 .repo
83 .as_ref()
84 .expect("still present as we never succeeded the worktree checkout yet");
85 let workdir = repo.work_dir().ok_or_else(|| Error::BareRepository {
86 git_dir: repo.git_dir().to_owned(),
87 })?;
4b012472 88 let root_tree = match repo.head()?.try_peel_to_id_in_place()? {
0a29b90c
FG
89 Some(id) => id.object().expect("downloaded from remote").peel_to_tree()?.id,
90 None => {
91 return Ok((
92 self.repo.take().expect("still present"),
781aab86 93 gix_worktree_state::checkout::Outcome::default(),
0a29b90c
FG
94 ))
95 }
96 };
4b012472
FG
97 let index = gix_index::State::from_tree(&root_tree, &repo.objects).map_err(|err| Error::IndexFromTree {
98 id: root_tree,
99 source: err,
100 })?;
0a29b90c
FG
101 let mut index = gix_index::File::from_state(index, repo.index_path());
102
781aab86
FG
103 let mut opts = repo
104 .config
105 .checkout_options(repo, gix_worktree::stack::state::attributes::Source::IdMapping)?;
0a29b90c
FG
106 opts.destination_is_initially_empty = true;
107
781aab86
FG
108 let mut files = progress.add_child_with_id("checkout".to_string(), ProgressId::CheckoutFiles.into());
109 let mut bytes = progress.add_child_with_id("writing".to_string(), ProgressId::BytesWritten.into());
0a29b90c
FG
110
111 files.init(Some(index.entries().len()), crate::progress::count("files"));
112 bytes.init(None, crate::progress::bytes());
113
114 let start = std::time::Instant::now();
781aab86 115 let outcome = gix_worktree_state::checkout(
0a29b90c
FG
116 &mut index,
117 workdir,
4b012472 118 repo.objects.clone().into_arc()?,
781aab86
FG
119 &files,
120 &bytes,
0a29b90c
FG
121 should_interrupt,
122 opts,
123 )?;
124 files.show_throughput(start);
125 bytes.show_throughput(start);
126
127 index.write(Default::default())?;
128 Ok((self.repo.take().expect("still present"), outcome))
129 }
130 }
131}
132
133/// Access
134impl PrepareCheckout {
135 /// Get access to the repository while the checkout isn't yet completed.
136 ///
137 /// # Panics
138 ///
139 /// If the checkout is completed and the [`Repository`] was already passed on to the caller.
140 pub fn repo(&self) -> &Repository {
141 self.repo
142 .as_ref()
143 .expect("present as checkout operation isn't complete")
144 }
145}
146
147/// Consumption
148impl PrepareCheckout {
149 /// Persist the contained repository as is even if an error may have occurred when checking out the main working tree.
150 pub fn persist(mut self) -> Repository {
151 self.repo.take().expect("present and consumed once")
152 }
153}
154
155impl Drop for PrepareCheckout {
156 fn drop(&mut self) {
157 if let Some(repo) = self.repo.take() {
158 std::fs::remove_dir_all(repo.work_dir().unwrap_or_else(|| repo.path())).ok();
159 }
160 }
161}
162
163impl From<PrepareCheckout> for Repository {
164 fn from(prep: PrepareCheckout) -> Self {
165 prep.persist()
166 }
167}