@@ -5,7 +5,7 @@ use std::ops::Range;
5
5
6
6
use gix_hash:: ObjectId ;
7
7
8
- use crate :: types:: { BlameEntry , Either , LineRange } ;
8
+ use crate :: types:: { BlameEntry , BlameLines , ChangeLines , Either , LineRange } ;
9
9
use crate :: types:: { Change , Offset , UnblamedHunk } ;
10
10
11
11
pub ( super ) mod function;
@@ -357,6 +357,147 @@ fn process_changes(
357
357
new_hunks_to_blame
358
358
}
359
359
360
+ /// Consume `cached_blames` and `changes`. With the changes we update the cached blames.
361
+ /// This function returns the updated blames and the new hunks to blame.
362
+ fn update_blame_with_changes (
363
+ cached_blames : Vec < BlameEntry > ,
364
+ changes : Vec < Change > ,
365
+ head_id : ObjectId ,
366
+ ) -> ( Vec < BlameEntry > , Vec < UnblamedHunk > ) {
367
+ fn blame_fully_contained_by_change (
368
+ blame_lines : & BlameLines ,
369
+ blame : & BlameEntry ,
370
+ change_lines : & ChangeLines ,
371
+ change : & Change ,
372
+ ) -> bool {
373
+ blame_lines. get_remaining ( blame) < change_lines. get_remaining ( change)
374
+ }
375
+
376
+ let mut updated_blames = Vec :: new ( ) ;
377
+ let mut new_hunks_to_blame = Vec :: new ( ) ;
378
+
379
+ let mut blame_iter = cached_blames. into_iter ( ) . peekable ( ) ;
380
+
381
+ // This is a nested loop where we iterate over the changes and the blames.
382
+ // We keep track of the assigned lines in the change and the blame.
383
+ // For each of the three possible cases (Unchanged, Deleted, AddedOrReplaced) we have different
384
+ // rules for how to update the blame.
385
+ ' change: for change in changes {
386
+ let mut change_assigned = ChangeLines :: default ( ) ;
387
+ while let Some ( blame) = blame_iter. peek_mut ( ) {
388
+ let mut blame_assigned = BlameLines :: default ( ) ;
389
+
390
+ // For each of the three cases we have to check if the blame is fully contained by the change.
391
+ // If so we can update the blame with the remaining length of the blame.
392
+ // If not we have to update the blame with the remaining length of the change.
393
+ match change {
394
+ Change :: Unchanged ( ref range) => {
395
+ match blame_fully_contained_by_change ( & blame_assigned, blame, & change_assigned, & change) {
396
+ true => {
397
+ updated_blames. push ( BlameEntry {
398
+ start_in_blamed_file : range. start + change_assigned. assigned . get_assigned ( ) ,
399
+ start_in_source_file : blame. start_in_source_file ,
400
+ len : blame. len ,
401
+ commit_id : blame. commit_id ,
402
+ } ) ;
403
+
404
+ change_assigned. assigned . add_assigned ( blame. len . get ( ) ) ;
405
+ blame_assigned. assigned . add_assigned ( blame. len . get ( ) ) ;
406
+ }
407
+ false => {
408
+ updated_blames. push ( BlameEntry {
409
+ start_in_blamed_file : range. start + change_assigned. assigned . get_assigned ( ) ,
410
+ start_in_source_file : blame. start_in_source_file ,
411
+ len : NonZeroU32 :: new ( change_assigned. get_remaining ( & change) ) . unwrap ( ) ,
412
+ commit_id : blame. commit_id ,
413
+ } ) ;
414
+
415
+ blame_assigned
416
+ . assigned
417
+ . add_assigned ( change_assigned. get_remaining ( & change) ) ;
418
+ change_assigned
419
+ . assigned
420
+ . add_assigned ( change_assigned. get_remaining ( & change) ) ;
421
+ }
422
+ }
423
+ }
424
+ Change :: Deleted ( _start_deletion, _lines_deleted) => {
425
+ match blame_fully_contained_by_change ( & blame_assigned, blame, & change_assigned, & change) {
426
+ true => {
427
+ blame_assigned. assigned . add_assigned ( blame. len . get ( ) ) ;
428
+ change_assigned. assigned . add_assigned ( blame. len . get ( ) ) ;
429
+ }
430
+ false => {
431
+ blame_assigned
432
+ . assigned
433
+ . add_assigned ( change_assigned. get_remaining ( & change) ) ;
434
+ change_assigned
435
+ . assigned
436
+ . add_assigned ( change_assigned. get_remaining ( & change) ) ;
437
+ }
438
+ }
439
+ }
440
+ Change :: AddedOrReplaced ( ref range, lines_deleted) => {
441
+ let new_unblamed_hunk = |range : & Range < u32 > , head_id : ObjectId | UnblamedHunk {
442
+ range_in_blamed_file : range. clone ( ) ,
443
+ suspects : [ ( head_id, range. clone ( ) ) ] . into ( ) ,
444
+ } ;
445
+ match blame_fully_contained_by_change ( & blame_assigned, blame, & change_assigned, & change) {
446
+ true => {
447
+ if lines_deleted == 0 {
448
+ new_hunks_to_blame. push ( new_unblamed_hunk ( range, head_id) ) ;
449
+ }
450
+
451
+ change_assigned. assigned . add_assigned ( blame. len . get ( ) ) ;
452
+ blame_assigned. assigned . add_assigned ( blame. len . get ( ) ) ;
453
+ }
454
+ false => {
455
+ new_hunks_to_blame. push ( new_unblamed_hunk ( range, head_id) ) ;
456
+
457
+ blame_assigned
458
+ . assigned
459
+ . add_assigned ( change_assigned. get_remaining ( & change) ) ;
460
+ change_assigned
461
+ . assigned
462
+ . add_assigned ( change_assigned. get_remaining ( & change) ) ;
463
+ }
464
+ }
465
+ }
466
+ }
467
+
468
+ // Check if the blame or the change is fully assigned.
469
+ // If the blame is fully assigned we can continue with the next blame.
470
+ // If the change is fully assigned we can continue with the next change.
471
+ // Since we have a mutable reference to the blame we can update it and reset the assigned blame lines.
472
+ // If both are fully assigned we can continue with the next blame and change.
473
+ match (
474
+ blame_assigned. has_remaining ( blame) ,
475
+ change_assigned. has_remaining ( & change) ,
476
+ ) {
477
+ ( true , true ) => {
478
+ // Both have remaining
479
+ blame. update_blame ( & blame_assigned. assigned ) ;
480
+ }
481
+ ( true , false ) => {
482
+ // Change is fully assigned
483
+ blame. update_blame ( & blame_assigned. assigned ) ;
484
+ continue ' change;
485
+ }
486
+ ( false , true ) => {
487
+ // Blame is fully assigned
488
+ blame_iter. next ( ) ;
489
+ }
490
+ ( false , false ) => {
491
+ // Both are fully assigned
492
+ blame_iter. next ( ) ;
493
+ continue ' change;
494
+ }
495
+ } ;
496
+ }
497
+ }
498
+ ( updated_blames, new_hunks_to_blame)
499
+ }
500
+
360
501
impl UnblamedHunk {
361
502
fn shift_by ( mut self , suspect : ObjectId , offset : Offset ) -> Self {
362
503
self . suspects . entry ( suspect) . and_modify ( |e| * e = e. shift_by ( offset) ) ;
0 commit comments