summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/remote/connection/fetch/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/remote/connection/fetch/mod.rs')
-rw-r--r--vendor/gix/src/remote/connection/fetch/mod.rs240
1 files changed, 240 insertions, 0 deletions
diff --git a/vendor/gix/src/remote/connection/fetch/mod.rs b/vendor/gix/src/remote/connection/fetch/mod.rs
new file mode 100644
index 000000000..4ce631b1e
--- /dev/null
+++ b/vendor/gix/src/remote/connection/fetch/mod.rs
@@ -0,0 +1,240 @@
+use gix_protocol::transport::client::Transport;
+
+use crate::{
+ bstr::BString,
+ remote,
+ remote::{
+ fetch::{DryRun, RefMap},
+ ref_map, Connection,
+ },
+ Progress,
+};
+
+mod error;
+pub use error::Error;
+
+use crate::remote::fetch::WritePackedRefs;
+
+/// The way reflog messages should be composed whenever a ref is written with recent objects from a remote.
+pub enum RefLogMessage {
+ /// Prefix the log with `action` and generate the typical suffix as `git` would.
+ Prefixed {
+ /// The action to use, like `fetch` or `pull`.
+ action: String,
+ },
+ /// Control the entire message, using `message` verbatim.
+ Override {
+ /// The complete reflog message.
+ message: BString,
+ },
+}
+
+impl RefLogMessage {
+ pub(crate) fn compose(&self, context: &str) -> BString {
+ match self {
+ RefLogMessage::Prefixed { action } => format!("{action}: {context}").into(),
+ RefLogMessage::Override { message } => message.to_owned(),
+ }
+ }
+}
+
+/// The status of the repository after the fetch operation
+#[derive(Debug, Clone)]
+pub enum Status {
+ /// Nothing changed as the remote didn't have anything new compared to our tracking branches, thus no pack was received
+ /// and no new object was added.
+ NoPackReceived {
+ /// However, depending on the refspecs, references might have been updated nonetheless to point to objects as
+ /// reported by the remote.
+ update_refs: refs::update::Outcome,
+ },
+ /// There was at least one tip with a new object which we received.
+ Change {
+ /// Information collected while writing the pack and its index.
+ write_pack_bundle: gix_pack::bundle::write::Outcome,
+ /// Information collected while updating references.
+ update_refs: refs::update::Outcome,
+ },
+ /// A dry run was performed which leaves the local repository without any change
+ /// nor will a pack have been received.
+ DryRun {
+ /// Information about what updates to refs would have been done.
+ update_refs: refs::update::Outcome,
+ },
+}
+
+/// The outcome of receiving a pack via [`Prepare::receive()`].
+#[derive(Debug, Clone)]
+pub struct Outcome {
+ /// The result of the initial mapping of references, the prerequisite for any fetch.
+ pub ref_map: RefMap,
+ /// The status of the operation to indicate what happened.
+ pub status: Status,
+}
+
+/// The progress ids used in during various steps of the fetch operation.
+///
+/// Note that tagged progress isn't very widely available yet, but support can be improved as needed.
+///
+/// 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 progress name is defined by the remote and the progress messages it sets, along with their progress values and limits.
+ RemoteProgress,
+}
+
+impl From<ProgressId> for gix_features::progress::Id {
+ fn from(v: ProgressId) -> Self {
+ match v {
+ ProgressId::RemoteProgress => *b"FERP",
+ }
+ }
+}
+
+///
+pub mod negotiate;
+
+///
+pub mod prepare {
+ /// The error returned by [`prepare_fetch()`][super::Connection::prepare_fetch()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Cannot perform a meaningful fetch operation without any configured ref-specs")]
+ MissingRefSpecs,
+ #[error(transparent)]
+ RefMap(#[from] crate::remote::ref_map::Error),
+ }
+
+ impl gix_protocol::transport::IsSpuriousError for Error {
+ fn is_spurious(&self) -> bool {
+ match self {
+ Error::RefMap(err) => err.is_spurious(),
+ _ => false,
+ }
+ }
+ }
+}
+
+impl<'remote, 'repo, T, P> Connection<'remote, 'repo, T, P>
+where
+ T: Transport,
+ P: Progress,
+{
+ /// Perform a handshake with the remote and obtain a ref-map with `options`, and from there one
+ /// Note that at this point, the `transport` should already be configured using the [`transport_mut()`][Self::transport_mut()]
+ /// method, as it will be consumed here.
+ ///
+ /// From there additional properties of the fetch can be adjusted to override the defaults that are configured via gix-config.
+ ///
+ /// # Async Experimental
+ ///
+ /// Note that this implementation is currently limited correctly in blocking mode only as it relies on Drop semantics to close the connection
+ /// should the fetch not be performed. Furthermore, there the code doing the fetch is inherently blocking and it's not offloaded to a thread,
+ /// making this call block the executor.
+ /// It's best to unblock it by placing it into its own thread or offload it should usage in an async context be truly required.
+ #[allow(clippy::result_large_err)]
+ #[gix_protocol::maybe_async::maybe_async]
+ pub async fn prepare_fetch(
+ mut self,
+ options: ref_map::Options,
+ ) -> Result<Prepare<'remote, 'repo, T, P>, prepare::Error> {
+ if self.remote.refspecs(remote::Direction::Fetch).is_empty() {
+ return Err(prepare::Error::MissingRefSpecs);
+ }
+ let ref_map = self.ref_map_inner(options).await?;
+ Ok(Prepare {
+ con: Some(self),
+ ref_map,
+ dry_run: DryRun::No,
+ reflog_message: None,
+ write_packed_refs: WritePackedRefs::Never,
+ })
+ }
+}
+
+impl<'remote, 'repo, T, P> Prepare<'remote, 'repo, T, P>
+where
+ T: Transport,
+{
+ /// Return the ref_map (that includes the server handshake) which was part of listing refs prior to fetching a pack.
+ pub fn ref_map(&self) -> &RefMap {
+ &self.ref_map
+ }
+}
+
+mod config;
+mod receive_pack;
+///
+#[path = "update_refs/mod.rs"]
+pub mod refs;
+
+/// A structure to hold the result of the handshake with the remote and configure the upcoming fetch operation.
+pub struct Prepare<'remote, 'repo, T, P>
+where
+ T: Transport,
+{
+ con: Option<Connection<'remote, 'repo, T, P>>,
+ ref_map: RefMap,
+ dry_run: DryRun,
+ reflog_message: Option<RefLogMessage>,
+ write_packed_refs: WritePackedRefs,
+}
+
+/// Builder
+impl<'remote, 'repo, T, P> Prepare<'remote, 'repo, T, P>
+where
+ T: Transport,
+{
+ /// If dry run is enabled, no change to the repository will be made.
+ ///
+ /// This works by not actually fetching the pack after negotiating it, nor will refs be updated.
+ pub fn with_dry_run(mut self, enabled: bool) -> Self {
+ self.dry_run = if enabled { DryRun::Yes } else { DryRun::No };
+ self
+ }
+
+ /// If enabled, don't write ref updates to loose refs, but put them exclusively to packed-refs.
+ ///
+ /// This improves performance and allows case-sensitive filesystems to deal with ref names that would otherwise
+ /// collide.
+ pub fn with_write_packed_refs_only(mut self, enabled: bool) -> Self {
+ self.write_packed_refs = if enabled {
+ WritePackedRefs::Only
+ } else {
+ WritePackedRefs::Never
+ };
+ self
+ }
+
+ /// Set the reflog message to use when updating refs after fetching a pack.
+ pub fn with_reflog_message(mut self, reflog_message: RefLogMessage) -> Self {
+ self.reflog_message = reflog_message.into();
+ self
+ }
+}
+
+impl<'remote, 'repo, T, P> Drop for Prepare<'remote, 'repo, T, P>
+where
+ T: Transport,
+{
+ fn drop(&mut self) {
+ if let Some(mut con) = self.con.take() {
+ #[cfg(feature = "async-network-client")]
+ {
+ // TODO: this should be an async drop once the feature is available.
+ // Right now we block the executor by forcing this communication, but that only
+ // happens if the user didn't actually try to receive a pack, which consumes the
+ // connection in an async context.
+ gix_protocol::futures_lite::future::block_on(gix_protocol::indicate_end_of_interaction(
+ &mut con.transport,
+ ))
+ .ok();
+ }
+ #[cfg(not(feature = "async-network-client"))]
+ {
+ gix_protocol::indicate_end_of_interaction(&mut con.transport).ok();
+ }
+ }
+ }
+}