remote-helper: Simplify handling of `GIT_DIR`

The contract for Git remote handlers says that we can expect
`GIT_DIR` to be set, and this also simplifies logic regarding
bare vs. non-bare repositories.
This commit is contained in:
Lorenz Leutgeb 2025-09-11 21:35:43 +02:00 committed by Fintan Halpenny
parent 354565c579
commit 606882f01f
2 changed files with 23 additions and 22 deletions

View File

@ -7,6 +7,14 @@
//! Usage of standard streams:
//! - Standard Error ([`eprintln`]) is used for communicating with the user.
//! - Standard Output ([`println`]) is used for communicating with Git tooling.
//!
//! This process assumes that the environment variable `GIT_DIR` is set
//! appropriately (to the repository being pushed from or fetched to), as
//! mentioned in the documentation on Git remote helpers.
//!
//! For example, the following two mechanisms rely on `GIT_DIR` being set:
//! - [`git::raw::Repository::open_from_env`] to open the repository
//! - [`radicle::git::run`] (with [`None`] as first argument) to invoke `git`
mod fetch;
mod list;
@ -89,12 +97,6 @@ pub enum Error {
/// I/O error.
#[error("i/o error: {0}")]
Io(#[from] io::Error),
/// The `GIT_DIR` env var is not set.
#[error("the `GIT_DIR` environment variable is not set")]
NoGitDir,
/// No parent of `GIT_DIR` was found.
#[error("expected parent of .git but found {path:?}")]
NoWorkingCopy { path: PathBuf },
/// Git error.
#[error("git: {0}")]
Git(#[from] git::raw::Error),
@ -185,13 +187,14 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
}
};
// Assume the default remote if there was no remote.
let remote = remote.unwrap_or_else(|| (*radicle::rad::REMOTE_NAME).clone());
let stored = profile.storage.repository_mut(url.repo)?;
if stored.is_empty()? {
return Err(Error::RepositoryNotFound(stored.path().to_path_buf()));
}
// `GIT_DIR` is set by Git tooling, if we're in a working copy.
let working = env::var("GIT_DIR").map(PathBuf::from);
// Whether we should output debug logs.
let debug = radicle::profile::env::debug();
@ -243,25 +246,23 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
let oid = git::Oid::from_str(oid)?;
let refstr = git::RefString::try_from(*refstr)?;
return fetch::run(vec![(oid, refstr)], stored, &stdin, opts.verbosity)
.map_err(Error::from);
return Ok(fetch::run(
vec![(oid, refstr)],
stored,
&stdin,
opts.verbosity,
)?);
}
["push", refspec] => {
// We have to be in a working copy to push.
let working = working.map_err(|_| Error::NoGitDir)?;
return push::run(
return Ok(push::run(
vec![refspec.to_string()],
&working,
// N.b. assume the default remote if there was no remote
remote.unwrap_or((*radicle::rad::REMOTE_NAME).clone()),
remote,
url,
&stored,
&profile,
&stdin,
opts,
)
.map_err(Error::from);
)?);
}
["list"] => {
list::for_fetch(&url, &profile, &stored)?;

View File

@ -5,7 +5,6 @@ mod error;
use std::collections::HashMap;
use std::io::IsTerminal;
use std::path::Path;
use std::process::ExitStatus;
use std::str::FromStr;
use std::{assert_eq, io};
@ -247,7 +246,6 @@ impl PushAction {
/// Run a git push command.
pub fn run(
mut specs: Vec<String>,
working: &Path,
remote: git::RefString,
url: Url,
stored: &storage::git::Repository,
@ -292,7 +290,9 @@ pub fn run(
let canonical_ref = git::refs::branch(project.default_branch());
let mut set_canonical_refs: Vec<(git::Qualified, git::canonical::Object)> =
Vec::with_capacity(specs.len());
let working = git::raw::Repository::open(working)?;
// Rely on the environment variable `GIT_DIR`.
let working = git::raw::Repository::open_from_env()?;
// For each refspec, push a ref or delete a ref.
for spec in specs {