]> git.proxmox.com Git - rustc.git/blob - vendor/gix/src/clone/fetch/mod.rs
New upstream version 1.75.0+dfsg1
[rustc.git] / vendor / gix / src / clone / fetch / mod.rs
1 use crate::clone::PrepareFetch;
2
3 /// The error returned by [`PrepareFetch::fetch_only()`].
4 #[derive(Debug, thiserror::Error)]
5 #[allow(missing_docs)]
6 pub enum Error {
7 #[error(transparent)]
8 Connect(#[from] crate::remote::connect::Error),
9 #[error(transparent)]
10 PrepareFetch(#[from] crate::remote::fetch::prepare::Error),
11 #[error(transparent)]
12 Fetch(#[from] crate::remote::fetch::Error),
13 #[error(transparent)]
14 RemoteInit(#[from] crate::remote::init::Error),
15 #[error("Custom configuration of remote to clone from failed")]
16 RemoteConfiguration(#[source] Box<dyn std::error::Error + Send + Sync>),
17 #[error("Custom configuration of connection to use when cloning failed")]
18 RemoteConnection(#[source] Box<dyn std::error::Error + Send + Sync>),
19 #[error(transparent)]
20 RemoteName(#[from] crate::config::remote::symbolic_name::Error),
21 #[error("Failed to load repo-local git configuration before writing")]
22 LoadConfig(#[from] gix_config::file::init::from_paths::Error),
23 #[error("Failed to store configured remote in memory")]
24 SaveConfig(#[from] crate::remote::save::AsError),
25 #[error("Failed to write repository configuration to disk")]
26 SaveConfigIo(#[from] std::io::Error),
27 #[error("The remote HEAD points to a reference named {head_ref_name:?} which is invalid.")]
28 InvalidHeadRef {
29 source: gix_validate::reference::name::Error,
30 head_ref_name: crate::bstr::BString,
31 },
32 #[error("Failed to update HEAD with values from remote")]
33 HeadUpdate(#[from] crate::reference::edit::Error),
34 }
35
36 /// Modification
37 impl PrepareFetch {
38 /// Fetch a pack and update local branches according to refspecs, providing `progress` and checking `should_interrupt` to stop
39 /// the operation.
40 /// On success, the persisted repository is returned, and this method must not be called again to avoid a **panic**.
41 /// On error, the method may be called again to retry as often as needed.
42 ///
43 /// If the remote repository was empty, that is newly initialized, the returned repository will also be empty and like
44 /// it was newly initialized.
45 ///
46 /// Note that all data we created will be removed once this instance drops if the operation wasn't successful.
47 ///
48 /// ### Note for users of `async`
49 ///
50 /// Even though
51 #[gix_protocol::maybe_async::maybe_async]
52 pub async fn fetch_only<P>(
53 &mut self,
54 mut progress: P,
55 should_interrupt: &std::sync::atomic::AtomicBool,
56 ) -> Result<(crate::Repository, crate::remote::fetch::Outcome), Error>
57 where
58 P: crate::NestedProgress,
59 P::SubProgress: 'static,
60 {
61 self.fetch_only_inner(&mut progress, should_interrupt).await
62 }
63
64 #[gix_protocol::maybe_async::maybe_async]
65 async fn fetch_only_inner(
66 &mut self,
67 progress: &mut dyn crate::DynNestedProgress,
68 should_interrupt: &std::sync::atomic::AtomicBool,
69 ) -> Result<(crate::Repository, crate::remote::fetch::Outcome), Error> {
70 use crate::{bstr::ByteVec, remote, remote::fetch::RefLogMessage};
71
72 let repo = self
73 .repo
74 .as_mut()
75 .expect("user error: multiple calls are allowed only until it succeeds");
76
77 let remote_name = match self.remote_name.as_ref() {
78 Some(name) => name.to_owned(),
79 None => repo
80 .config
81 .resolved
82 .string("clone", None, crate::config::tree::Clone::DEFAULT_REMOTE_NAME.name)
83 .map(|n| crate::config::tree::Clone::DEFAULT_REMOTE_NAME.try_into_symbolic_name(n))
84 .transpose()?
85 .unwrap_or_else(|| "origin".into()),
86 };
87
88 let mut remote = repo
89 .remote_at(self.url.clone())?
90 .with_refspecs(
91 Some(format!("+refs/heads/*:refs/remotes/{remote_name}/*").as_str()),
92 remote::Direction::Fetch,
93 )
94 .expect("valid static spec");
95 let mut clone_fetch_tags = None;
96 if let Some(f) = self.configure_remote.as_mut() {
97 remote = f(remote).map_err(Error::RemoteConfiguration)?;
98 } else {
99 clone_fetch_tags = remote::fetch::Tags::All.into();
100 }
101
102 let config = util::write_remote_to_local_config_file(&mut remote, remote_name.clone())?;
103
104 // Now we are free to apply remote configuration we don't want to be written to disk.
105 if let Some(fetch_tags) = clone_fetch_tags {
106 remote = remote.with_fetch_tags(fetch_tags);
107 }
108
109 // Add HEAD after the remote was written to config, we need it to know what to checkout later, and assure
110 // the ref that HEAD points to is present no matter what.
111 let head_refspec = gix_refspec::parse(
112 format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(),
113 gix_refspec::parse::Operation::Fetch,
114 )
115 .expect("valid")
116 .to_owned();
117 let pending_pack: remote::fetch::Prepare<'_, '_, _> = {
118 let mut connection = remote.connect(remote::Direction::Fetch).await?;
119 if let Some(f) = self.configure_connection.as_mut() {
120 f(&mut connection).map_err(Error::RemoteConnection)?;
121 }
122 connection
123 .prepare_fetch(&mut *progress, {
124 let mut opts = self.fetch_options.clone();
125 if !opts.extra_refspecs.contains(&head_refspec) {
126 opts.extra_refspecs.push(head_refspec)
127 }
128 opts
129 })
130 .await?
131 };
132 if pending_pack.ref_map().object_hash != repo.object_hash() {
133 unimplemented!("configure repository to expect a different object hash as advertised by the server")
134 }
135 let reflog_message = {
136 let mut b = self.url.to_bstring();
137 b.insert_str(0, "clone: from ");
138 b
139 };
140 let outcome = pending_pack
141 .with_write_packed_refs_only(true)
142 .with_reflog_message(RefLogMessage::Override {
143 message: reflog_message.clone(),
144 })
145 .with_shallow(self.shallow.clone())
146 .receive_inner(progress, should_interrupt)
147 .await?;
148
149 util::append_config_to_repo_config(repo, config);
150 util::update_head(
151 repo,
152 &outcome.ref_map.remote_refs,
153 reflog_message.as_ref(),
154 remote_name.as_ref(),
155 )?;
156
157 Ok((self.repo.take().expect("still present"), outcome))
158 }
159
160 /// Similar to [`fetch_only()`][Self::fetch_only()`], but passes ownership to a utility type to configure a checkout operation.
161 #[cfg(all(feature = "worktree-mutation", feature = "blocking-network-client"))]
162 pub fn fetch_then_checkout<P>(
163 &mut self,
164 progress: P,
165 should_interrupt: &std::sync::atomic::AtomicBool,
166 ) -> Result<(crate::clone::PrepareCheckout, crate::remote::fetch::Outcome), Error>
167 where
168 P: crate::NestedProgress,
169 P::SubProgress: 'static,
170 {
171 let (repo, fetch_outcome) = self.fetch_only(progress, should_interrupt)?;
172 Ok((crate::clone::PrepareCheckout { repo: repo.into() }, fetch_outcome))
173 }
174 }
175
176 mod util;