Skip to main content

radicle_fetch/git/refs/
update.rs

1//! The set of types that describe performing updates to a Git
2//! repository.
3//!
4//! An [`Update`] describes a single update that can made to a Git
5//! repository. **Note** that it currently does not support symbolic
6//! references.
7//!
8//! A group of `Update`s is described by [`Updates`] which groups
9//! those updates by each peer's namespace, i.e. their [`PublicKey`].
10//!
11//! When an `Update` is successful the corresponding [`Updated`] is
12//! expected to be produced.
13//!
14//! The final result of applying a set of [`Updates`] is captured in
15//! the [`Applied`] type, which contains any rejected, but non-fatal,
16//! [`Update`]s and successful [`Updated`] values.
17
18use std::collections::BTreeMap;
19
20use either::Either;
21use radicle::git::fmt::{Namespaced, Qualified};
22use radicle::git::Oid;
23use radicle::prelude::PublicKey;
24
25pub use radicle::storage::RefUpdate;
26
27/// The set of applied changes from a reference store update.
28#[derive(Debug, Default)]
29pub struct Applied<'a> {
30    /// Set of rejected updates if they did not meet the update
31    /// requirements, e.g. concurrent change to previous object id,
32    /// broke fast-forward policy, etc.
33    pub rejected: Vec<Update<'a>>,
34    /// Set of successfully updated references.
35    pub updated: Vec<RefUpdate>,
36}
37
38impl Applied<'_> {
39    pub fn append(&mut self, other: &mut Self) {
40        self.rejected.append(&mut other.rejected);
41        self.updated.append(&mut other.updated);
42    }
43}
44
45/// A set of [`Update`]s that are grouped by which namespace they are
46/// affecting.
47#[derive(Clone, Default, Debug)]
48pub struct Updates<'a> {
49    pub tips: BTreeMap<PublicKey, Vec<Update<'a>>>,
50}
51
52impl<'a> Updates<'a> {
53    pub fn build(updates: impl IntoIterator<Item = (PublicKey, Update<'a>)>) -> Self {
54        let tips = updates.into_iter().fold(
55            BTreeMap::<_, Vec<Update<'a>>>::new(),
56            |mut tips, (remote, up)| {
57                tips.entry(remote)
58                    .and_modify(|ups| ups.push(up.clone()))
59                    .or_insert(vec![up]);
60                tips
61            },
62        );
63        Self { tips }
64    }
65
66    pub fn add(&mut self, remote: PublicKey, up: Update<'a>) {
67        self.tips
68            .entry(remote)
69            .and_modify(|ups| ups.push(up.clone()))
70            .or_insert(vec![up]);
71    }
72
73    pub fn append(&mut self, remote: PublicKey, mut new: Vec<Update<'a>>) {
74        self.tips
75            .entry(remote)
76            .and_modify(|ups| ups.append(&mut new))
77            .or_insert(new);
78    }
79}
80
81/// The policy to follow when an [`Update::Direct`] is not a
82/// fast-forward.
83#[derive(Clone, Copy, Debug)]
84pub enum Policy {
85    /// Abort the entire transaction.
86    Abort,
87    /// Reject this update, but continue the transaction.
88    Reject,
89    /// Allow the update.
90    Allow,
91}
92
93/// An update that can be applied to a Git repository.
94#[derive(Clone, Debug)]
95pub enum Update<'a> {
96    /// Update a direct reference, i.e. a reference that points to an
97    /// object.
98    Direct {
99        /// The name of the reference that is being updated.
100        name: Namespaced<'a>,
101        /// The resulting target of the reference that is being
102        /// updated.
103        target: Oid,
104        /// Policy to apply when an [`Update`] would not apply as a
105        /// fast-forward.
106        no_ff: Policy,
107    },
108    /// Delete a reference.
109    Prune {
110        /// The name of the reference that is being deleted.
111        name: Namespaced<'a>,
112        /// The previous value of the reference.
113        ///
114        /// It can either be a direct reference pointing to an
115        /// [`Oid`], or a symbolic reference pointing to a
116        /// [`Qualified`] reference name.
117        prev: Either<Oid, Qualified<'a>>,
118    },
119}
120
121impl<'a> Update<'a> {
122    pub fn refname(&self) -> &Namespaced<'a> {
123        match self {
124            Update::Direct { name, .. } => name,
125            Update::Prune { name, .. } => name,
126        }
127    }
128
129    pub fn into_owned<'b>(self) -> Update<'b> {
130        match self {
131            Self::Direct {
132                name,
133                target,
134                no_ff,
135            } => Update::Direct {
136                name: name.into_owned(),
137                target,
138                no_ff,
139            },
140            Self::Prune { name, prev } => Update::Prune {
141                name: name.into_owned(),
142                prev: prev.map_right(|q| q.into_owned()),
143            },
144        }
145    }
146}