@@ -16,10 +16,10 @@ pub(super) mod function;
16
16
///
17
17
/// This is the core of the blame implementation as it matches regions in *Source File* to the *Blamed File*.
18
18
fn process_change(
19
- out: &mut Vec<BlameEntry>,
20
19
new_hunks_to_blame: &mut Vec<UnblamedHunk>,
21
20
offset: &mut Offset,
22
21
suspect: ObjectId,
22
+ parent: ObjectId,
23
23
hunk: Option<UnblamedHunk>,
24
24
change: Option<Change>,
25
25
) -> (Option<UnblamedHunk>, Option<Change>) {
@@ -40,6 +40,8 @@ fn process_change(
40
40
match (hunk, change) {
41
41
(Some(hunk), Some(Change::Unchanged(unchanged))) => {
42
42
let Some(range_in_suspect) = hunk.suspects.get(&suspect) else {
43
+ // We don’t clone blame to `parent` as `suspect` has nothing to do with this
44
+ // `hunk`.
43
45
new_hunks_to_blame.push(hunk);
44
46
return (None, Some(Change::Unchanged(unchanged)));
45
47
};
@@ -64,7 +66,7 @@ fn process_change(
64
66
65
67
// Nothing to do with `hunk` except shifting it,
66
68
// but `unchanged` needs to be checked against the next hunk to catch up.
67
- new_hunks_to_blame.push(hunk.shift_by (suspect, *offset));
69
+ new_hunks_to_blame.push(hunk.clone_blame (suspect, parent).shift_by(parent , *offset));
68
70
(None, Some(Change::Unchanged(unchanged)))
69
71
}
70
72
(false, false) => {
@@ -93,7 +95,7 @@ fn process_change(
93
95
94
96
// Nothing to do with `hunk` except shifting it,
95
97
// but `unchanged` needs to be checked against the next hunk to catch up.
96
- new_hunks_to_blame.push(hunk.shift_by (suspect, *offset));
98
+ new_hunks_to_blame.push(hunk.clone_blame (suspect, parent).shift_by(parent , *offset));
97
99
(None, Some(Change::Unchanged(unchanged)))
98
100
}
99
101
}
@@ -123,7 +125,7 @@ fn process_change(
123
125
}
124
126
Either::Right((before, after)) => {
125
127
// requeue the left side `before` after offsetting it…
126
- new_hunks_to_blame.push(before.shift_by (suspect, *offset));
128
+ new_hunks_to_blame.push(before.clone_blame (suspect, parent).shift_by(parent , *offset));
127
129
// …and treat `after` as `new_hunk`, which contains the `added` range.
128
130
after
129
131
}
@@ -132,20 +134,18 @@ fn process_change(
132
134
*offset += added.end - added.start;
133
135
*offset -= number_of_lines_deleted;
134
136
135
- // The overlapping `added` section was successfully located.
136
- out.push(BlameEntry::with_offset(
137
- added.clone(),
138
- suspect,
139
- hunk_starting_at_added.offset_for(suspect),
140
- ));
141
-
137
+ // The overlapping `added` section was successfully located.
142
138
// Re-split at the end of `added` to continue with what's after.
143
139
match hunk_starting_at_added.split_at(suspect, added.end) {
144
- Either::Left(_) => {
140
+ Either::Left(hunk) => {
141
+ new_hunks_to_blame.push(hunk);
142
+
145
143
// Nothing to split, so we are done with this hunk.
146
144
(None, None)
147
145
}
148
- Either::Right((_, after)) => {
146
+ Either::Right((hunk, after)) => {
147
+ new_hunks_to_blame.push(hunk);
148
+
149
149
// Keep processing the unblamed range after `added`
150
150
(Some(after), None)
151
151
}
@@ -162,17 +162,13 @@ fn process_change(
162
162
Either::Left(hunk) => hunk,
163
163
Either::Right((before, after)) => {
164
164
// Keep looking for the left side of the unblamed portion.
165
- new_hunks_to_blame.push(before.shift_by (suspect, *offset));
165
+ new_hunks_to_blame.push(before.clone_blame (suspect, parent).shift_by(parent , *offset));
166
166
after
167
167
}
168
168
};
169
169
170
170
// We can 'blame' the overlapping area of `added` and `hunk`.
171
- out.push(BlameEntry::with_offset(
172
- added.start..range_in_suspect.end,
173
- suspect,
174
- hunk_starting_at_added.offset_for(suspect),
175
- ));
171
+ new_hunks_to_blame.push(hunk_starting_at_added);
176
172
// Keep processing `added`, it's portion past `hunk` may still contribute.
177
173
(None, Some(Change::AddedOrReplaced(added, number_of_lines_deleted)))
178
174
}
@@ -183,18 +179,20 @@ fn process_change(
183
179
// <---> (blamed)
184
180
// <--> (new hunk)
185
181
186
- out.push(BlameEntry::with_offset(
187
- range_in_suspect.start..added.end,
188
- suspect,
189
- hunk.offset_for(suspect),
190
- ));
191
-
192
182
*offset += added.end - added.start;
193
183
*offset -= number_of_lines_deleted;
194
184
195
185
match hunk.split_at(suspect, added.end) {
196
- Either::Left(_) => (None, None),
197
- Either::Right((_, after)) => (Some(after), None),
186
+ Either::Left(hunk) => {
187
+ new_hunks_to_blame.push(hunk);
188
+
189
+ (None, None)
190
+ }
191
+ Either::Right((before, after)) => {
192
+ new_hunks_to_blame.push(before);
193
+
194
+ (Some(after), None)
195
+ }
198
196
}
199
197
}
200
198
(false, false) => {
@@ -222,7 +220,7 @@ fn process_change(
222
220
// <----> (added)
223
221
224
222
// Retry `hunk` once there is overlapping changes to process.
225
- new_hunks_to_blame.push(hunk.shift_by (suspect, *offset));
223
+ new_hunks_to_blame.push(hunk.clone_blame (suspect, parent).shift_by(parent , *offset));
226
224
227
225
// Let hunks catchup with this change.
228
226
(
@@ -237,11 +235,7 @@ fn process_change(
237
235
// <---> (blamed)
238
236
239
237
// Successfully blame the whole range.
240
- out.push(BlameEntry::with_offset(
241
- range_in_suspect.clone(),
242
- suspect,
243
- hunk.offset_for(suspect),
244
- ));
238
+ new_hunks_to_blame.push(hunk);
245
239
246
240
// And keep processing `added` with future `hunks` that might be affected by it.
247
241
(
@@ -279,7 +273,7 @@ fn process_change(
279
273
}
280
274
Either::Right((before, after)) => {
281
275
// `before` isn't affected by deletion, so keep it for later.
282
- new_hunks_to_blame.push(before.shift_by (suspect, *offset));
276
+ new_hunks_to_blame.push(before.clone_blame (suspect, parent).shift_by(parent , *offset));
283
277
// after will be affected by offset, and we will see if there are more changes affecting it.
284
278
after
285
279
}
@@ -291,7 +285,8 @@ fn process_change(
291
285
// | (line_number_in_destination)
292
286
293
287
// Catchup with changes.
294
- new_hunks_to_blame.push(hunk.shift_by(suspect, *offset));
288
+ new_hunks_to_blame.push(hunk.clone_blame(suspect, parent).shift_by(parent, *offset));
289
+
295
290
(
296
291
None,
297
292
Some(Change::Deleted(line_number_in_destination, number_of_lines_deleted)),
@@ -300,7 +295,7 @@ fn process_change(
300
295
}
301
296
(Some(hunk), None) => {
302
297
// nothing to do - changes are exhausted, re-evaluate `hunk`.
303
- new_hunks_to_blame.push(hunk.shift_by (suspect, *offset));
298
+ new_hunks_to_blame.push(hunk.clone_blame (suspect, parent).shift_by(parent , *offset));
304
299
(None, None)
305
300
}
306
301
(None, Some(Change::Unchanged(_))) => {
@@ -328,10 +323,10 @@ fn process_change(
328
323
/// Consume `hunks_to_blame` and `changes` to pair up matches ranges (also overlapping) with each other.
329
324
/// Once a match is found, it's pushed onto `out`.
330
325
fn process_changes(
331
- out: &mut Vec<BlameEntry>,
332
326
hunks_to_blame: Vec<UnblamedHunk>,
333
327
changes: Vec<Change>,
334
328
suspect: ObjectId,
329
+ parent: ObjectId,
335
330
) -> Vec<UnblamedHunk> {
336
331
let mut hunks_iter = hunks_to_blame.into_iter();
337
332
let mut changes_iter = changes.into_iter();
@@ -344,10 +339,10 @@ fn process_changes(
344
339
345
340
loop {
346
341
(hunk, change) = process_change(
347
- out,
348
342
&mut new_hunks_to_blame,
349
343
&mut offset_in_destination,
350
344
suspect,
345
+ parent,
351
346
hunk,
352
347
change,
353
348
);
@@ -407,30 +402,20 @@ impl UnblamedHunk {
407
402
}
408
403
}
409
404
410
- fn offset_for(&self, suspect: ObjectId) -> Offset {
411
- let range_in_suspect = self
412
- .suspects
413
- .get(&suspect)
414
- .expect("Internal and we know suspect is present");
415
-
416
- if self.range_in_blamed_file.start > range_in_suspect.start {
417
- Offset::Added(self.range_in_blamed_file.start - range_in_suspect.start)
418
- } else {
419
- Offset::Deleted(range_in_suspect.start - self.range_in_blamed_file.start)
420
- }
421
- }
422
-
423
405
/// Transfer all ranges from the commit at `from` to the commit at `to`.
424
406
fn pass_blame(&mut self, from: ObjectId, to: ObjectId) {
425
407
if let Some(range_in_suspect) = self.suspects.remove(&from) {
426
408
self.suspects.insert(to, range_in_suspect);
427
409
}
428
410
}
429
411
430
- fn clone_blame(&mut self, from: ObjectId, to: ObjectId) {
412
+ // TODO
413
+ // Should this also accept `&mut self` as the other functions do?
414
+ fn clone_blame(mut self, from: ObjectId, to: ObjectId) -> Self {
431
415
if let Some(range_in_suspect) = self.suspects.get(&from) {
432
416
self.suspects.insert(to, range_in_suspect.clone());
433
417
}
418
+ self
434
419
}
435
420
436
421
fn remove_blame(&mut self, suspect: ObjectId) {
@@ -439,36 +424,6 @@ impl UnblamedHunk {
439
424
}
440
425
441
426
impl BlameEntry {
442
- /// Create a new instance by creating `range_in_blamed_file` after applying `offset` to `range_in_source_file`.
443
- fn with_offset(range_in_source_file: Range<u32>, commit_id: ObjectId, offset: Offset) -> Self {
444
- debug_assert!(
445
- range_in_source_file.end > range_in_source_file.start,
446
- "{range_in_source_file:?}"
447
- );
448
-
449
- match offset {
450
- Offset::Added(added) => Self {
451
- start_in_blamed_file: range_in_source_file.start + added,
452
- start_in_source_file: range_in_source_file.start,
453
- len: force_non_zero(range_in_source_file.len() as u32),
454
- commit_id,
455
- },
456
- Offset::Deleted(deleted) => {
457
- debug_assert!(
458
- range_in_source_file.start >= deleted,
459
- "{range_in_source_file:?} {offset:?}"
460
- );
461
-
462
- Self {
463
- start_in_blamed_file: range_in_source_file.start - deleted,
464
- start_in_source_file: range_in_source_file.start,
465
- len: force_non_zero(range_in_source_file.len() as u32),
466
- commit_id,
467
- }
468
- }
469
- }
470
- }
471
-
472
427
/// Create an offset from a portion of the *Blamed File*.
473
428
fn from_unblamed_hunk(unblamed_hunk: &UnblamedHunk, commit_id: ObjectId) -> Option<Self> {
474
429
let range_in_source_file = unblamed_hunk.suspects.get(&commit_id)?;
0 commit comments