Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle/git/repository: define Revwalk trait
Fintan Halpenny committed 24 days ago
commit 9bd68f02a3c274e73f4609a3a29c74c70dd94fee
parent 38397b53d6ebcb5c761cd9b6655e400e2c4dddba
3 files changed +209 -0
modified crates/radicle/src/git/repository.rs
@@ -7,15 +7,18 @@
//! - [`types`] — Git domain types, i.e. Blob, Commit, TreeEntry, etc.
//! - [`object`] — The Git object store; providing read and write capabilities of Git objects.
//! - [`reference`] — The Git reference store; providing read and write capabilities of Git references.
+
//! - [`revwalk`] – Git commit graph walk operations, i.e. "revwalk".
//!
//! [`reference`]: self::reference

pub mod ancestry;
pub mod object;
pub mod reference;
+
pub mod revwalk;
pub mod types;

pub use ancestry::{AheadBehind, Ancestry};
pub use object::{ObjectReader, ObjectWriter};
pub use reference::{RefReader, RefTarget, RefWriter, SymbolicRefTarget, SymbolicRefWriter};
+
pub use revwalk::{Revwalk, RevwalkPlan, SortOrder};
pub use types::{Blob, Commit, ObjectKind, TreeEntry};
added crates/radicle/src/git/repository/revwalk.rs
@@ -0,0 +1,132 @@
+
//! Git commit graph walk trait.
+
//!
+
//! [`Revwalk`] provides commit iterators, given a [`RevwalkPlan`].
+

+
pub mod error;
+
pub use error::{CommitError, InitError, OidError};
+

+
use radicle_oid::Oid;
+

+
use super::types::Commit;
+

+
/// The sort order for a [`RevwalkPlan`].
+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+
pub enum SortOrder {
+
    /// Chronological order (newest first, by commit time).
+
    #[default]
+
    Chronological,
+
    /// Topological order (parents before children).
+
    Topological,
+
    /// Reverse of default order.
+
    Reverse,
+
    /// Topological order with children before parents.
+
    TopologicalReverse,
+
}
+

+
/// A plan for walking the commit graph.
+
///
+
/// Accumulates configuration (start points, hidden commits, sort order)
+
/// and is finalised by passing it to an [`Revwalk`] implementation.
+
#[derive(Clone, Debug, Default)]
+
pub struct RevwalkPlan {
+
    start: Vec<Oid>,
+
    hide: Vec<Oid>,
+
    range: Option<(Oid, Oid)>,
+
    sort: SortOrder,
+
}
+

+
impl RevwalkPlan {
+
    /// Create a default walk, that walks all commits in chronological order.
+
    pub fn new() -> Self {
+
        Self::default()
+
    }
+

+
    /// Add a starting point for the walk.
+
    pub fn push(mut self, oid: Oid) -> Self {
+
        self.start.push(oid);
+
        self
+
    }
+

+
    /// Exclude commits reachable from this [`Oid`].
+
    pub fn hide(mut self, oid: Oid) -> Self {
+
        self.hide.push(oid);
+
        self
+
    }
+

+
    /// Walk only commits in the range `from..to` (commits reachable from
+
    /// `to` but not from `from`).
+
    pub fn range(mut self, from: Oid, to: Oid) -> Self {
+
        self.range = Some((from, to));
+
        self
+
    }
+

+
    /// Set the sort order for the walk.
+
    pub fn sort(mut self, order: SortOrder) -> Self {
+
        self.sort = order;
+
        self
+
    }
+

+
    /// The starting points for the walk.
+
    pub fn starts(&self) -> &[Oid] {
+
        &self.start
+
    }
+

+
    /// The commits to hide (exclude reachable commits).
+
    pub fn hidden(&self) -> &[Oid] {
+
        &self.hide
+
    }
+

+
    /// The range, if set.
+
    pub fn range_bounds(&self) -> Option<(Oid, Oid)> {
+
        self.range
+
    }
+

+
    /// The sort order.
+
    pub fn sort_order(&self) -> SortOrder {
+
        self.sort
+
    }
+
}
+

+
/// Git commit graph walks.
+
pub trait Revwalk {
+
    /// Iterator of commit [`Oid`]s.
+
    type RevwalkOids<'a>: Iterator<Item = Result<Oid, OidError>> + 'a
+
    where
+
        Self: 'a;
+

+
    /// Iterator of [`Commit`]s.
+
    type RevwalkCommits<'a>: Iterator<Item = Result<Commit, CommitError>> + 'a
+
    where
+
        Self: 'a;
+

+
    /// Execute a revwalk plan, returning an iterator of commit [`Oid`]s.
+
    ///
+
    /// The returned iterator yields [`OidError`] for per-step failures:
+
    /// - [`OidError::Backend`]: An unexpected error during iteration.
+
    ///
+
    /// # Errors
+
    ///
+
    /// - [`Backend`]: An unexpected error when initialising the walk.
+
    ///
+
    /// [`Backend`]: InitError::Backend
+
    fn revwalk_oids<'a>(&'a self, plan: &RevwalkPlan) -> Result<Self::RevwalkOids<'a>, InitError>;
+

+
    /// Execute a revwalk plan, returning an iterator of full [`Commit`] data.
+
    ///
+
    /// More expensive than [`Self::revwalk_oids`] since each commit is fully
+
    /// parsed during iteration.
+
    ///
+
    /// The returned iterator yields [`CommitError`] for per-step failures:
+
    /// - [`CommitError::Parse`]: A commit's raw bytes could not be parsed.
+
    /// - [`CommitError::Backend`]: An unexpected error during iteration.
+
    ///
+
    /// # Errors
+
    ///
+
    /// - [`Backend`]: An unexpected error when initialising the walk.
+
    ///
+
    /// [`Backend`]: InitError::Backend
+
    fn revwalk_commits<'a>(
+
        &'a self,
+
        plan: &RevwalkPlan,
+
    ) -> Result<Self::RevwalkCommits<'a>, InitError>;
+
}
added crates/radicle/src/git/repository/revwalk/error.rs
@@ -0,0 +1,74 @@
+
//! Errors returned by [`Revwalk`] methods and iterators.
+
//!
+
//! [`Revwalk`]: super::Revwalk
+

+
use radicle_oid::Oid;
+
use thiserror::Error;
+

+
/// Error returned by [`Revwalk::revwalk_oids`] and
+
/// [`Revwalk::revwalk_commits`] when initialising the walk.
+
///
+
/// [`Revwalk::revwalk_oids`]: super::Revwalk::revwalk_oids
+
/// [`Revwalk::revwalk_commits`]: super::Revwalk::revwalk_commits
+
#[derive(Debug, Error)]
+
#[non_exhaustive]
+
pub enum InitError {
+
    /// An error from the underlying git library.
+
    #[error(transparent)]
+
    Backend(Box<dyn std::error::Error + Send + Sync + 'static>),
+
}
+

+
impl InitError {
+
    pub fn backend<E>(err: E) -> Self
+
    where
+
        E: std::error::Error + Send + Sync + 'static,
+
    {
+
        Self::Backend(Box::new(err))
+
    }
+
}
+

+
/// Error yielded by the [`Revwalk::RevwalkOids`] iterator.
+
///
+
/// [`Revwalk::RevwalkOids`]: super::Revwalk::RevwalkOids
+
#[derive(Debug, Error)]
+
#[non_exhaustive]
+
pub enum OidError {
+
    /// An error from the underlying git library.
+
    #[error(transparent)]
+
    Backend(Box<dyn std::error::Error + Send + Sync + 'static>),
+
}
+

+
impl OidError {
+
    pub fn backend<E>(err: E) -> Self
+
    where
+
        E: std::error::Error + Send + Sync + 'static,
+
    {
+
        Self::Backend(Box::new(err))
+
    }
+
}
+

+
/// Error yielded by the [`Revwalk::RevwalkCommits`] iterator.
+
///
+
/// [`Revwalk::RevwalkCommits`]: super::Revwalk::RevwalkCommits
+
#[derive(Debug, Error)]
+
#[non_exhaustive]
+
pub enum CommitError {
+
    /// Failed to parse the raw commit bytes.
+
    #[error("failed to parse commit '{oid}': {source}")]
+
    Parse {
+
        oid: Oid,
+
        source: radicle_git_metadata::commit::ParseError,
+
    },
+
    /// An error from the underlying git library.
+
    #[error(transparent)]
+
    Backend(Box<dyn std::error::Error + Send + Sync + 'static>),
+
}
+

+
impl CommitError {
+
    pub fn backend<E>(err: E) -> Self
+
    where
+
        E: std::error::Error + Send + Sync + 'static,
+
    {
+
        Self::Backend(Box::new(err))
+
    }
+
}