@@ -5,7 +5,7 @@ use std::ops::Range;
55
66use gix_hash:: ObjectId ;
77
8- use crate :: types:: { BlameEntry , Either , LineRange } ;
8+ use crate :: types:: { BlameEntry , BlameLines , ChangeLines , Either , LineRange } ;
99use crate :: types:: { Change , Offset , UnblamedHunk } ;
1010
1111pub ( super ) mod function;
@@ -357,6 +357,147 @@ fn process_changes(
357357 new_hunks_to_blame
358358}
359359
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+
360501impl UnblamedHunk {
361502 fn shift_by ( mut self , suspect : ObjectId , offset : Offset ) -> Self {
362503 self . suspects . entry ( suspect) . and_modify ( |e| * e = e. shift_by ( offset) ) ;
0 commit comments