Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
radicle: Have migration repair downgrades
Lorenz Leutgeb committed 1 month ago
commit 86cacfb846af3fde033f2f447c7790a29ccd7190
parent 33db663
5 files changed +57 -42
modified crates/radicle-cli/src/commands/ls.rs
@@ -2,7 +2,7 @@ mod args;

pub use args::Args;

-
use radicle::storage::{ReadStorage, RepositoryInfo};
+
use radicle::storage::{ReadStorage, RepositoryInfo, SignedRefsInfo};

use crate::terminal as term;

@@ -34,7 +34,7 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
        if !doc.is_public() && args.public {
            continue;
        }
-
        if refs.is_none() && !args.all && !args.seeded {
+
        if matches!(refs, SignedRefsInfo::None) && !args.all && !args.seeded {
            continue;
        }
        let seeded = policy.is_seeding(&rid)?;
modified crates/radicle-protocol/src/service.rs
@@ -38,7 +38,7 @@ use radicle::node::seed;
use radicle::node::seed::Store as _;
use radicle::node::{Penalty, Severity};
use radicle::storage::refs::{FeatureLevel, SIGREFS_BRANCH};
-
use radicle::storage::RepositoryError;
+
use radicle::storage::{RepositoryError, RepositoryInfo, SignedRefsInfo};
use radicle_fetch::policy::SeedingPolicy;

use crate::fetcher;
@@ -522,33 +522,19 @@ where
        Ok(())
    }

-
    fn upgrade_sigrefs(&mut self, info: &radicle::storage::RepositoryInfo) -> Result<(), Error> {
-
        let Some(ref refs) = info.refs else {
-
            // No refs, nothing to upgrade.
-
            return Ok(());
-
        };
-

-
        if refs.feature_level() >= FeatureLevel::LATEST && refs.parent().is_some() {
-
            // Refs are at target level or above, nothing to upgrade.
+
    fn upgrade_sigrefs(&mut self, info: &RepositoryInfo) -> Result<(), Error> {
+
        if !matches!(info.refs, SignedRefsInfo::NeedsMigration) {
            return Ok(());
        }

        let rid = info.rid;

-
        if refs.parent().is_none() {
-
            log::info!(
-
                "Migrating `rad/sigrefs` of {rid} to force feature level {}, as the history currently contains only a root commit.",
-
                FeatureLevel::LATEST
-
            );
-
        } else {
-
            log::info!(
-
                "Migrating `rad/sigrefs` of {rid} from level {} which is lower than target level {}.",
-
                refs.feature_level(),
-
                FeatureLevel::LATEST
-
            );
-
        }
+
        log::info!(
+
            "Migrating `rad/sigrefs` of {rid} to force feature level {}.",
+
            FeatureLevel::LATEST
+
        );

-
        let repo = self.storage.repository_mut(info.rid)?;
+
        let repo = self.storage.repository_mut(rid)?;
        // NOTE: We assume to reach `FeatureLevel::LATEST` by signing refs.
        repo.force_sign_refs(&self.signer)?;
        Ok(())
modified crates/radicle/src/storage.rs
@@ -26,11 +26,40 @@ use crate::identity::{Identity, RepoId};
use crate::node::device::Device;
use crate::node::SyncedAt;
use crate::storage::git::NAMESPACES_GLOB;
-
use crate::storage::refs::Refs;
+
use crate::storage::refs::{FeatureLevel, Refs, SignedRefsAt};

use self::refs::{RefsAt, SignedRefs};
use crate::git::UserInfo;

+
#[derive(Debug, Clone, PartialEq, Eq)]
+
pub enum SignedRefsInfo {
+
    /// Repositories with this set to `None` are ones that are seeded but not forked.
+
    None,
+
    /// Local signed refs, if any.
+
    Some(refs::SignedRefsAt),
+
    NeedsMigration,
+
}
+

+
impl SignedRefsInfo {
+
    pub(crate) fn new(
+
        result: Result<Option<SignedRefsAt>, refs::sigrefs::read::error::Read>,
+
    ) -> Result<Self, refs::sigrefs::read::error::Read> {
+
        Ok(match result {
+
            Ok(Some(refs))
+
                if refs.feature_level() >= FeatureLevel::LATEST && refs.parent().is_some() =>
+
            {
+
                SignedRefsInfo::Some(refs)
+
            }
+
            Ok(Some(_)) => SignedRefsInfo::NeedsMigration,
+
            Ok(None) => SignedRefsInfo::None,
+
            Err(refs::sigrefs::read::error::Read::Downgrade { .. }) => {
+
                SignedRefsInfo::NeedsMigration
+
            }
+
            Err(err) => return Err(err),
+
        })
+
    }
+
}
+

/// Basic repository information.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RepositoryInfo {
@@ -40,9 +69,8 @@ pub struct RepositoryInfo {
    pub head: Oid,
    /// Identity document.
    pub doc: Doc,
-
    /// Local signed refs, if any.
-
    /// Repositories with this set to `None` are ones that are seeded but not forked.
-
    pub refs: Option<refs::SignedRefsAt>,
+
    /// Information about local signed references.
+
    pub refs: SignedRefsInfo,
    /// Sync time of the repository.
    pub synced_at: Option<SyncedAt>,
}
modified crates/radicle/src/storage/git.rs
@@ -18,9 +18,8 @@ use crate::identity::doc::DocError;
use crate::identity::{CanonicalRefs, Doc, DocAt, RepoId};
use crate::identity::{Identity, Project};
use crate::node::device::Device;
-
use crate::node::SyncedAt;
-
use crate::storage::refs;
use crate::storage::refs::{FeatureLevel, Refs, SignedRefs, SignedRefsAt};
+
use crate::storage::{refs, SignedRefsInfo};
use crate::storage::{
    ReadRepository, ReadStorage, Remote, Remotes, RepositoryInfo, SetHead, SignRepository,
    WriteRepository, WriteStorage,
@@ -167,12 +166,13 @@ impl ReadStorage for Storage {
                }
            };
            // Nb. This will be `None` if they were not found.
-
            let refs = refs::SignedRefsAt::load(self.info.key, &repo)
+
            let refs = SignedRefsInfo::new(refs::SignedRefsAt::load(self.info.key, &repo))
                .map_err(|err| Error::Refs(refs::Error::Read(err)))?;
-
            let synced_at = refs
-
                .as_ref()
-
                .map(|r| node::SyncedAt::new(r.at, &repo))
-
                .transpose()?;
+

+
            let synced_at = match &refs {
+
                SignedRefsInfo::Some(refs) => Some(node::SyncedAt::new(refs.at, &repo)?),
+
                _ => None,
+
            };

            repos.push(RepositoryInfo {
                rid,
@@ -258,12 +258,13 @@ impl Storage {
            let repo = self.repository(*rid)?;
            let (_, head) = repo.head()?;

-
            let refs = refs::SignedRefsAt::load(self.info.key, &repo)
-
                .map_err(|err| RepositoryError::from(refs::Error::Read(err)))?;
-
            let synced_at = refs
-
                .as_ref()
-
                .map(|r| SyncedAt::new(r.at, &repo))
-
                .transpose()?;
+
            let refs = SignedRefsInfo::new(refs::SignedRefsAt::load(self.info.key, &repo))
+
                .map_err(|err| Error::Refs(refs::Error::Read(err)))?;
+

+
            let synced_at = match &refs {
+
                SignedRefsInfo::Some(refs) => Some(node::SyncedAt::new(refs.at, &repo)?),
+
                _ => None,
+
            };

            Ok(RepositoryInfo {
                rid: *rid,
modified crates/radicle/src/test/storage.rs
@@ -102,7 +102,7 @@ impl ReadStorage for MockStorage {
                rid: *rid,
                head: r.head().unwrap().1,
                doc: r.doc.clone().into(),
-
                refs: None,
+
                refs: SignedRefsInfo::None,
                synced_at: None,
            })
            .collect())