1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
//! **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<crate::TargetRef<'_>> {
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<crate::TargetRef<'_>> {
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;