@@ -732,6 +732,47 @@ mod baseline {
732732 }
733733}
734734
735+ /// This function merges adjacent blame entries. It merges entries that are adjacent both in the
736+ /// blamed file as well as in the original file that introduced them. This follows `git`’s
737+ /// behaviour. `libgit2`, as of 2024-09-19, only checks whether two entries are adjacent in the
738+ /// blamed file which can result in different blames in certain edge cases. See [the commit][1]
739+ /// that introduced the extra check into `git` for context.
740+ ///
741+ /// [1]: https://github.com/git/git/commit/c2ebaa27d63bfb7c50cbbdaba90aee4efdd45d0a
742+ fn coalesce_blame_entries ( lines_blamed : Vec < BlameEntry > ) -> Vec < BlameEntry > {
743+ // TODO
744+ // It’s possible this could better be done on insertion into `lines_blamed`.
745+ lines_blamed. into_iter ( ) . fold ( vec ! [ ] , |mut acc, entry| {
746+ let previous_entry = acc. last ( ) ;
747+
748+ if let Some ( previous_entry) = previous_entry {
749+ if previous_entry. commit_id == entry. commit_id
750+ && previous_entry. range_in_blamed_file . end == entry. range_in_blamed_file . start
751+ // As of 2024-09-19, the check below only is in `git`, but not in `libgit2`.
752+ && previous_entry. range_in_original_file . end == entry. range_in_original_file . start
753+ {
754+ let coalesced_entry = BlameEntry {
755+ range_in_blamed_file : previous_entry. range_in_blamed_file . start ..entry. range_in_blamed_file . end ,
756+ range_in_original_file : previous_entry. range_in_original_file . start
757+ ..entry. range_in_original_file . end ,
758+ commit_id : previous_entry. commit_id ,
759+ } ;
760+
761+ acc. pop ( ) ;
762+ acc. push ( coalesced_entry) ;
763+ } else {
764+ acc. push ( entry) ;
765+ }
766+
767+ acc
768+ } else {
769+ acc. push ( entry) ;
770+
771+ acc
772+ }
773+ } )
774+ }
775+
735776fn blame_file ( worktree_path : PathBuf , file_path : & BStr ) -> Vec < BlameEntry > {
736777 // TODO
737778 // At a high level, what we want to do is the following:
@@ -944,7 +985,7 @@ fn blame_file(worktree_path: PathBuf, file_path: &BStr) -> Vec<BlameEntry> {
944985 // order on insertion.
945986 lines_blamed. sort_by ( |a, b| a. range_in_blamed_file . start . cmp ( & b. range_in_blamed_file . start ) ) ;
946987
947- lines_blamed
988+ coalesce_blame_entries ( lines_blamed)
948989}
949990
950991macro_rules! mktest {
@@ -979,6 +1020,7 @@ mktest!(added_lines_around, "added-lines-around", 3);
9791020mktest ! ( switched_lines, "switched-lines" , 4 ) ;
9801021mktest ! ( added_line_before_changed_line, "added-line-before-changed-line" , 3 ) ;
9811022mktest ! ( same_line_changed_twice, "same-line-changed-twice" , 2 ) ;
1023+ mktest ! ( coalesce_adjacent_hunks, "coalesce-adjacent-hunks" , 1 ) ;
9821024
9831025#[ test]
9841026fn process_change_works ( ) {
0 commit comments