Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
cobs: Fix COB drafts to work correctly
Merged did:key:z6MksFqX...wzpT opened 1 year ago
5 files changed +50 -67 e130b4dc 729a6e05
modified radicle-cli/src/commands/patch/review/builder.rs
@@ -635,11 +635,7 @@ impl<'a, G: Signer> ReviewBuilder<'a, G> {
            Brain::new(self.patch_id, signer.public_key(), base, repo)?
        };
        let diff = self.diff(&brain.accepted, &tree, repo, opts)?;
-
        let drafts = DraftStore::new(*signer.public_key(), self.repo).with(
-
            signer.public_key(),
-
            &cob::patch::TYPENAME,
-
            &patch_id,
-
        )?;
+
        let drafts = DraftStore::new(self.repo, *signer.public_key());
        let mut patches = cob::patch::Cache::no_cache(&drafts)?;
        let mut patch = patches.get_mut(&patch_id)?;
        let mut queue = ReviewQueue::from(diff);
modified radicle-cob/src/object/collaboration/error.rs
@@ -10,7 +10,7 @@ pub enum Create {
    Evaluate(Box<dyn std::error::Error + Send + Sync + 'static>),
    #[error(transparent)]
    CreateChange(#[from] git::change::error::Create),
-
    #[error("failed to updated references for during object creation")]
+
    #[error("failed to updated references for during object creation: {err}")]
    Refs {
        #[source]
        err: Box<dyn std::error::Error + Send + Sync + 'static>,
@@ -40,7 +40,7 @@ pub enum Retrieve {
    Evaluate(Box<dyn std::error::Error + Send + Sync + 'static>),
    #[error(transparent)]
    Git(#[from] git2::Error),
-
    #[error("failed to get references during object retrieval")]
+
    #[error("failed to get references during object retrieval: {err}")]
    Refs {
        #[source]
        err: Box<dyn std::error::Error + Send + Sync + 'static>,
@@ -63,7 +63,7 @@ pub enum Update {
    NoSuchObject,
    #[error(transparent)]
    CreateChange(#[from] git::change::error::Create),
-
    #[error("failed to get references during object update")]
+
    #[error("failed to get references during object update: {err}")]
    Refs {
        #[source]
        err: Box<dyn std::error::Error + Send + Sync + 'static>,
modified radicle/src/cob/patch.rs
@@ -3208,7 +3208,12 @@ mod test {
        let alice = test::setup::NodeWithRepo::default();
        let checkout = alice.repo.checkout();
        let branch = checkout.branch_with([("README", b"Hello World!")]);
-
        let mut patches = Cache::no_cache(&*alice.repo).unwrap();
+
        let mut patches = {
+
            let path = alice.tmp.path().join("cobs.db");
+
            let db = cob::cache::Store::open(path).unwrap();
+
            let store = cob::patch::Patches::open(&*alice.repo).unwrap();
+
            cob::patch::Cache::open(store, db)
+
        };
        let mut patch = patches
            .create(
                "My first patch",
modified radicle/src/git.rs
@@ -324,18 +324,15 @@ pub mod refs {
                .with_namespace(remote.into())
            }

-
            /// Draft collaborative objects of a type.
+
            /// All draft collaborative object, identified by `typename` and `object_id`, for all remotes.
            ///
-
            /// `refs/namespaces/<remote>/refs/drafts/cobs/<typename>/*`
+
            /// `refs/namespaces/*/refs/drafts/cobs/<typename>/<object_id>`
            ///
-
            pub fn cobs(remote: &RemoteId, typename: &cob::TypeName) -> PatternString {
-
                Qualified::from_components(
-
                    component!("drafts"),
-
                    component!("cobs"),
-
                    Some(Component::from(typename)),
-
                )
-
                .with_namespace(remote.into())
-
                .to_pattern(refspec::pattern!("*"))
+
            pub fn cobs(typename: &cob::TypeName, object_id: &cob::ObjectId) -> PatternString {
+
                refspec::pattern!("refs/namespaces/*")
+
                    .join(refname!("refs/drafts/cobs"))
+
                    .join(Component::from(typename))
+
                    .join(Component::from(object_id))
            }
        }

modified radicle/src/storage/git/cob.rs
@@ -32,6 +32,8 @@ pub enum ObjectsError {
    Convert(#[from] cob::object::storage::convert::Error),
    #[error(transparent)]
    Git(#[from] git2::Error),
+
    #[error(transparent)]
+
    GitExt(#[from] git_ext::Error),
}

#[derive(Error, Debug)]
@@ -178,38 +180,13 @@ impl cob::object::Storage for Repository {
// which allows for some of the features needed for code review. For
// example, users can draft comments and later decide to publish them.
pub struct DraftStore<'a, R> {
-
    remote: RemoteId,
    repo: &'a R,
-
}
-

-
impl<'a, R: storage::WriteRepository> DraftStore<'a, R> {
-
    /// Create this draft store with an existing COB from storage, so that changes applied
-
    /// to this COB can evaluate properly. This creates a symbolic reference to the COB
-
    /// pointing to the public COB reference.
-
    pub fn with(
-
        self,
-
        remote: &RemoteId,
-
        typename: &cob::TypeName,
-
        id: &ObjectId,
-
    ) -> Result<Self, git::Error> {
-
        let target = git::refs::storage::cob(remote, typename, id);
-
        let name = git::refs::storage::draft::cob(remote, typename, id);
-
        let repo = self.repo.raw();
-

-
        repo.reference_symbolic(
-
            name.as_str(),
-
            target.as_str(),
-
            true, // The reference may already exist, overwrite it if so.
-
            format!("Link to COB {id} of type {typename}").as_str(),
-
        )?;
-

-
        Ok(self)
-
    }
+
    remote: RemoteId,
}

impl<'a, R> DraftStore<'a, R> {
-
    pub fn new(remote: RemoteId, repo: &'a R) -> Self {
-
        Self { remote, repo }
+
    pub fn new(repo: &'a R, remote: RemoteId) -> Self {
+
        Self { repo, remote }
    }
}

@@ -398,35 +375,43 @@ impl<'a, R: storage::WriteRepository> cob::object::Storage for DraftStore<'a, R>
        typename: &cob::TypeName,
        object_id: &cob::ObjectId,
    ) -> Result<cob::object::Objects, Self::ObjectsError> {
-
        // Nb. There can only be one draft per COB, per remote.
-
        let Ok(r) = self.repo.raw().find_reference(
-
            git::refs::storage::draft::cob(&self.remote, typename, object_id).as_str(),
-
        ) else {
-
            return Ok(Objects::default());
-
        };
-
        let r = cob::object::Reference::try_from(r).map_err(Self::ObjectsError::from)?;
+
        let mut objs = Objects::default();

-
        Ok(Objects::new(r))
-
    }
+
        // First get the signed COB tips of all remotes for this object.
+
        let signed = self
+
            .repo
+
            .references_glob(&git::refs::storage::cobs(typename, object_id))?;

-
    fn types(
-
        &self,
-
        typename: &cob::TypeName,
-
    ) -> Result<BTreeMap<cob::ObjectId, cob::object::Objects>, Self::TypesError> {
-
        let glob = git::refs::storage::draft::cobs(&self.remote, typename);
-
        let references = self.repo.references_glob(&glob)?;
-
        let mut objs = BTreeMap::new();
-

-
        for (name, id) in references {
+
        for (r, id) in signed {
            let r = cob::object::Reference {
-
                name: name.into_refstring(),
+
                name: r.to_ref_string(),
                target: cob::object::Commit { id },
            };
-
            objs.insert(id.into(), Objects::new(r));
+
            objs.push(r);
+
        }
+

+
        // Then get the draft COB tip that belongs to us only, if any.
+
        let draft_ref = &git::refs::storage::draft::cob(&self.remote, typename, object_id);
+
        if let Ok(draft_oid) = self
+
            .repo
+
            .reference_oid(&self.remote, &draft_ref.strip_namespace())
+
        {
+
            objs.push(cob::object::Reference {
+
                name: draft_ref.to_ref_string(),
+
                target: cob::object::Commit { id: draft_oid },
+
            })
        }
+

        Ok(objs)
    }

+
    fn types(
+
        &self,
+
        _typename: &cob::TypeName,
+
    ) -> Result<BTreeMap<cob::ObjectId, cob::object::Objects>, Self::TypesError> {
+
        unimplemented!()
+
    }
+

    fn update(
        &self,
        identifier: &PublicKey,