remote-helper: remove check for fast-forward

Since the commit is now always pushed to the Radicle storage, there is no need
to check for fast-forward. If heads diverge, it will be reported by the quorum
error.
This commit is contained in:
Fintan Halpenny 2025-08-06 07:43:20 +01:00 committed by Lorenz Leutgeb
parent 119a124897
commit 690f6b02c0
3 changed files with 32 additions and 111 deletions

View File

@ -361,9 +361,9 @@ pub fn run(
.ok_or(Error::UnknownObjectType { oid: *src })?;
let canonical = canonical::Canonical::new(me, object, canonical)?;
match canonical.quorum(&working) {
match canonical.quorum() {
Ok(quorum) => set_canonical_refs.push(quorum),
Err(e) => canonical::io::handle_error(e, &dst, hints)?,
Err(e) => canonical::io::handle_error(e)?,
}
}
Ok(explorer)

View File

@ -1,24 +1,12 @@
use radicle::git;
use radicle::git::canonical;
use radicle::git::canonical::effects;
use radicle::git::raw::Repository;
use radicle::git::canonical::error::QuorumError;
use radicle::git::canonical::QuorumWithConvergence;
use radicle::prelude::Did;
use super::error;
pub(crate) struct Vote {
object: canonical::Object,
}
impl Vote {
pub(crate) fn id(&self) -> git::Oid {
self.object.id()
}
}
/// Validates a vote to update a canonical reference during push.
pub(crate) struct Canonical<'a, 'b, 'r, R> {
vote: Vote,
canonical: canonical::CanonicalWithConvergence<'a, 'b, 'r, R>,
}
@ -33,7 +21,6 @@ where
) -> Result<Self, canonical::error::FindObjectsError> {
let canonical = canonical.find_objects()?;
Ok(Self {
vote: Vote { object },
canonical: canonical.with_convergence(me, object),
})
}
@ -55,89 +42,47 @@ where
/// Ensures that the new head and the canonical commit do not diverge.
///
/// [`head`]: crate::push::canonical::Canonical::head
pub fn quorum(
self,
working: &Repository,
) -> Result<(git::Qualified<'a>, canonical::Object), error::Canonical> {
match self.canonical.quorum() {
Ok(result) => {
let canonical::QuorumWithConvergence { quorum, converges } = result;
let canonical::Quorum { refname, object } = quorum;
let quorum_head = object.id();
// Canonical head is an ancestor of head.
let is_ff = self.vote.id() == quorum_head
|| working
.graph_descendant_of(*self.vote.id(), *quorum_head)
.map_err(|err| {
error::Canonical::graph_descendant(self.vote.id(), quorum_head, err)
})?;
if !is_ff && !converges {
Err(error::Canonical::heads_diverge(self.vote.id(), quorum_head))
} else {
Ok((refname, object))
}
}
Err(err) => Err(err.into()),
}
pub fn quorum(self) -> Result<(git::Qualified<'a>, canonical::Object), QuorumError> {
self.canonical
.quorum()
.map(|QuorumWithConvergence { quorum, .. }| (quorum.refname, quorum.object))
}
}
pub(crate) mod io {
use radicle::git;
use radicle::git::canonical::error::QuorumError;
use crate::push::error;
use crate::{hint, warn};
use crate::warn;
/// Handle recoverable errors, printing relevant information to the
/// terminal. Otherwise, convert the error into an unrecoverable error
/// [`error::CanonicalUnrecoverable`].
pub fn handle_error(
e: error::Canonical,
canonical: &git::Qualified,
hints: bool,
) -> Result<(), error::CanonicalUnrecoverable> {
pub fn handle_error(e: QuorumError) -> Result<(), error::CanonicalUnrecoverable> {
match e {
error::Canonical::GraphDescendant(e) => Err(e.into()),
error::Canonical::HeadsDiverge(e) => {
if hints {
hint(
format!("you are attempting to push a commit that would cause \
your upstream to diverge from the canonical reference {canonical}"),
);
hint(
"to integrate the remote changes, run `git pull --rebase` \
and try again",
);
}
Err(e.into())
QuorumError::Convergence(err) => Err(err.into()),
QuorumError::MergeBase(err) => Err(err.into()),
e @ QuorumError::DivergingCommits { .. } => {
warn(e.to_string());
warn("it is recommended to find a commit to agree upon");
Ok(())
}
error::Canonical::Quorum(e) => {
match e {
QuorumError::Convergence(err) => Err(err.into()),
QuorumError::MergeBase(err) => Err(err.into()),
e @ QuorumError::DivergingCommits { .. } => {
warn(e.to_string());
warn("it is recommended to find a commit to agree upon");
Ok(())
}
e @ QuorumError::DivergingTags { .. } => {
warn(e.to_string());
warn("it is recommended to find a tag to agree upon");
Ok(())
}
e @ QuorumError::DifferentTypes { .. } => {
warn(e.to_string());
warn("it is recommended to find an object type (either commit or tag) to agree upon");
Ok(())
}
e @ QuorumError::NoCandidates { .. } => {
warn(e.to_string());
warn("it is recommended to find an object (either commit or tag) to agree upon");
Ok(())
}
}
e @ QuorumError::DivergingTags { .. } => {
warn(e.to_string());
warn("it is recommended to find a tag to agree upon");
Ok(())
}
e @ QuorumError::DifferentTypes { .. } => {
warn(e.to_string());
warn(
"it is recommended to find an object type (either commit or tag) to agree upon",
);
Ok(())
}
e @ QuorumError::NoCandidates { .. } => {
warn(e.to_string());
warn("it is recommended to find an object (either commit or tag) to agree upon");
Ok(())
}
}
}

View File

@ -18,30 +18,6 @@ pub enum CanonicalUnrecoverable {
Git { source: git::raw::Error },
}
#[derive(Debug, Error)]
pub enum Canonical {
#[error(transparent)]
GraphDescendant(GraphDescendant),
#[error(transparent)]
HeadsDiverge(HeadsDiverge),
#[error(transparent)]
Quorum(#[from] canonical::error::QuorumError),
}
impl Canonical {
pub fn graph_descendant(head: git::Oid, canonical: git::Oid, source: git::raw::Error) -> Self {
Self::GraphDescendant(GraphDescendant {
head,
canonical,
source,
})
}
pub fn heads_diverge(head: git::Oid, canonical: git::Oid) -> Self {
Self::HeadsDiverge(HeadsDiverge { head, canonical })
}
}
#[derive(Debug, Error)]
#[error("failed to check if {head} is an ancestor of {canonical} due to: {source}")]
pub struct GraphDescendant {