Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
Use `git {send,fetch}-pack` instead of `git {push,fetch}` internally
Merged lorenz opened 7 months ago
4 files changed +46 -33 f9ff484c 86472fdc
modified crates/radicle-cli/src/commands/patch/checkout.rs
@@ -129,7 +129,7 @@ fn find_patch_commit<'a>(
    match working.find_commit(head) {
        Ok(commit) => Ok(commit),
        Err(e) if git::ext::is_not_found_err(&e) => {
-
            let output = git::process::fetch_local(
+
            let output = git::process::fetch_pack(
                Some(working.path()),
                stored,
                [head.into()],
modified crates/radicle-remote-helper/src/fetch.rs
@@ -1,5 +1,5 @@
-
use std::io;
use std::str::FromStr;
+
use std::{io, process::ExitStatus};

use thiserror::Error;

@@ -21,7 +21,15 @@ pub enum Error {
    InvalidRef(#[from] radicle::git::fmt::Error),
    /// Git error.
    #[error("git: {0}")]
-
    Git(#[from] git::raw::Error),
+
    InvalidOid(#[source] git::raw::Error),
+

+
    /// Error fetching pack from storage to working copy.
+
    #[error("`git fetch-pack` failed with exit status {status}, stderr and stdout follow:\n{stderr}\n{stdout}")]
+
    FetchPackFailed {
+
        status: ExitStatus,
+
        stderr: String,
+
        stdout: String,
+
    },
}

/// Run a git fetch command.
@@ -37,7 +45,7 @@ pub fn run<R: ReadRepository>(
        let tokens = read_line(stdin, &mut line)?;
        match tokens.as_slice() {
            ["fetch", oid, refstr] => {
-
                let oid = git::Oid::from_str(oid)?;
+
                let oid = git::Oid::from_str(oid).map_err(Error::InvalidOid)?;
                let refstr = git::RefString::try_from(*refstr)?;

                refs.push((oid, refstr));
@@ -64,7 +72,15 @@ pub fn run<R: ReadRepository>(
    // used in the working copy, this will always result in the object
    // missing. This seems to only be an issue with `libgit2`/`git2`
    // and not `git` itself.
-
    git::process::fetch_local(working, &stored, oids, verbosity.into())?;
+
    let output = git::process::fetch_pack(working, &stored, oids, verbosity.into())?;
+

+
    if !output.status.success() {
+
        return Err(Error::FetchPackFailed {
+
            stderr: String::from_utf8_lossy(&output.stderr).to_string(),
+
            stdout: String::from_utf8_lossy(&output.stdout).to_string(),
+
            status: output.status,
+
        });
+
    }

    // Nb. An empty line means we're done.
    println!();
modified crates/radicle-remote-helper/src/push.rs
@@ -121,10 +121,9 @@ pub enum Error {
    #[error(transparent)]
    FindObjects(#[from] git::canonical::error::FindObjectsError),

-
    /// Errors for "internal" pushes, i.e., pushes that this process
-
    /// initiates between the working copy and storage.
-
    #[error("internal push failed with exit status {status}, stderr and stdout follow:\n{stderr}\n{stdout}")]
-
    InternalPushFailed {
+
    /// Error sending pack from the working copy to storage.
+
    #[error("`git send-pack` failed with exit status {status}, stderr and stdout follow:\n{stderr}\n{stdout}")]
+
    SendPackFailed {
        status: ExitStatus,
        stderr: String,
        stdout: String,
@@ -883,27 +882,17 @@ fn push_ref(
    stored: &git::raw::Repository,
    verbosity: Verbosity,
) -> Result<(), Error> {
-
    let url = git::url::File::new(stored.path()).to_string();
+
    let path = dunce::canonicalize(stored.path())?.display().to_string();
    // Nb. The *force* indicator (`+`) is processed by Git tooling before we even reach this code.
    // This happens during the `list for-push` phase.
    let refspec = git::Refspec { src, dst, force };

-
    let mut args = vec![
-
        "push".to_string(),
-
        // This push is "internal" from the point of view of the user.
-
        // If they want to run a pre-push hook, then they would configure
-
        // this on their remote, and the hook would run before we get here.
-
        // However, the context of this invocation of `git push` is
-
        // the same repository, so if the user has configured a pre-push
-
        // hook it would run twice, which is not what we want, so set this
-
        // option.
-
        "--no-verify".to_string(),
-
    ];
+
    let mut args = vec!["send-pack".to_string()];

    let verbosity: git::Verbosity = verbosity.into();
    args.extend(verbosity.into_flag());

-
    args.extend([url.to_string(), refspec.to_string()]);
+
    args.extend([path.to_string(), refspec.to_string()]);

    // Rely on the environment variable `GIT_DIR`.
    let working = None;
@@ -911,7 +900,7 @@ fn push_ref(
    let output = radicle::git::run(working, args)?;

    if !output.status.success() {
-
        return Err(Error::InternalPushFailed {
+
        return Err(Error::SendPackFailed {
            stderr: String::from_utf8_lossy(&output.stderr).to_string(),
            stdout: String::from_utf8_lossy(&output.stdout).to_string(),
            status: output.status,
modified crates/radicle/src/git.rs
@@ -78,6 +78,18 @@ impl Verbosity {

        Some(FLAG_PREFIX.to_string() + &flag.repeat(repetitions))
    }
+

+
    /// Clamps verbosity to a range, as some commands only accept a specific
+
    /// number of repetitions.
+
    fn clamp(self, min: i8, max: i8) -> Self {
+
        Self(self.0.clamp(min, max))
+
    }
+

+
    /// Clamps verbosity to at most `-v` or `-q`, as some commands do not accept
+
    /// repetitions.
+
    pub fn clamp_one(self) -> Self {
+
        self.clamp(-1, 1)
+
    }
}

impl From<i8> for Verbosity {
@@ -783,13 +795,13 @@ pub mod process {

    use crate::storage::ReadRepository;

-
    use super::{run, url, Oid, Verbosity};
+
    use super::{run, Oid, Verbosity};

-
    /// Perform a local fetch, i.e. `file://<storage path>`.
+
    /// Perform a local fetch, from storage using `git fetch-pack`.
    ///
    /// `oids` are the set of [`Oid`]s that are being fetched from the
    /// `storage`.
-
    pub fn fetch_local<R>(
+
    pub fn fetch_pack<R>(
        working: Option<&Path>,
        storage: &R,
        oids: impl IntoIterator<Item = Oid>,
@@ -798,13 +810,9 @@ pub mod process {
    where
        R: ReadRepository,
    {
-
        let mut args = vec![
-
            "fetch".to_string(),
-
            // Avoid writing fetch head since we're only fetching objects
-
            "--no-write-fetch-head".to_string(),
-
        ];
-
        args.extend(verbosity.into_flag());
-
        args.push(url::File::new(storage.path()).to_string());
+
        let mut args = vec!["fetch-pack".to_string()];
+
        args.extend(verbosity.clamp_one().into_flag());
+
        args.push(dunce::canonicalize(storage.path())?.display().to_string());
        args.extend(oids.into_iter().map(|oid| oid.to_string()));
        run(working, args)
    }