Skip to content

Commit b291de0

Browse files
committed
feat: allow threaded-handling of tree-diff changes.
This works by providing `Change::detach()` and `ChangeDetached::attach()`.
1 parent d986b2b commit b291de0

File tree

2 files changed

+200
-2
lines changed

2 files changed

+200
-2
lines changed

gix/src/object/tree/diff/change.rs

+186-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::{bstr::BStr, diff::blob::DiffLineStats, Id};
1+
use crate::bstr::BString;
2+
use crate::ext::ObjectIdExt;
3+
use crate::object::tree::diff::ChangeDetached;
4+
use crate::{bstr::BStr, diff::blob::DiffLineStats, Id, Repository};
25

36
/// An event emitted when finding differences between two trees.
47
#[derive(Debug, Clone, Copy)]
@@ -67,6 +70,99 @@ pub enum Event<'a, 'old, 'new> {
6770
},
6871
}
6972

73+
/// An event emitted when finding differences between two trees.
74+
#[derive(Debug, Clone)]
75+
pub enum EventDetached {
76+
/// An entry was added, like the addition of a file or directory.
77+
Addition {
78+
/// The mode of the added entry.
79+
entry_mode: gix_object::tree::EntryMode,
80+
/// The object id of the added entry.
81+
id: gix_hash::ObjectId,
82+
},
83+
/// An entry was deleted, like the deletion of a file or directory.
84+
Deletion {
85+
/// The mode of the deleted entry.
86+
entry_mode: gix_object::tree::EntryMode,
87+
/// The object id of the deleted entry.
88+
id: gix_hash::ObjectId,
89+
},
90+
/// An entry was modified, e.g. changing the contents of a file adjusts its object id and turning
91+
/// a file into a symbolic link adjusts its mode.
92+
Modification {
93+
/// The mode of the entry before the modification.
94+
previous_entry_mode: gix_object::tree::EntryMode,
95+
/// The object id of the entry before the modification.
96+
previous_id: gix_hash::ObjectId,
97+
98+
/// The mode of the entry after the modification.
99+
entry_mode: gix_object::tree::EntryMode,
100+
/// The object id after the modification.
101+
id: gix_hash::ObjectId,
102+
},
103+
/// Entries are considered rewritten if they are not trees and they, according to some understanding of identity, were renamed
104+
/// or copied.
105+
/// In case of renames, this means they originally appeared as [`Deletion`][Event::Deletion] signalling their source as well as an
106+
/// [`Addition`][Event::Addition] acting as destination.
107+
///
108+
/// In case of copies, the `copy` flag is true and typically represents a perfect copy of a source was made.
109+
///
110+
/// This variant can only be encountered if [rewrite tracking][super::Platform::track_rewrites()] is enabled.
111+
///
112+
/// Note that mode changes may have occurred as well, i.e. changes from executable to non-executable or vice-versa.
113+
Rewrite {
114+
/// The location of the source of the rename operation.
115+
///
116+
/// It may be empty if neither [file names][super::Platform::track_filename()] nor [file paths][super::Platform::track_path()]
117+
/// are tracked.
118+
source_location: BString,
119+
/// The mode of the entry before the rename.
120+
source_entry_mode: gix_object::tree::EntryMode,
121+
/// The object id of the entry before the rename.
122+
///
123+
/// Note that this is the same as `id` if we require the [similarity to be 100%][super::Rewrites::percentage], but may
124+
/// be different otherwise.
125+
source_id: gix_hash::ObjectId,
126+
/// Information about the diff we performed to detect similarity and match the `source_id` with the current state at `id`.
127+
/// It's `None` if `source_id` is equal to `id`, as identity made an actual diff computation unnecessary.
128+
diff: Option<DiffLineStats>,
129+
/// The mode of the entry after the rename.
130+
/// It could differ but still be considered a rename as we are concerned only about content.
131+
entry_mode: gix_object::tree::EntryMode,
132+
/// The object id after the rename.
133+
id: gix_hash::ObjectId,
134+
/// If true, this rewrite is created by copy, and `source_id` is pointing to its source. Otherwise, it's a rename, and `source_id`
135+
/// points to a deleted object, as renames are tracked as deletions and additions of the same or similar content.
136+
copy: bool,
137+
},
138+
}
139+
140+
/// Lifecycle
141+
impl super::Change<'_, '_, '_> {
142+
/// Detach the repository instance to obtain a fully-owned version
143+
pub fn detach(self) -> ChangeDetached {
144+
ChangeDetached {
145+
location: self.location.to_owned(),
146+
event: self.event.detach(),
147+
}
148+
}
149+
}
150+
151+
/// Lifecycle
152+
impl ChangeDetached {
153+
/// Return an attached version of this instance that uses `old_repo` for previous values and `new_repo` for current values.
154+
pub fn attach<'old, 'new>(
155+
&self,
156+
old_repo: &'old Repository,
157+
new_repo: &'new Repository,
158+
) -> super::Change<'_, 'old, 'new> {
159+
super::Change {
160+
location: self.location.as_ref(),
161+
event: self.event.attach(old_repo, new_repo),
162+
}
163+
}
164+
}
165+
70166
impl<'a, 'old, 'new> super::Change<'a, 'old, 'new> {
71167
/// Produce a platform for performing a line-diff no matter whether the underlying [Event] is an addition, modification,
72168
/// deletion or rewrite.
@@ -86,6 +182,95 @@ impl<'a, 'old, 'new> super::Change<'a, 'old, 'new> {
86182
}
87183
}
88184

185+
/// Lifecycle
186+
impl Event<'_, '_, '_> {
187+
/// Detach the repository instance to obtain a fully-owned version
188+
pub fn detach(self) -> EventDetached {
189+
match self {
190+
Event::Addition { entry_mode, id } => EventDetached::Addition {
191+
entry_mode,
192+
id: id.detach(),
193+
},
194+
Event::Deletion { entry_mode, id } => EventDetached::Deletion {
195+
entry_mode,
196+
id: id.detach(),
197+
},
198+
Event::Modification {
199+
previous_entry_mode,
200+
previous_id,
201+
entry_mode,
202+
id,
203+
} => EventDetached::Modification {
204+
previous_entry_mode,
205+
previous_id: previous_id.detach(),
206+
entry_mode,
207+
id: id.detach(),
208+
},
209+
Event::Rewrite {
210+
source_location,
211+
source_entry_mode,
212+
source_id,
213+
diff,
214+
entry_mode,
215+
id,
216+
copy,
217+
} => EventDetached::Rewrite {
218+
source_location: source_location.to_owned(),
219+
source_entry_mode,
220+
source_id: source_id.detach(),
221+
diff,
222+
entry_mode,
223+
id: id.detach(),
224+
copy,
225+
},
226+
}
227+
}
228+
}
229+
230+
impl EventDetached {
231+
/// Return an attached version of this instance that uses `old_repo` for previous values and `new_repo` for current values.
232+
pub fn attach<'old, 'new>(&self, old_repo: &'old Repository, new_repo: &'new Repository) -> Event<'_, 'old, 'new> {
233+
match self {
234+
EventDetached::Addition { entry_mode, id } => Event::Addition {
235+
entry_mode: *entry_mode,
236+
id: id.attach(new_repo),
237+
},
238+
EventDetached::Deletion { entry_mode, id } => Event::Deletion {
239+
entry_mode: *entry_mode,
240+
id: id.attach(old_repo),
241+
},
242+
EventDetached::Modification {
243+
previous_entry_mode,
244+
previous_id,
245+
entry_mode,
246+
id,
247+
} => Event::Modification {
248+
previous_entry_mode: *previous_entry_mode,
249+
previous_id: previous_id.attach(old_repo),
250+
entry_mode: *entry_mode,
251+
id: id.attach(new_repo),
252+
},
253+
EventDetached::Rewrite {
254+
source_location,
255+
source_entry_mode,
256+
source_id,
257+
diff,
258+
entry_mode,
259+
id,
260+
copy,
261+
} => Event::Rewrite {
262+
source_location: source_location.as_ref(),
263+
source_entry_mode: *source_entry_mode,
264+
source_id: source_id.attach(old_repo),
265+
diff: *diff,
266+
entry_mode: *entry_mode,
267+
id: id.attach(new_repo),
268+
copy: *copy,
269+
},
270+
}
271+
}
272+
}
273+
89274
impl<'a, 'old, 'new> Event<'a, 'old, 'new> {
90275
/// Return the current mode of this instance.
91276
pub fn entry_mode(&self) -> gix_object::tree::EntryMode {

gix/src/object/tree/diff/mod.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use gix_diff::tree::recorder::Location;
22

3+
use crate::bstr::BString;
34
use crate::{bstr::BStr, diff::Rewrites, Tree};
45

56
/// Returned by the `for_each` function to control flow.
@@ -17,12 +18,24 @@ pub enum Action {
1718
pub struct Change<'a, 'old, 'new> {
1819
/// The location of the file or directory described by `event`, if tracking was enabled.
1920
///
20-
/// Otherwise this value is always an empty path.
21+
/// Otherwise, this value is always an empty path.
2122
pub location: &'a BStr,
2223
/// The diff event itself to provide information about what would need to change.
2324
pub event: change::Event<'a, 'old, 'new>,
2425
}
2526

27+
/// Represents any possible change in order to turn one tree into another, the fully owned version
28+
/// of [`Change`].
29+
#[derive(Debug, Clone)]
30+
pub struct ChangeDetached {
31+
/// The location of the file or directory described by `event`, if tracking was enabled.
32+
///
33+
/// Otherwise, this value is always an empty path.
34+
pub location: BString,
35+
/// The diff event itself to provide information about what would need to change.
36+
pub event: change::EventDetached,
37+
}
38+
2639
///
2740
#[allow(clippy::empty_docs)]
2841
pub mod change;

0 commit comments

Comments
 (0)