Coverage Report

Created: 2026-07-14 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gitoxide/gix-diff/src/tree/visit.rs
Line
Count
Source
1
use gix_hash::ObjectId;
2
use gix_object::{tree, tree::EntryMode};
3
4
/// A way to recognize and associate different [`Change`] instances.
5
///
6
/// These are unique only within one diff operation.
7
pub type ChangeId = u32;
8
9
/// Identifies a relationship between this instance and another one.
10
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
11
pub enum Relation {
12
    /// This is a parent with the given ID, which will always have at least one child
13
    /// assuming that empty directories are not allowed in valid trees.
14
    /// It's also always a tree which is the start of a recursive deletion or addition.
15
    ///
16
    /// The change with this relation is always emitted first.
17
    Parent(ChangeId),
18
    /// This is a direct or indirect child, tree or not tree, of the parent with the given ID.
19
    ChildOfParent(ChangeId),
20
}
21
22
/// Represents any possible change in order to turn one tree into another.
23
#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
24
pub enum Change {
25
    /// An entry was added, like the addition of a file or directory.
26
    Addition {
27
        /// The mode of the added entry.
28
        entry_mode: tree::EntryMode,
29
        /// The object id of the added entry.
30
        oid: ObjectId,
31
        /// Possibly associate this change with another for hierarchical rename tracking.
32
        relation: Option<Relation>,
33
    },
34
    /// An entry was deleted, like the deletion of a file or directory.
35
    Deletion {
36
        /// The mode of the deleted entry.
37
        entry_mode: tree::EntryMode,
38
        /// The object id of the deleted entry.
39
        oid: ObjectId,
40
        /// Possibly associate this change with another for hierarchical rename tracking.
41
        relation: Option<Relation>,
42
    },
43
    /// An entry was modified, e.g. changing the contents of a file adjusts its object id and turning
44
    /// a file into a symbolic link adjusts its mode.
45
    Modification {
46
        /// The mode of the entry before the modification.
47
        previous_entry_mode: tree::EntryMode,
48
        /// The object id of the entry before the modification.
49
        previous_oid: ObjectId,
50
51
        /// The mode of the entry after the modification.
52
        entry_mode: tree::EntryMode,
53
        /// The object id after the modification.
54
        oid: ObjectId,
55
    },
56
}
57
58
impl Change {
59
    /// Return the current object id.
60
0
    pub fn oid(&self) -> &gix_hash::oid {
61
0
        match self {
62
0
            Change::Addition { oid, .. } | Change::Deletion { oid, .. } | Change::Modification { oid, .. } => oid,
63
        }
64
0
    }
Unexecuted instantiation: <gix_diff::tree::visit::Change>::oid
Unexecuted instantiation: <gix_diff::tree::visit::Change>::oid
65
    /// Return the current tree entry mode.
66
0
    pub fn entry_mode(&self) -> EntryMode {
67
0
        match self {
68
0
            Change::Addition { entry_mode, .. }
69
0
            | Change::Deletion { entry_mode, .. }
70
0
            | Change::Modification { entry_mode, .. } => *entry_mode,
71
        }
72
0
    }
Unexecuted instantiation: <gix_diff::tree::visit::Change>::entry_mode
Unexecuted instantiation: <gix_diff::tree::visit::Change>::entry_mode
73
    /// Return the current object id and tree entry mode of a change.
74
0
    pub fn oid_and_entry_mode(&self) -> (&gix_hash::oid, EntryMode) {
75
0
        match self {
76
            Change::Addition {
77
0
                oid,
78
0
                entry_mode,
79
                relation: _,
80
            }
81
            | Change::Deletion {
82
0
                oid,
83
0
                entry_mode,
84
                relation: _,
85
            }
86
0
            | Change::Modification { oid, entry_mode, .. } => (oid, *entry_mode),
87
        }
88
0
    }
Unexecuted instantiation: <gix_diff::tree::visit::Change>::oid_and_entry_mode
Unexecuted instantiation: <gix_diff::tree::visit::Change>::oid_and_entry_mode
89
}
90
91
/// What to do after a [Change] was [recorded](super::Visit::visit()).
92
///
93
/// Use [`std::ops::ControlFlow::Continue`] to continue the traversal of changes.
94
/// Use [`std::ops::ControlFlow::Break`] to stop the traversal of changes, making this the last call to [visit(…)](super::Visit::visit()).
95
pub type Action = std::ops::ControlFlow<()>;
96
97
#[cfg(feature = "blob")]
98
mod change_impls {
99
    use gix_hash::oid;
100
    use gix_object::tree::EntryMode;
101
102
    use crate::{
103
        rewrites::tracker::ChangeKind,
104
        tree::visit::{Change, Relation},
105
    };
106
107
    impl crate::rewrites::tracker::Change for crate::tree::visit::Change {
108
0
        fn id(&self) -> &oid {
109
0
            match self {
110
0
                Change::Addition { oid, .. } | Change::Deletion { oid, .. } | Change::Modification { oid, .. } => oid,
111
            }
112
0
        }
113
114
0
        fn relation(&self) -> Option<Relation> {
115
0
            match self {
116
0
                Change::Addition { relation, .. } | Change::Deletion { relation, .. } => *relation,
117
0
                Change::Modification { .. } => None,
118
            }
119
0
        }
120
121
0
        fn kind(&self) -> ChangeKind {
122
0
            match self {
123
0
                Change::Addition { .. } => ChangeKind::Addition,
124
0
                Change::Deletion { .. } => ChangeKind::Deletion,
125
0
                Change::Modification { .. } => ChangeKind::Modification,
126
            }
127
0
        }
128
129
0
        fn entry_mode(&self) -> EntryMode {
130
0
            match self {
131
0
                Change::Addition { entry_mode, .. }
132
0
                | Change::Deletion { entry_mode, .. }
133
0
                | Change::Modification { entry_mode, .. } => *entry_mode,
134
            }
135
0
        }
136
137
0
        fn id_and_entry_mode(&self) -> (&oid, EntryMode) {
138
0
            match self {
139
0
                Change::Addition { entry_mode, oid, .. }
140
0
                | Change::Deletion { entry_mode, oid, .. }
141
0
                | Change::Modification { entry_mode, oid, .. } => (oid, *entry_mode),
142
            }
143
0
        }
144
    }
145
}
146
147
#[cfg(test)]
148
mod tests {
149
    use super::*;
150
151
    #[test]
152
    fn size_of_change() {
153
        let actual = std::mem::size_of::<Change>();
154
        let sha1 = 48;
155
        let sha256_extra = 24;
156
        let ceiling = sha1 + sha256_extra;
157
        assert!(
158
            actual <= ceiling,
159
            "{actual} <= {ceiling}: this type shouldn't grow without us knowing"
160
        );
161
    }
162
}