//! **Transactions** are the only way make changes to the ref store in order to increase the chance of consistency in a multi-threaded //! environment. //! //! Transactions currently allow to… //! //! * create or update reference //! * delete references //! //! The following guarantees are made: //! //! * transactions are prepared which is when other writers are prevented from changing them //! - errors during preparations will cause a perfect rollback //! * prepared transactions are committed to finalize the change //! - errors when committing while leave the ref store in an inconsistent, but operational state. use gix_object::bstr::BString; use crate::{FullName, Target}; /// A change to the reflog. #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] pub struct LogChange { /// How to treat the reference log. pub mode: RefLog, /// If set, create a reflog even though it would otherwise not be the case as prohibited by general rules. /// Note that ref-log writing might be prohibited in the entire repository which is when this flag has no effect either. pub force_create_reflog: bool, /// The message to put into the reference log. It must be a single line, hence newlines are forbidden. /// The string can be empty to indicate there should be no message at all. pub message: BString, } impl Default for LogChange { fn default() -> Self { LogChange { mode: RefLog::AndReference, force_create_reflog: false, message: Default::default(), } } } /// The desired value of an updated value #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] pub enum PreviousValue { /// No requirements are made towards the current value, and the new value is set unconditionally. Any, /// The reference must exist and may have any value. MustExist, /// Create the ref only, hence the reference must not exist. MustNotExist, /// The ref _must_ exist and have the given value. MustExistAndMatch(Target), /// The ref _may_ exist and have the given value, or may not exist at all. ExistingMustMatch(Target), } /// A description of an edit to perform. #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] pub enum Change { /// If previous is not `None`, the ref must exist and its `oid` must agree with the `previous`, and /// we function like `update`. /// Otherwise it functions as `create-or-update`. Update { /// The desired change to the reference log. log: LogChange, /// The expected value already present in the reference. /// If a ref was existing previously this field will be overwritten with `MustExistAndMatch(actual_value)` for use after /// the transaction was committed successfully. expected: PreviousValue, /// The new state of the reference, either for updating an existing one or creating a new one. new: Target, }, /// Delete a reference and optionally check if `previous` is its content. Delete { /// The expected value of the reference, with the `MustNotExist` variant being invalid. /// /// If a previous ref existed, this value will be filled in automatically as `MustExistAndMatch(actual_value)` and /// can be accessed if the transaction was committed successfully. expected: PreviousValue, /// How to treat the reference log during deletion. log: RefLog, }, } impl Change { /// Return references to values that are the new value after the change is applied, if this is an update. pub fn new_value(&self) -> Option> { match self { Change::Update { new, .. } => new.to_ref().into(), Change::Delete { .. } => None, } } /// Return references to values that are in common between all variants and denote the previous observed value. pub fn previous_value(&self) -> Option> { match self { Change::Update { expected: PreviousValue::MustExistAndMatch(previous) | PreviousValue::ExistingMustMatch(previous), .. } | Change::Delete { expected: PreviousValue::MustExistAndMatch(previous) | PreviousValue::ExistingMustMatch(previous), .. } => previous, _ => return None, } .to_ref() .into() } } /// A reference that is to be changed #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] pub struct RefEdit { /// The change itself pub change: Change, /// The name of the reference to apply the change to pub name: FullName, /// If set, symbolic references identified by `name` will be dereferenced to have the `change` applied to their target. /// This flag has no effect if the reference isn't symbolic. pub deref: bool, } /// The way to deal with the Reflog in deletions. #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] pub enum RefLog { /// Delete or update the reference and the log AndReference, /// Delete or update only the reflog Only, } mod ext; pub use ext::RefEditsExt;