gix_diff/tree_with_rewrites/
function.rs1use bstr::BStr;
2use gix_error::ExnResult;
3use gix_error::ResultExt;
4use gix_object::TreeRefIter;
5
6use super::{Action, ChangeRef, Options};
7use crate::{rewrites, rewrites::tracker, tree::Error};
8
9pub fn diff(
27 lhs: TreeRefIter<'_>,
28 rhs: TreeRefIter<'_>,
29 resource_cache: &mut crate::blob::Platform,
30 tree_diff_state: &mut crate::tree::State,
31 objects: &impl gix_object::FindObjectOrHeader,
32 for_each: impl FnMut(ChangeRef<'_>) -> ExnResult<Action>,
33 options: Options,
34) -> Result<Option<rewrites::Outcome>, Error> {
35 fn callback_error(err: gix_error::Exn) -> Error {
36 err.raise(gix_error::message("The user-provided callback failed"))
37 .into()
38 }
39
40 let mut delegate = Delegate {
41 src_tree: lhs,
42 recorder: crate::tree::Recorder::default().track_location(options.location),
43 visit: for_each,
44 location: options.location,
45 objects,
46 tracked: options.rewrites.map(rewrites::Tracker::new),
47 err: None,
48 };
49 match crate::tree(lhs, rhs, tree_diff_state, objects, &mut delegate) {
50 Ok(()) => {
51 let outcome = delegate.process_tracked_changes(resource_cache)?;
52 match delegate.err {
53 Some(err) => Err(callback_error(err)),
54 None => Ok(outcome),
55 }
56 }
57 Err(crate::tree::Error::Cancelled) => delegate
58 .err
59 .map_or(Err(Error::Cancelled), |err| Err(callback_error(err))),
60 Err(err) => Err(err),
61 }
62}
63
64struct Delegate<'a, 'old, VisitFn, Objects> {
65 src_tree: TreeRefIter<'old>,
66 recorder: crate::tree::Recorder,
67 objects: &'a Objects,
68 visit: VisitFn,
69 tracked: Option<rewrites::Tracker<crate::tree::visit::Change>>,
70 location: Option<crate::tree::recorder::Location>,
71 err: Option<gix_error::Exn>,
72}
73
74impl<VisitFn, Objects> Delegate<'_, '_, VisitFn, Objects>
75where
76 Objects: gix_object::FindObjectOrHeader,
77 VisitFn: FnMut(ChangeRef<'_>) -> ExnResult<Action>,
78{
79 fn emit_change(
81 change: crate::tree::visit::Change,
82 location: &BStr,
83 visit: &mut VisitFn,
84 stored_err: &mut Option<gix_error::Exn>,
85 ) -> crate::tree::visit::Action {
86 use crate::tree::visit::Change::*;
87 let change = match change {
88 Addition {
89 entry_mode,
90 oid,
91 relation,
92 } => ChangeRef::Addition {
93 location,
94 relation,
95 entry_mode,
96 id: oid,
97 },
98 Deletion {
99 entry_mode,
100 oid,
101 relation,
102 } => ChangeRef::Deletion {
103 entry_mode,
104 location,
105 relation,
106 id: oid,
107 },
108 Modification {
109 previous_entry_mode,
110 previous_oid,
111 entry_mode,
112 oid,
113 } => ChangeRef::Modification {
114 location,
115 previous_entry_mode,
116 entry_mode,
117 previous_id: previous_oid,
118 id: oid,
119 },
120 };
121 match visit(change) {
122 Ok(std::ops::ControlFlow::Break(())) => std::ops::ControlFlow::Break(()),
123 Ok(std::ops::ControlFlow::Continue(())) => std::ops::ControlFlow::Continue(()),
124 Err(err) => {
125 *stored_err = Some(err);
126 std::ops::ControlFlow::Break(())
127 }
128 }
129 }
130
131 fn process_tracked_changes(
132 &mut self,
133 diff_cache: &mut crate::blob::Platform,
134 ) -> Result<Option<rewrites::Outcome>, Error> {
135 use crate::rewrites::tracker::Change as _;
136 let tracked = match self.tracked.as_mut() {
137 Some(t) => t,
138 None => return Ok(None),
139 };
140
141 let outcome = tracked
142 .emit(
143 |dest, source| match source {
144 Some(source) => {
145 let (oid, mode) = dest.change.oid_and_entry_mode();
146 let change = ChangeRef::Rewrite {
147 source_location: source.location,
148 source_entry_mode: source.entry_mode,
149 source_id: source.id,
150 source_relation: source.change.relation(),
151 entry_mode: mode,
152 id: oid.to_owned(),
153 relation: dest.change.relation(),
154 diff: source.diff,
155 location: dest.location,
156 copy: match source.kind {
157 tracker::visit::SourceKind::Rename => false,
158 tracker::visit::SourceKind::Copy => true,
159 },
160 };
161 match (self.visit)(change) {
162 Ok(std::ops::ControlFlow::Break(())) => std::ops::ControlFlow::Break(()),
163 Ok(std::ops::ControlFlow::Continue(())) => std::ops::ControlFlow::Continue(()),
164 Err(err) => {
165 self.err = Some(err);
166 std::ops::ControlFlow::Break(())
167 }
168 }
169 }
170 None => Self::emit_change(dest.change, dest.location, &mut self.visit, &mut self.err),
171 },
172 diff_cache,
173 self.objects,
174 |push| {
175 let mut delegate = tree_to_changes::Delegate::new(push, self.location);
176 let state = gix_traverse::tree::breadthfirst::State::default();
177 gix_traverse::tree::breadthfirst(self.src_tree, state, self.objects, &mut delegate)
178 .map_err(gix_error::Exn::into_error)
179 },
180 )
181 .or_raise(|| gix_error::message("Failure during rename tracking"))?;
182 Ok(Some(outcome))
183 }
184}
185
186impl<VisitFn, Objects> crate::tree::Visit for Delegate<'_, '_, VisitFn, Objects>
187where
188 Objects: gix_object::FindObjectOrHeader,
189 VisitFn: FnMut(ChangeRef<'_>) -> ExnResult<Action>,
190{
191 fn pop_front_tracked_path_and_set_current(&mut self) {
192 self.recorder.pop_front_tracked_path_and_set_current();
193 }
194
195 fn push_back_tracked_path_component(&mut self, component: &BStr) {
196 self.recorder.push_back_tracked_path_component(component);
197 }
198
199 fn push_path_component(&mut self, component: &BStr) {
200 self.recorder.push_path_component(component);
201 }
202
203 fn pop_path_component(&mut self) {
204 self.recorder.pop_path_component();
205 }
206
207 fn visit(&mut self, change: crate::tree::visit::Change) -> crate::tree::visit::Action {
208 match self.tracked.as_mut() {
209 Some(tracked) => tracked
210 .try_push_change(change, self.recorder.path())
211 .map_or(std::ops::ControlFlow::Continue(()), |change| {
212 Self::emit_change(change, self.recorder.path(), &mut self.visit, &mut self.err)
213 }),
214 None => Self::emit_change(change, self.recorder.path(), &mut self.visit, &mut self.err),
215 }
216 }
217}
218
219mod tree_to_changes {
220 use bstr::BStr;
221 use gix_object::tree::EntryRef;
222
223 use crate::tree::visit::Change;
224
225 pub struct Delegate<'a> {
226 push: &'a mut dyn FnMut(Change, &BStr),
227 recorder: gix_traverse::tree::Recorder,
228 }
229
230 impl<'a> Delegate<'a> {
231 pub fn new(push: &'a mut dyn FnMut(Change, &BStr), location: Option<crate::tree::recorder::Location>) -> Self {
232 let location = location.map(|t| match t {
233 crate::tree::recorder::Location::FileName => gix_traverse::tree::recorder::Location::FileName,
234 crate::tree::recorder::Location::Path => gix_traverse::tree::recorder::Location::Path,
235 });
236 Self {
237 push,
238 recorder: gix_traverse::tree::Recorder::default().track_location(location),
239 }
240 }
241 }
242
243 impl gix_traverse::tree::Visit for Delegate<'_> {
244 fn pop_back_tracked_path_and_set_current(&mut self) {
245 self.recorder.pop_back_tracked_path_and_set_current();
246 }
247
248 fn pop_front_tracked_path_and_set_current(&mut self) {
249 self.recorder.pop_front_tracked_path_and_set_current();
250 }
251
252 fn push_back_tracked_path_component(&mut self, component: &BStr) {
253 self.recorder.push_back_tracked_path_component(component);
254 }
255
256 fn push_path_component(&mut self, component: &BStr) {
257 self.recorder.push_path_component(component);
258 }
259
260 fn pop_path_component(&mut self) {
261 self.recorder.pop_path_component();
262 }
263
264 fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
265 std::ops::ControlFlow::Continue(true)
266 }
267
268 fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
269 if entry.mode.is_blob() {
270 (self.push)(
271 Change::Modification {
272 previous_entry_mode: entry.mode,
273 previous_oid: gix_hash::ObjectId::null(entry.oid.kind()),
274 entry_mode: entry.mode,
275 oid: entry.oid.to_owned(),
276 },
277 self.recorder.path(),
278 );
279 }
280 std::ops::ControlFlow::Continue(true)
281 }
282 }
283}