diff --git a/crates/radicle-cli/src/git.rs b/crates/radicle-cli/src/git.rs index d01d20b0..ca8ffdbe 100644 --- a/crates/radicle-cli/src/git.rs +++ b/crates/radicle-cli/src/git.rs @@ -132,11 +132,23 @@ pub fn repository() -> Result { } /// Execute a git command by spawning a child process. +/// Returns [`Result::Ok`] if the command *exited successfully*. pub fn git>( repo: &std::path::Path, args: impl IntoIterator, -) -> Result { - radicle::git::run::<_, _, &str, &str>(repo, args, []) +) -> anyhow::Result { + let output = radicle::git::run::<_, _, &str, &str>(repo, args, [])?; + + if !output.status.success() { + anyhow::bail!( + "`git` exited with status {}, stderr and stdout follow:\n{}\n{}\n", + output.status, + String::from_utf8_lossy(&output.stderr), + String::from_utf8_lossy(&output.stdout), + ) + } + + Ok(output) } /// Configure SSH signing in the given git repo, for the given peer. diff --git a/crates/radicle-remote-helper/src/push.rs b/crates/radicle-remote-helper/src/push.rs index c280bb20..c7432fde 100644 --- a/crates/radicle-remote-helper/src/push.rs +++ b/crates/radicle-remote-helper/src/push.rs @@ -6,6 +6,7 @@ mod error; use std::collections::HashMap; use std::io::IsTerminal; use std::path::{Path, PathBuf}; +use std::process::ExitStatus; use std::str::FromStr; use std::{assert_eq, io}; @@ -124,6 +125,15 @@ pub enum Error { UnknownObjectType { oid: git::Oid }, #[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 { + status: ExitStatus, + stderr: String, + stdout: String, + }, } /// Push command. @@ -888,13 +898,15 @@ fn push_ref( args.extend([url.to_string(), refspec.to_string()]); - radicle::git::run::<_, _, &str, &str>(repo, args, []) - .map_err(|err| { - Error::Io(std::io::Error::other(format!( - "failed to run `git push {url} {refspec}` in {:?}: {err}", - working.path() - ))) - })?; + let output = radicle::git::run::<_, _, &str, &str>(repo, args, [])?; + + if !output.status.success() { + return Err(Error::InternalPushFailed { + stderr: String::from_utf8_lossy(&output.stderr).to_string(), + stdout: String::from_utf8_lossy(&output.stdout).to_string(), + status: output.status, + }); + } Ok(()) } diff --git a/crates/radicle/src/git.rs b/crates/radicle/src/git.rs index 17c0c1a8..5b7ba0d9 100644 --- a/crates/radicle/src/git.rs +++ b/crates/radicle/src/git.rs @@ -761,29 +761,18 @@ pub fn run( repo: P, args: impl IntoIterator, envs: impl IntoIterator, -) -> Result +) -> io::Result where P: AsRef, S: AsRef, K: AsRef, V: AsRef, { - let output = Command::new("git") + Command::new("git") .current_dir(repo) .envs(envs) .args(args) - .output()?; - - if output.status.success() { - let out = if output.stdout.is_empty() { - &output.stderr - } else { - &output.stdout - }; - return Ok(String::from_utf8_lossy(out).into()); - } - - Err(io::Error::other(String::from_utf8_lossy(&output.stderr))) + .output() } /// Functions that call to the `git` CLI instead of `git2`. @@ -804,7 +793,7 @@ pub mod process { storage: &R, oids: impl IntoIterator, verbosity: Verbosity, - ) -> Result<(), io::Error> + ) -> io::Result where R: ReadRepository, { @@ -817,8 +806,7 @@ pub mod process { fetch.push(url::File::new(storage.path()).to_string()); fetch.extend(oids.into_iter().map(|oid| oid.to_string())); // N.b. `.` is used since we're fetching within the working copy - run::<_, _, &str, &str>(working, fetch, [])?; - Ok(()) + run::<_, _, &str, &str>(working, fetch, []) } }