1
1
use super :: { process_changes, Change , Offset , UnblamedHunk } ;
2
- use crate :: { BlameEntry , Outcome } ;
2
+ use crate :: { BlameEntry , Outcome , Statistics } ;
3
3
use gix_diff:: blob:: intern:: TokenSource ;
4
4
use gix_hash:: ObjectId ;
5
5
use gix_object:: { bstr:: BStr , FindExt } ;
@@ -62,9 +62,12 @@ pub fn file<E>(
62
62
let Some ( Ok ( suspect) ) = traverse. peek ( ) . map ( |res| res. as_ref ( ) . map ( |item| item. id ) ) else {
63
63
todo ! ( "return actual error" ) ;
64
64
} ;
65
+ let _span = gix_trace:: coarse!( "gix_blame::file()" , ?file_path, ?suspect) ;
65
66
66
- let ( mut buf, mut buf2) = ( Vec :: new ( ) , Vec :: new ( ) ) ;
67
- let original_file_entry = find_path_entry_in_commit ( & odb, & suspect, file_path, & mut buf, & mut buf2) . unwrap ( ) ;
67
+ let mut stats = Statistics :: default ( ) ;
68
+ let ( mut buf, mut buf2, mut buf3) = ( Vec :: new ( ) , Vec :: new ( ) , Vec :: new ( ) ) ;
69
+ let original_file_entry =
70
+ find_path_entry_in_commit ( & odb, & suspect, file_path, & mut buf, & mut buf2, & mut stats) . unwrap ( ) ;
68
71
let original_file_blob = odb. find_blob ( & original_file_entry. oid , & mut buf) . unwrap ( ) . data . to_vec ( ) ;
69
72
let num_lines_in_original = {
70
73
let mut interner = gix_diff:: blob:: intern:: Interner :: new ( original_file_blob. len ( ) / 100 ) ;
@@ -81,9 +84,11 @@ pub fn file<E>(
81
84
) ] ;
82
85
83
86
let mut out = Vec :: new ( ) ;
87
+ let mut diff_state = gix_diff:: tree:: State :: default ( ) ;
84
88
' outer: for item in traverse {
85
89
let item = item?;
86
90
let suspect = item. id ;
91
+ stats. commits_traversed += 1 ;
87
92
88
93
let mut parent_ids = item. parent_ids ;
89
94
if parent_ids. is_empty ( ) {
@@ -102,13 +107,15 @@ pub fn file<E>(
102
107
break ;
103
108
}
104
109
105
- let Some ( entry) = find_path_entry_in_commit ( & odb, & suspect, file_path, & mut buf, & mut buf2) else {
110
+ let Some ( entry) = find_path_entry_in_commit ( & odb, & suspect, file_path, & mut buf, & mut buf2, & mut stats ) else {
106
111
continue ;
107
112
} ;
108
113
109
114
if parent_ids. len ( ) == 1 {
110
115
let parent_id = parent_ids. pop ( ) . expect ( "just validated there is exactly one" ) ;
111
- if let Some ( parent_entry) = find_path_entry_in_commit ( & odb, & parent_id, file_path, & mut buf, & mut buf2) {
116
+ if let Some ( parent_entry) =
117
+ find_path_entry_in_commit ( & odb, & parent_id, file_path, & mut buf, & mut buf2, & mut stats)
118
+ {
112
119
if entry. oid == parent_entry. oid {
113
120
// The blobs storing the blamed file in `entry` and `parent_entry` are identical
114
121
// which is why we can pass blame to the parent without further checks.
@@ -119,7 +126,17 @@ pub fn file<E>(
119
126
}
120
127
}
121
128
122
- let Some ( modification) = tree_diff_at_file_path ( & odb, file_path, item. id , parent_id) else {
129
+ let Some ( modification) = tree_diff_at_file_path (
130
+ & odb,
131
+ file_path,
132
+ item. id ,
133
+ parent_id,
134
+ & mut stats,
135
+ & mut diff_state,
136
+ & mut buf,
137
+ & mut buf2,
138
+ & mut buf3,
139
+ ) else {
123
140
// None of the changes affected the file we’re currently blaming. Pass blame to parent.
124
141
for unblamed_hunk in & mut hunks_to_blame {
125
142
unblamed_hunk. pass_blame ( suspect, parent_id) ;
@@ -142,8 +159,7 @@ pub fn file<E>(
142
159
}
143
160
gix_diff:: tree:: recorder:: Change :: Deletion { .. } => todo ! ( ) ,
144
161
gix_diff:: tree:: recorder:: Change :: Modification { previous_oid, oid, .. } => {
145
- let changes = blob_changes ( & odb, resource_cache, oid, previous_oid, file_path) ;
146
-
162
+ let changes = blob_changes ( & odb, resource_cache, oid, previous_oid, file_path, & mut stats) ;
147
163
hunks_to_blame = process_changes ( & mut out, hunks_to_blame, changes, suspect) ;
148
164
for unblamed_hunk in & mut hunks_to_blame {
149
165
unblamed_hunk. pass_blame ( suspect, parent_id) ;
@@ -152,7 +168,8 @@ pub fn file<E>(
152
168
}
153
169
} else {
154
170
for parent_id in & parent_ids {
155
- if let Some ( parent_entry) = find_path_entry_in_commit ( & odb, & parent_id, file_path, & mut buf, & mut buf2)
171
+ if let Some ( parent_entry) =
172
+ find_path_entry_in_commit ( & odb, parent_id, file_path, & mut buf, & mut buf2, & mut stats)
156
173
{
157
174
if entry. oid == parent_entry. oid {
158
175
// The blobs storing the blamed file in `entry` and `parent_entry` are
@@ -167,7 +184,17 @@ pub fn file<E>(
167
184
}
168
185
169
186
for parent_id in parent_ids {
170
- let changes_for_file_path = tree_diff_at_file_path ( & odb, file_path, item. id , parent_id) ;
187
+ let changes_for_file_path = tree_diff_at_file_path (
188
+ & odb,
189
+ file_path,
190
+ item. id ,
191
+ parent_id,
192
+ & mut stats,
193
+ & mut diff_state,
194
+ & mut buf,
195
+ & mut buf2,
196
+ & mut buf3,
197
+ ) ;
171
198
let Some ( modification) = changes_for_file_path else {
172
199
// None of the changes affected the file we’re currently blaming. Pass blame
173
200
// to parent.
@@ -188,8 +215,7 @@ pub fn file<E>(
188
215
}
189
216
gix_diff:: tree:: recorder:: Change :: Deletion { .. } => todo ! ( ) ,
190
217
gix_diff:: tree:: recorder:: Change :: Modification { previous_oid, oid, .. } => {
191
- let changes = blob_changes ( & odb, resource_cache, oid, previous_oid, file_path) ;
192
-
218
+ let changes = blob_changes ( & odb, resource_cache, oid, previous_oid, file_path, & mut stats) ;
193
219
hunks_to_blame = process_changes ( & mut out, hunks_to_blame, changes, suspect) ;
194
220
for unblamed_hunk in & mut hunks_to_blame {
195
221
unblamed_hunk. pass_blame ( suspect, parent_id) ;
@@ -215,6 +241,7 @@ pub fn file<E>(
215
241
Ok ( Outcome {
216
242
entries : coalesce_blame_entries ( out) ,
217
243
blob : original_file_blob,
244
+ statistics : stats,
218
245
} )
219
246
}
220
247
@@ -262,42 +289,37 @@ fn coalesce_blame_entries(lines_blamed: Vec<BlameEntry>) -> Vec<BlameEntry> {
262
289
} )
263
290
}
264
291
292
+ #[ allow( clippy:: too_many_arguments) ]
265
293
fn tree_diff_at_file_path (
266
294
odb : impl gix_object:: Find + gix_object:: FindHeader ,
267
295
file_path : & BStr ,
268
296
id : ObjectId ,
269
297
parent_id : ObjectId ,
298
+ stats : & mut Statistics ,
299
+ state : & mut gix_diff:: tree:: State ,
300
+ commit_buf : & mut Vec < u8 > ,
301
+ lhs_tree_buf : & mut Vec < u8 > ,
302
+ rhs_tree_buf : & mut Vec < u8 > ,
270
303
) -> Option < gix_diff:: tree:: recorder:: Change > {
271
- let mut buffer = Vec :: new ( ) ;
304
+ let parent_tree = odb. find_commit ( & parent_id, commit_buf) . unwrap ( ) . tree ( ) ;
305
+ stats. commits_to_tree += 1 ;
272
306
273
- let parent = odb. find_commit ( & parent_id, & mut buffer) . unwrap ( ) ;
274
-
275
- let mut buffer = Vec :: new ( ) ;
276
307
let parent_tree_iter = odb
277
- . find ( & parent . tree ( ) , & mut buffer )
308
+ . find ( & parent_tree , lhs_tree_buf )
278
309
. unwrap ( )
279
310
. try_into_tree_iter ( )
280
311
. unwrap ( ) ;
312
+ stats. trees_decoded += 1 ;
281
313
282
- let mut buffer = Vec :: new ( ) ;
283
- let commit = odb . find_commit ( & id , & mut buffer ) . unwrap ( ) ;
314
+ let tree_id = odb . find_commit ( & id , commit_buf ) . unwrap ( ) . tree ( ) ;
315
+ stats . commits_to_tree += 1 ;
284
316
285
- let mut buffer = Vec :: new ( ) ;
286
- let tree_iter = odb
287
- . find ( & commit. tree ( ) , & mut buffer)
288
- . unwrap ( )
289
- . try_into_tree_iter ( )
290
- . unwrap ( ) ;
317
+ let tree_iter = odb. find ( & tree_id, rhs_tree_buf) . unwrap ( ) . try_into_tree_iter ( ) . unwrap ( ) ;
318
+ stats. trees_decoded += 1 ;
291
319
292
320
let mut recorder = gix_diff:: tree:: Recorder :: default ( ) ;
293
- gix_diff:: tree (
294
- parent_tree_iter,
295
- tree_iter,
296
- gix_diff:: tree:: State :: default ( ) ,
297
- & odb,
298
- & mut recorder,
299
- )
300
- . unwrap ( ) ;
321
+ gix_diff:: tree ( parent_tree_iter, tree_iter, state, & odb, & mut recorder) . unwrap ( ) ;
322
+ stats. trees_diffed += 1 ;
301
323
302
324
recorder. records . into_iter ( ) . find ( |change| match change {
303
325
gix_diff:: tree:: recorder:: Change :: Modification { path, .. } => path == file_path,
@@ -312,6 +334,7 @@ fn blob_changes(
312
334
oid : ObjectId ,
313
335
previous_oid : ObjectId ,
314
336
file_path : & BStr ,
337
+ stats : & mut Statistics ,
315
338
) -> Vec < Change > {
316
339
/// Record all [`Change`]s to learn about additions, deletions and unchanged portions of a *Blamed File*.
317
340
struct ChangeRecorder {
@@ -391,6 +414,7 @@ fn blob_changes(
391
414
let number_of_lines_in_destination = input. after . len ( ) ;
392
415
let change_recorder = ChangeRecorder :: new ( number_of_lines_in_destination. try_into ( ) . unwrap ( ) ) ;
393
416
417
+ stats. blobs_diffed += 1 ;
394
418
gix_diff:: blob:: diff ( gix_diff:: blob:: Algorithm :: Histogram , & input, change_recorder)
395
419
}
396
420
@@ -400,12 +424,19 @@ fn find_path_entry_in_commit(
400
424
file_path : & BStr ,
401
425
buf : & mut Vec < u8 > ,
402
426
buf2 : & mut Vec < u8 > ,
427
+ stats : & mut Statistics ,
403
428
) -> Option < gix_object:: tree:: Entry > {
404
429
let commit_id = odb. find_commit ( commit, buf) . unwrap ( ) . tree ( ) ;
405
430
let tree_iter = odb. find_tree_iter ( & commit_id, buf) . unwrap ( ) ;
431
+ stats. commits_to_tree += 1 ;
432
+ stats. trees_decoded += 1 ;
406
433
407
434
tree_iter
408
- . lookup_entry ( odb, buf2, file_path. split ( |b| * b == b'/' ) )
435
+ . lookup_entry (
436
+ odb,
437
+ buf2,
438
+ file_path. split ( |b| * b == b'/' ) . inspect ( |_| stats. trees_decoded += 1 ) ,
439
+ )
409
440
. unwrap ( )
410
441
}
411
442
0 commit comments