radicle: use `git push` to avoid `libgit2` push
In issue #[2836] of `libgit2`, the speed of `libgit2`'s `file://` protocol for git operations is found to be very slow. This is further corrobarated by `rad init` taking 13 hours to initialise the hardenedbsd [ports] repository. There are two areas where the `heartwood` project uses `libgit2` to push using the file protocol. The first is when via the `trasnport::local` smart transport registration and the second is the final push to storage in the `git-remote-rad` binary. When both these `push` operations are changed to use the `git` binary instead, the [ports] repository can be initialised in less than 10 minutes (nearly 100x speed up). This change is clearly required if `heartwood` wishes to support larger repositories. [2836]: https://github.com/libgit2/libgit2/issues/2836 [ports]: https://git.hardenedbsd.org/hardenedbsd/ports
This commit is contained in:
parent
433483e07a
commit
8fd044833e
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
use std::collections::HashMap;
|
||||
use std::io::IsTerminal;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::{assert_eq, io};
|
||||
|
||||
|
|
@ -30,6 +30,10 @@ use crate::{hint, read_line, warn, Options};
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(
|
||||
"the Git repository found at {path:?} is a bare repository, expected a working directory"
|
||||
)]
|
||||
BareRepository { path: PathBuf },
|
||||
/// Public key doesn't match the remote namespace we're pushing to.
|
||||
#[error("cannot push to remote namespace owned by {0}")]
|
||||
KeyMismatch(Did),
|
||||
|
|
@ -777,12 +781,32 @@ fn push_ref(
|
|||
working: &git::raw::Repository,
|
||||
stored: &git::raw::Repository,
|
||||
) -> Result<(), Error> {
|
||||
let mut remote = working.remote_anonymous(&git::url::File::new(stored.path()).to_string())?;
|
||||
let refspec = git::Refspec { src, dst, force };
|
||||
|
||||
let url = git::url::File::new(stored.path()).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.
|
||||
remote.push(&[refspec.to_string().as_str()], None)?;
|
||||
let refspec = git::Refspec { src, dst, force };
|
||||
let repo = working.workdir().ok_or(Error::BareRepository {
|
||||
path: working.path().to_path_buf(),
|
||||
})?;
|
||||
|
||||
radicle::git::run::<_, _, &str, &str>(
|
||||
repo,
|
||||
[
|
||||
"push",
|
||||
url.to_string().as_str(),
|
||||
refspec.to_string().as_str(),
|
||||
],
|
||||
[],
|
||||
)
|
||||
.map_err(|err| {
|
||||
Error::Io(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"failed to run `git push {url} {refspec}` in {:?}: {err}",
|
||||
working.path()
|
||||
),
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::let_unit_value)]
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
|
@ -29,6 +29,10 @@ pub static PATCHES_REFNAME: Lazy<git::RefString> = Lazy::new(|| git::refname!("r
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum InitError {
|
||||
#[error(
|
||||
"the Git repository found at {path:?} is a bare repository, expected a working directory"
|
||||
)]
|
||||
BareRepository { path: PathBuf },
|
||||
#[error("doc: {0}")]
|
||||
Doc(#[from] DocError),
|
||||
#[error("repository: {0}")]
|
||||
|
|
@ -103,13 +107,36 @@ where
|
|||
|
||||
git::configure_repository(repo)?;
|
||||
git::configure_remote(repo, &REMOTE_NAME, url, &url.clone().with_namespace(*pk))?;
|
||||
git::push(
|
||||
repo,
|
||||
&REMOTE_NAME,
|
||||
[(
|
||||
&git::fmt::lit::refs_heads(default_branch).into(),
|
||||
&git::fmt::lit::refs_heads(default_branch).into(),
|
||||
)],
|
||||
let branch = git::Qualified::from(git::fmt::lit::refs_heads(default_branch));
|
||||
// Pushes to default branch to the namespace of the `signer`
|
||||
let pushspec = git::Refspec {
|
||||
src: branch.clone(),
|
||||
dst: branch.with_namespace(git::Component::from(pk)),
|
||||
force: false,
|
||||
};
|
||||
git::run::<_, _, &str, &str>(
|
||||
repo.workdir().ok_or(InitError::BareRepository {
|
||||
path: repo.path().to_path_buf(),
|
||||
})?,
|
||||
[
|
||||
"push",
|
||||
&format!("{}", stored.path().canonicalize()?.display()),
|
||||
&pushspec.to_string(),
|
||||
],
|
||||
[],
|
||||
)?;
|
||||
// N.b. we need to create the remote branch for the default branch
|
||||
let rad_remote =
|
||||
git::Qualified::from(git::lit::refs_remotes(&*REMOTE_COMPONENT)).join(default_branch);
|
||||
let oid = repo.refname_to_id(branch.as_str())?;
|
||||
repo.reference(
|
||||
rad_remote.as_str(),
|
||||
oid,
|
||||
false,
|
||||
&format!(
|
||||
"radicle: remote branch {}/{}",
|
||||
*REMOTE_COMPONENT, default_branch
|
||||
),
|
||||
)?;
|
||||
stored.set_remote_identity_root_to(pk, identity)?;
|
||||
stored.set_identity_head_to(identity)?;
|
||||
|
|
|
|||
Loading…
Reference in New Issue