1
- use super :: { process_changes, Change , UnblamedHunk } ;
1
+ use super :: { process_changes, update_blame_with_changes, Change , UnblamedHunk } ;
2
+ use crate :: types:: BlameCacheObject ;
2
3
use crate :: { BlameEntry , Error , Outcome , Statistics } ;
3
4
use gix_diff:: blob:: intern:: TokenSource ;
4
5
use gix_diff:: tree:: Visit ;
5
6
use gix_hash:: ObjectId ;
6
7
use gix_object:: {
7
- bstr:: { BStr , BString } ,
8
+ bstr:: { BStr , BString , ByteSlice } ,
8
9
FindExt ,
9
10
} ;
10
11
use gix_traverse:: commit:: find as find_commit;
@@ -66,17 +67,22 @@ pub fn file(
66
67
suspect : ObjectId ,
67
68
cache : Option < gix_commitgraph:: Graph > ,
68
69
resource_cache : & mut gix_diff:: blob:: Platform ,
70
+ blame_cache : Option < BlameCacheObject > ,
69
71
file_path : & BStr ,
70
72
range : Option < Range < u32 > > ,
71
73
) -> Result < Outcome , Error > {
72
74
let _span = gix_trace:: coarse!( "gix_blame::file()" , ?file_path, ?suspect) ;
73
75
74
76
let mut stats = Statistics :: default ( ) ;
75
77
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
+ } ;
80
86
let blamed_file_entry_id = file_id ( & suspect, & mut buf, & mut buf2) ?;
81
87
let blamed_file_blob = odb. find_blob ( & blamed_file_entry_id, & mut buf) ?. data . to_vec ( ) ;
82
88
let num_lines_in_blamed = tokens_for_diffing ( & blamed_file_blob) . tokenize ( ) . count ( ) as u32 ;
@@ -87,17 +93,56 @@ pub fn file(
87
93
}
88
94
89
95
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
+ } ;
94
139
95
140
let ( mut buf, mut buf2) = ( Vec :: new ( ) , Vec :: new ( ) ) ;
96
141
let commit = find_commit ( cache. as_ref ( ) , & odb, & suspect, & mut buf) ?;
97
142
let mut queue: gix_revwalk:: PriorityQueue < CommitTime , ObjectId > = gix_revwalk:: PriorityQueue :: new ( ) ;
98
143
queue. insert ( commit_time ( commit) ?, suspect) ;
99
144
100
- let mut out = Vec :: new ( ) ;
145
+ let mut out = blame_entries ;
101
146
let mut diff_state = gix_diff:: tree:: State :: default ( ) ;
102
147
let mut previous_entry: Option < ( ObjectId , ObjectId ) > = None ;
103
148
' outer: while let Some ( suspect) = queue. pop_value ( ) {
0 commit comments