1- use super :: { process_changes, Change , UnblamedHunk } ;
1+ use super :: { process_changes, update_blame_with_changes, Change , UnblamedHunk } ;
2+ use crate :: types:: BlameCacheObject ;
23use crate :: { BlameEntry , Error , Outcome , Statistics } ;
34use gix_diff:: blob:: intern:: TokenSource ;
45use gix_diff:: tree:: Visit ;
56use gix_hash:: ObjectId ;
67use gix_object:: {
7- bstr:: { BStr , BString } ,
8+ bstr:: { BStr , BString , ByteSlice } ,
89 FindExt ,
910} ;
1011use gix_traverse:: commit:: find as find_commit;
@@ -66,6 +67,7 @@ pub fn file(
6667 suspect : ObjectId ,
6768 cache : Option < gix_commitgraph:: Graph > ,
6869 resource_cache : & mut gix_diff:: blob:: Platform ,
70+ blame_cache : Option < BlameCacheObject > ,
6971 file_path : & BStr ,
7072 range : Option < Range < u32 > > ,
7173) -> Result < Outcome , Error > {
@@ -87,17 +89,56 @@ pub fn file(
8789 }
8890
8991 let range_in_blamed_file = one_based_inclusive_to_zero_based_exclusive_range ( range, num_lines_in_blamed) ?;
90- let mut hunks_to_blame = vec ! [ UnblamedHunk {
91- range_in_blamed_file: range_in_blamed_file. clone( ) ,
92- suspects: [ ( suspect, range_in_blamed_file) ] . into( ) ,
93- } ] ;
92+
93+ let ( blame_entries, mut hunks_to_blame) = match blame_cache {
94+ Some ( blame_cache) => {
95+ // If there is a cache, we first get the diff between the current commit and the commit
96+ // we passed as the cache.
97+ let old_file_id = file_id ( & blame_cache. cache_id , & mut buf, & mut buf2) ?;
98+ let changes = blob_changes (
99+ & odb,
100+ resource_cache,
101+ blamed_file_entry_id,
102+ old_file_id,
103+ file_path. as_bstr ( ) ,
104+ & mut stats,
105+ ) ?;
106+
107+ // If there are no changes, we can return the cache as is immediately.
108+ if changes. iter ( ) . all ( |change| matches ! ( change, Change :: Unchanged ( _) ) ) {
109+ return Ok ( Outcome {
110+ entries : blame_cache. entries . clone ( ) ,
111+ blob : blamed_file_blob,
112+ statistics : stats,
113+ } ) ;
114+ }
115+ // Otherwise, we update the cache with the new changes.
116+ let ( blame_entries, hunks_to_blame) = update_blame_with_changes ( blame_cache. entries , changes, suspect) ;
117+ // If there are no more hunks to blame, we can return the result immediately.
118+ if hunks_to_blame. is_empty ( ) {
119+ return Ok ( Outcome {
120+ entries : blame_entries,
121+ blob : blamed_file_blob,
122+ statistics : stats,
123+ } ) ;
124+ }
125+ ( blame_entries, hunks_to_blame)
126+ }
127+ None => {
128+ let hunks_to_blame = vec ! [ UnblamedHunk {
129+ range_in_blamed_file: range_in_blamed_file. clone( ) ,
130+ suspects: [ ( suspect, range_in_blamed_file) ] . into( ) ,
131+ } ] ;
132+ ( Vec :: new ( ) , hunks_to_blame)
133+ }
134+ } ;
94135
95136 let ( mut buf, mut buf2) = ( Vec :: new ( ) , Vec :: new ( ) ) ;
96137 let commit = find_commit ( cache. as_ref ( ) , & odb, & suspect, & mut buf) ?;
97138 let mut queue: gix_revwalk:: PriorityQueue < CommitTime , ObjectId > = gix_revwalk:: PriorityQueue :: new ( ) ;
98139 queue. insert ( commit_time ( commit) ?, suspect) ;
99140
100- let mut out = Vec :: new ( ) ;
141+ let mut out = blame_entries ;
101142 let mut diff_state = gix_diff:: tree:: State :: default ( ) ;
102143 let mut previous_entry: Option < ( ObjectId , ObjectId ) > = None ;
103144 ' outer: while let Some ( suspect) = queue. pop_value ( ) {
0 commit comments