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 135 136 137 138 139 140 141 142 143 144 145 146 147
use gix_hash::ObjectId;
use gix_object::{bstr::BStr, tree, tree::EntryMode};
/// Represents any possible change in order to turn one tree into another.
#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub enum Change {
/// An entry was added, like the addition of a file or directory.
Addition {
/// The mode of the added entry.
entry_mode: tree::EntryMode,
/// The object id of the added entry.
oid: ObjectId,
},
/// An entry was deleted, like the deletion of a file or directory.
Deletion {
/// The mode of the deleted entry.
entry_mode: tree::EntryMode,
/// The object id of the deleted entry.
oid: ObjectId,
},
/// An entry was modified, e.g. changing the contents of a file adjusts its object id and turning
/// a file into a symbolic link adjusts its mode.
Modification {
/// The mode of the entry before the modification.
previous_entry_mode: tree::EntryMode,
/// The object id of the entry before the modification.
previous_oid: ObjectId,
/// The mode of the entry after the modification.
entry_mode: tree::EntryMode,
/// The object id after the modification.
oid: ObjectId,
},
}
impl Change {
/// Return the current object id.
pub fn oid(&self) -> &gix_hash::oid {
match self {
Change::Addition { oid, .. } | Change::Deletion { oid, .. } | Change::Modification { oid, .. } => oid,
}
}
/// Return the current tree entry mode.
pub fn entry_mode(&self) -> EntryMode {
match self {
Change::Addition { entry_mode, .. }
| Change::Deletion { entry_mode, .. }
| Change::Modification { entry_mode, .. } => *entry_mode,
}
}
/// Return the current object id and tree entry mode of a change.
pub fn oid_and_entry_mode(&self) -> (&gix_hash::oid, EntryMode) {
match self {
Change::Addition { oid, entry_mode }
| Change::Deletion { oid, entry_mode }
| Change::Modification { oid, entry_mode, .. } => (oid, *entry_mode),
}
}
}
/// What to do after a [Change] was [recorded][Visit::visit()].
#[derive(Default, Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub enum Action {
/// Continue the traversal of changes.
#[default]
Continue,
/// Stop the traversal of changes, making this the last call to [visit(…)][Visit::visit()].
Cancel,
}
impl Action {
/// Returns true if this action means to stop the traversal.
pub fn cancelled(&self) -> bool {
matches!(self, Action::Cancel)
}
}
/// A trait to allow responding to a traversal designed to figure out the [changes][Change]
/// to turn tree A into tree B.
pub trait Visit {
/// Sets the full path path in front of the queue so future calls to push and pop components affect it instead.
fn pop_front_tracked_path_and_set_current(&mut self);
/// Append a `component` to the end of a path, which may be empty.
fn push_back_tracked_path_component(&mut self, component: &BStr);
/// Append a `component` to the end of a path, which may be empty.
fn push_path_component(&mut self, component: &BStr);
/// Removes the last component from the path, which may leave it empty.
fn pop_path_component(&mut self);
/// Record a `change` and return an instruction whether to continue or not.
///
/// The implementation may use the current path to lean where in the tree the change is located.
fn visit(&mut self, change: Change) -> Action;
}
#[cfg(feature = "blob")]
mod change_impls {
use gix_hash::oid;
use gix_object::tree::EntryMode;
use crate::{rewrites::tracker::ChangeKind, tree::visit::Change};
impl crate::rewrites::tracker::Change for crate::tree::visit::Change {
fn id(&self) -> &oid {
match self {
Change::Addition { oid, .. } | Change::Deletion { oid, .. } | Change::Modification { oid, .. } => oid,
}
}
fn kind(&self) -> ChangeKind {
match self {
Change::Addition { .. } => ChangeKind::Addition,
Change::Deletion { .. } => ChangeKind::Deletion,
Change::Modification { .. } => ChangeKind::Modification,
}
}
fn entry_mode(&self) -> EntryMode {
match self {
Change::Addition { entry_mode, .. }
| Change::Deletion { entry_mode, .. }
| Change::Modification { entry_mode, .. } => *entry_mode,
}
}
fn id_and_entry_mode(&self) -> (&oid, EntryMode) {
match self {
Change::Addition { entry_mode, oid, .. }
| Change::Deletion { entry_mode, oid, .. }
| Change::Modification { entry_mode, oid, .. } => (oid, *entry_mode),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size_of_change() {
let actual = std::mem::size_of::<Change>();
assert!(
actual <= 46,
"{actual} <= 46: this type shouldn't grow without us knowing"
)
}
}