radicle: Implement canonical fork
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
a37424d723
commit
f5bfb8d90a
|
|
@ -500,8 +500,8 @@ mod test {
|
|||
fixtures::project(tempdir.path().join("copy"), &storage, &alice).unwrap();
|
||||
|
||||
// Bob and Eve fork the project from Alice.
|
||||
rad::fork(&id, alice.public_key(), &bob, &storage).unwrap();
|
||||
rad::fork(&id, alice.public_key(), &eve, &storage).unwrap();
|
||||
rad::fork_remote(&id, alice.public_key(), &bob, &storage).unwrap();
|
||||
rad::fork_remote(&id, alice.public_key(), &eve, &storage).unwrap();
|
||||
|
||||
// TODO: In some cases we want to get the repo and the project, but don't
|
||||
// want to have to create a repository object twice. Perhaps there should
|
||||
|
|
|
|||
|
|
@ -88,12 +88,14 @@ pub enum ForkError {
|
|||
Storage(#[from] storage::Error),
|
||||
#[error("project `{0}` was not found in storage")]
|
||||
NotFound(Id),
|
||||
#[error("project identity error: {0}")]
|
||||
InvalidIdentity(#[from] storage::git::IdentityError),
|
||||
#[error("git: invalid reference")]
|
||||
InvalidReference,
|
||||
}
|
||||
|
||||
/// Create a local tree for an existing project, from an existing remote.
|
||||
pub fn fork<'r, G: Signer, S: storage::WriteStorage<'r>>(
|
||||
pub fn fork_remote<'r, G: Signer, S: storage::WriteStorage<'r>>(
|
||||
proj: &Id,
|
||||
remote: &RemoteId,
|
||||
signer: G,
|
||||
|
|
@ -145,6 +147,55 @@ pub fn fork<'r, G: Signer, S: storage::WriteStorage<'r>>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fork<'r, G: Signer, S: storage::WriteStorage<'r>>(
|
||||
proj: &Id,
|
||||
signer: G,
|
||||
storage: S,
|
||||
) -> Result<(), ForkError> {
|
||||
let me = signer.public_key();
|
||||
let repository = storage.repository(proj)?;
|
||||
let (canonical_id, project) = repository.project_identity()?;
|
||||
let raw = repository.raw();
|
||||
// TODO: Test the fork functions in isolation.
|
||||
// TODO: Move to function on `Repository`.
|
||||
let canonical_head = {
|
||||
let mut heads = Vec::new();
|
||||
for delegate in project.delegates.iter() {
|
||||
let name = format!("heads/{}", &project.default_branch);
|
||||
let refname = git::RefString::try_from(name.as_str()).unwrap();
|
||||
let r = repository
|
||||
.reference(&delegate.id, &refname)?
|
||||
.unwrap()
|
||||
.target()
|
||||
.unwrap();
|
||||
|
||||
heads.push(r);
|
||||
}
|
||||
|
||||
match heads.as_slice() {
|
||||
[head] => Ok(*head),
|
||||
// FIXME: This branch is not tested.
|
||||
heads => raw.merge_base_many(heads),
|
||||
}
|
||||
}?;
|
||||
|
||||
raw.reference(
|
||||
&git::refs::storage::branch(me, &project.default_branch),
|
||||
canonical_head,
|
||||
false,
|
||||
&format!("creating default branch for {me}"),
|
||||
)?;
|
||||
raw.reference(
|
||||
&git::refs::storage::id(me),
|
||||
canonical_id.into(),
|
||||
false,
|
||||
&format!("creating identity branch for {me}"),
|
||||
)?;
|
||||
storage.sign_refs(&repository, &signer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CheckoutError {
|
||||
#[error("git: {0}")]
|
||||
|
|
@ -248,7 +299,6 @@ mod tests {
|
|||
let mut rng = fastrand::Rng::new();
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let alice = MockSigner::new(&mut rng);
|
||||
let alice_id = alice.public_key();
|
||||
let bob = MockSigner::new(&mut rng);
|
||||
let bob_id = bob.public_key();
|
||||
let storage = Storage::open(tempdir.path().join("storage")).unwrap();
|
||||
|
|
@ -266,7 +316,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
// Bob forks it and creates a checkout.
|
||||
fork(&id, alice_id, &bob, &storage).unwrap();
|
||||
fork(&id, &bob, &storage).unwrap();
|
||||
checkout(&id, bob_id, tempdir.path().join("copy"), &storage).unwrap();
|
||||
|
||||
let bob_remote = storage.repository(&id).unwrap().remote(bob_id).unwrap();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use crate::git::Url;
|
|||
use crate::git::{RefError, RefStr, RefString};
|
||||
use crate::identity;
|
||||
use crate::identity::{Id, IdError, Project};
|
||||
use crate::storage::git::IdentityError;
|
||||
use crate::storage::refs::Refs;
|
||||
|
||||
use self::refs::SignedRefs;
|
||||
|
|
@ -254,6 +255,7 @@ pub trait ReadRepository<'r> {
|
|||
fn remotes(&'r self) -> Result<Self::Remotes, git2::Error>;
|
||||
/// Return the project associated with this repository.
|
||||
fn project(&self) -> Result<Project, Error>;
|
||||
fn project_identity(&self) -> Result<(Oid, identity::Doc<Unverified>), IdentityError>;
|
||||
}
|
||||
|
||||
pub trait WriteRepository<'r>: ReadRepository<'r> {
|
||||
|
|
|
|||
|
|
@ -448,6 +448,10 @@ impl<'r> ReadRepository<'r> for Repository {
|
|||
fn project(&self) -> Result<Project, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn project_identity(&self) -> Result<(Oid, identity::Doc<Unverified>), IdentityError> {
|
||||
Repository::identity(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> WriteRepository<'r> for Repository {
|
||||
|
|
|
|||
|
|
@ -125,6 +125,12 @@ impl ReadRepository<'_> for MockRepository {
|
|||
fn project(&self) -> Result<Project, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn project_identity(
|
||||
&self,
|
||||
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), git::IdentityError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteRepository<'_> for MockRepository {
|
||||
|
|
|
|||
Loading…
Reference in New Issue