Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
node: Implement canonical identity function
Alexis Sellier committed 3 years ago
commit 8760eee729f51e1a03aebba56d752c68f66f76f6
parent bc226a9c38e117e2c2ec5ca2776499087d86961c
1 file changed +57 -5
modified node/src/storage/git.rs
@@ -25,6 +25,20 @@ pub static REMOTES_GLOB: Lazy<refspec::PatternString> =
pub static SIGNATURES_GLOB: Lazy<refspec::PatternString> =
    Lazy::new(|| refspec::pattern!("refs/remotes/*/radicle/signature"));

+
#[derive(Error, Debug)]
+
pub enum IdentityError {
+
    #[error("identity branches diverge from each other")]
+
    BranchesDiverge,
+
    #[error("identity branches are in an invalid state")]
+
    InvalidState,
+
    #[error("git: {0}")]
+
    Git(#[from] git2::Error),
+
    #[error("git: {0}")]
+
    GitExt(#[from] git::Error),
+
    #[error("refs: {0}")]
+
    Refs(#[from] refs::Error),
+
}
+

pub struct Storage {
    path: PathBuf,
}
@@ -263,18 +277,56 @@ impl Repository {
        }
    }

-
    pub fn identity(&self) -> Result<identity::Doc<Unverified>, refs::Error> {
+
    /// Return the canonical identity [`git::Oid`] and document.
+
    pub fn identity(&self) -> Result<(git::Oid, identity::Doc<Unverified>), IdentityError> {
        let mut heads = Vec::new();
        for remote in self.remote_ids()? {
            let remote = remote?;
            let oid = Doc::<Unverified>::head(&remote, self)?.unwrap();

-
            heads.push(*oid);
+
            heads.push(oid.into());
+
        }
+
        // Keep track of the longest identity branch.
+
        let mut longest = heads.pop().ok_or(IdentityError::InvalidState)?;
+

+
        for head in &heads {
+
            let base = self.raw().merge_base(*head, longest)?;
+

+
            if base == longest {
+
                // `head` is a successor of `longest`. Update `longest`.
+
                //
+
                //   o head
+
                //   |
+
                //   o longest (base)
+
                //   |
+
                //
+
                longest = *head;
+
            } else if base == *head || *head == longest {
+
                // `head` is an ancestor of `longest`, or equal to it. Do nothing.
+
                //
+
                //   o longest             o longest, head (base)
+
                //   |                     |
+
                //   o head (base)   OR    o
+
                //   |                     |
+
                //
+
            } else {
+
                // The merge base between `head` and `longest` (`base`)
+
                // is neither `head` nor `longest`. Therefore, the branches have
+
                // diverged.
+
                //
+
                //    longest   head
+
                //           \ /
+
                //            o (base)
+
                //            |
+
                //
+
                return Err(IdentityError::BranchesDiverge);
+
            }
        }
-
        let oid = self.raw().merge_base_many(&heads)?;
-
        let (doc, _) = Doc::load_at(oid.into(), self)?.ok_or(refs::Error::NotFound)?;

-
        Ok(doc)
+
        Doc::load_at(longest.into(), self)?
+
            .ok_or(refs::Error::NotFound)
+
            .map(|(doc, _)| (longest.into(), doc))
+
            .map_err(IdentityError::from)
    }

    pub fn remote_ids(