Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle: Implement canonical fork
Alexis Sellier committed 3 years ago
commit f5bfb8d90a1a93ea2c888503e9948a91d9e9a8d2
parent a37424d72335fa41303ef78be66cf30b88ab420e
5 files changed +67 -5
modified radicle/src/identity/doc.rs
@@ -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
modified radicle/src/rad.rs
@@ -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();
modified radicle/src/storage.rs
@@ -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> {
modified radicle/src/storage/git.rs
@@ -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 {
modified radicle/src/test/storage.rs
@@ -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 {