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,17 +67,22 @@ 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 > {
7274 let _span = gix_trace:: coarse!( "gix_blame::file()" , ?file_path, ?suspect) ;
7375
7476 let mut stats = Statistics :: default ( ) ;
7577 let ( mut buf, mut buf2, mut buf3) = ( Vec :: new ( ) , Vec :: new ( ) , Vec :: new ( ) ) ;
76- let mut file_id = |commit, buf : & mut Vec < u8 > , buf2 : & mut Vec < u8 > | find_path_entry_in_commit ( & odb, commit, file_path, cache. as_ref ( ) , buf, buf2, & mut stats) ?. ok_or_else ( || Error :: FileMissing {
77- file_path : file_path. to_owned ( ) ,
78- commit_id : suspect,
79- } ) ;
78+ let mut file_id = |commit, buf : & mut Vec < u8 > , buf2 : & mut Vec < u8 > | {
79+ find_path_entry_in_commit ( & odb, commit, file_path, cache. as_ref ( ) , buf, buf2, & mut stats) ?. ok_or_else ( || {
80+ Error :: FileMissing {
81+ file_path : file_path. to_owned ( ) ,
82+ commit_id : suspect,
83+ }
84+ } )
85+ } ;
8086 let blamed_file_entry_id = file_id ( & suspect, & mut buf, & mut buf2) ?;
8187 let blamed_file_blob = odb. find_blob ( & blamed_file_entry_id, & mut buf) ?. data . to_vec ( ) ;
8288 let num_lines_in_blamed = tokens_for_diffing ( & blamed_file_blob) . tokenize ( ) . count ( ) as u32 ;
@@ -87,17 +93,56 @@ pub fn file(
8793 }
8894
8995 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- } ] ;
96+
97+ let ( blame_entries, mut hunks_to_blame) = match blame_cache {
98+ Some ( blame_cache) => {
99+ // If there is a cache, we first get the diff between the current commit and the commit
100+ // we passed as the cache.
101+ let old_file_id = file_id ( & blame_cache. cache_id , & mut buf, & mut buf2) ?;
102+ let changes = blob_changes (
103+ & odb,
104+ resource_cache,
105+ blamed_file_entry_id,
106+ old_file_id,
107+ file_path. as_bstr ( ) ,
108+ & mut stats,
109+ ) ?;
110+
111+ // If there are no changes, we can return the cache as is immediately.
112+ if changes. iter ( ) . all ( |change| matches ! ( change, Change :: Unchanged ( _) ) ) {
113+ return Ok ( Outcome {
114+ entries : blame_cache. entries . clone ( ) ,
115+ blob : blamed_file_blob,
116+ statistics : stats,
117+ } ) ;
118+ }
119+ // Otherwise, we update the cache with the new changes.
120+ let ( blame_entries, hunks_to_blame) = update_blame_with_changes ( blame_cache. entries , changes, suspect) ;
121+ // If there are no more hunks to blame, we can return the result immediately.
122+ if hunks_to_blame. is_empty ( ) {
123+ return Ok ( Outcome {
124+ entries : blame_entries,
125+ blob : blamed_file_blob,
126+ statistics : stats,
127+ } ) ;
128+ }
129+ ( blame_entries, hunks_to_blame)
130+ }
131+ None => {
132+ let hunks_to_blame = vec ! [ UnblamedHunk {
133+ range_in_blamed_file: range_in_blamed_file. clone( ) ,
134+ suspects: [ ( suspect, range_in_blamed_file) ] . into( ) ,
135+ } ] ;
136+ ( Vec :: new ( ) , hunks_to_blame)
137+ }
138+ } ;
94139
95140 let ( mut buf, mut buf2) = ( Vec :: new ( ) , Vec :: new ( ) ) ;
96141 let commit = find_commit ( cache. as_ref ( ) , & odb, & suspect, & mut buf) ?;
97142 let mut queue: gix_revwalk:: PriorityQueue < CommitTime , ObjectId > = gix_revwalk:: PriorityQueue :: new ( ) ;
98143 queue. insert ( commit_time ( commit) ?, suspect) ;
99144
100- let mut out = Vec :: new ( ) ;
145+ let mut out = blame_entries ;
101146 let mut diff_state = gix_diff:: tree:: State :: default ( ) ;
102147 let mut previous_entry: Option < ( ObjectId , ObjectId ) > = None ;
103148 ' outer: while let Some ( suspect) = queue. pop_value ( ) {
0 commit comments