1 use crate::clone
::PrepareFetch
;
3 /// The error returned by [`PrepareFetch::fetch_only()`].
4 #[derive(Debug, thiserror::Error)]
8 Connect(#[from] crate::remote::connect::Error),
10 PrepareFetch(#[from] crate::remote::fetch::prepare::Error),
12 Fetch(#[from] crate::remote::fetch::Error),
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>),
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.")]
29 source
: gix_validate
::reference
::name
::Error
,
30 head_ref_name
: crate::bstr
::BString
,
32 #[error("Failed to update HEAD with values from remote")]
33 HeadUpdate(#[from] crate::reference::edit::Error),
38 /// Fetch a pack and update local branches according to refspecs, providing `progress` and checking `should_interrupt` to stop
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.
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.
46 /// Note that all data we created will be removed once this instance drops if the operation wasn't successful.
48 /// ### Note for users of `async`
51 #[gix_protocol::maybe_async::maybe_async]
52 pub async
fn fetch_only
<P
>(
55 should_interrupt
: &std
::sync
::atomic
::AtomicBool
,
56 ) -> Result
<(crate::Repository
, crate::remote
::fetch
::Outcome
), Error
>
58 P
: crate::NestedProgress
,
59 P
::SubProgress
: '
static,
61 self.fetch_only_inner(&mut progress
, should_interrupt
).await
64 #[gix_protocol::maybe_async::maybe_async]
65 async
fn fetch_only_inner(
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}
;
75 .expect("user error: multiple calls are allowed only until it succeeds");
77 let remote_name
= match self.remote_name
.as_ref() {
78 Some(name
) => name
.to_owned(),
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
))
85 .unwrap_or_else(|| "origin".into()),
89 .remote_at(self.url
.clone())?
91 Some(format
!("+refs/heads/*:refs/remotes/{remote_name}/*").as_str()),
92 remote
::Direction
::Fetch
,
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
)?
;
99 clone_fetch_tags
= remote
::fetch
::Tags
::All
.into();
102 let config
= util
::write_remote_to_local_config_file(&mut remote
, remote_name
.clone())?
;
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
);
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
,
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
)?
;
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
)
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")
135 let reflog_message
= {
136 let mut b
= self.url
.to_bstring();
137 b
.insert_str(0, "clone: from ");
140 let outcome
= pending_pack
141 .with_write_packed_refs_only(true)
142 .with_reflog_message(RefLogMessage
::Override
{
143 message
: reflog_message
.clone(),
145 .with_shallow(self.shallow
.clone())
146 .receive_inner(progress
, should_interrupt
)
149 util
::append_config_to_repo_config(repo
, config
);
152 &outcome
.ref_map
.remote_refs
,
153 reflog_message
.as_ref(),
154 remote_name
.as_ref(),
157 Ok((self.repo
.take().expect("still present"), outcome
))
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
>(
165 should_interrupt
: &std
::sync
::atomic
::AtomicBool
,
166 ) -> Result
<(crate::clone
::PrepareCheckout
, crate::remote
::fetch
::Outcome
), Error
>
168 P
: crate::NestedProgress
,
169 P
::SubProgress
: '
static,
171 let (repo
, fetch_outcome
) = self.fetch_only(progress
, should_interrupt
)?
;
172 Ok((crate::clone
::PrepareCheckout { repo: repo.into() }
, fetch_outcome
))