gix_diff/tree/visit.rs
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 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
use gix_hash::ObjectId;
use gix_object::{tree, tree::EntryMode};
/// A way to recognize and associate different [`Change`] instances.
///
/// These are unique only within one diff operation.
pub type ChangeId = u32;
/// Identifies a relationship between this instance and another one.
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub enum Relation {
/// This is a parent with the given ID, which will always have at least one child
/// assuming that empty directories are not allowed in valid trees.
/// It's also always a tree which is the start of a recursive deletion or addition.
///
/// The change with this relation is always emitted first.
Parent(ChangeId),
/// This is a direct or indirect child, tree or not tree, of the parent with the given ID.
ChildOfParent(ChangeId),
}
/// 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,
/// Possibly associate this change with another for hierarchical rename tracking.
relation: Option<Relation>,
},
/// 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,
/// Possibly associate this change with another for hierarchical rename tracking.
relation: Option<Relation>,
},
/// 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,
relation: _,
}
| Change::Deletion {
oid,
entry_mode,
relation: _,
}
| Change::Modification { oid, entry_mode, .. } => (oid, *entry_mode),
}
}
}
/// What to do after a [Change] was [recorded](super::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(…)](super::Visit::visit()).
Cancel,
}
impl Action {
/// Returns true if this action means to stop the traversal.
pub fn cancelled(&self) -> bool {
matches!(self, Action::Cancel)
}
}
#[cfg(feature = "blob")]
mod change_impls {
use gix_hash::oid;
use gix_object::tree::EntryMode;
use crate::tree::visit::Relation;
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 relation(&self) -> Option<Relation> {
match self {
Change::Addition { relation, .. } | Change::Deletion { relation, .. } => *relation,
Change::Modification { .. } => None,
}
}
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 <= 48,
"{actual} <= 48: this type shouldn't grow without us knowing"
);
}
}