@@ -10,7 +10,7 @@ use gix_traverse::commit::find as find_commit;
1010use smallvec:: SmallVec ;
1111
1212use super :: { process_changes, Change , UnblamedHunk } ;
13- use crate :: { BlameEntry , Error , Options , Outcome , Statistics } ;
13+ use crate :: { types :: BlamePathEntry , BlameEntry , Error , Options , Outcome , Statistics } ;
1414
1515/// Produce a list of consecutive [`BlameEntry`] instances to indicate in which commits the ranges of the file
1616/// at `suspect:<file_path>` originated in.
@@ -115,6 +115,12 @@ pub fn file(
115115 let mut out = Vec :: new ( ) ;
116116 let mut diff_state = gix_diff:: tree:: State :: default ( ) ;
117117 let mut previous_entry: Option < ( ObjectId , ObjectId ) > = None ;
118+ let mut blame_path = if options. debug_track_path {
119+ Some ( Vec :: new ( ) )
120+ } else {
121+ None
122+ } ;
123+
118124 ' outer: while let Some ( suspect) = queue. pop_value ( ) {
119125 stats. commits_traversed += 1 ;
120126 if hunks_to_blame. is_empty ( ) {
@@ -156,6 +162,22 @@ pub fn file(
156162 // true here. We could perhaps use diff-tree-to-tree to compare `suspect` against
157163 // an empty tree to validate this assumption.
158164 if unblamed_to_out_is_done ( & mut hunks_to_blame, & mut out, suspect) {
165+ if let Some ( ref mut blame_path) = blame_path {
166+ let entry = previous_entry
167+ . take ( )
168+ . filter ( |( id, _) | * id == suspect)
169+ . map ( |( _, entry) | entry) ;
170+
171+ let blame_path_entry = BlamePathEntry {
172+ source_file_path : current_file_path. clone ( ) ,
173+ previous_source_file_path : None ,
174+ commit_id : suspect,
175+ blob_id : entry. unwrap_or ( ObjectId :: null ( gix_hash:: Kind :: Sha1 ) ) ,
176+ previous_blob_id : ObjectId :: null ( gix_hash:: Kind :: Sha1 ) ,
177+ } ;
178+ blame_path. push ( blame_path_entry) ;
179+ }
180+
159181 break ' outer;
160182 }
161183 }
@@ -271,12 +293,23 @@ pub fn file(
271293 } ;
272294
273295 match modification {
274- TreeDiffChange :: Addition => {
296+ TreeDiffChange :: Addition { id } => {
275297 if more_than_one_parent {
276298 // Do nothing under the assumption that this always (or almost always)
277299 // implies that the file comes from a different parent, compared to which
278300 // it was modified, not added.
279301 } else if unblamed_to_out_is_done ( & mut hunks_to_blame, & mut out, suspect) {
302+ if let Some ( ref mut blame_path) = blame_path {
303+ let blame_path_entry = BlamePathEntry {
304+ source_file_path : current_file_path. clone ( ) ,
305+ previous_source_file_path : None ,
306+ commit_id : suspect,
307+ blob_id : id,
308+ previous_blob_id : ObjectId :: null ( gix_hash:: Kind :: Sha1 ) ,
309+ } ;
310+ blame_path. push ( blame_path_entry) ;
311+ }
312+
280313 break ' outer;
281314 }
282315 }
@@ -295,6 +328,16 @@ pub fn file(
295328 & mut stats,
296329 ) ?;
297330 hunks_to_blame = process_changes ( hunks_to_blame, changes, suspect, parent_id) ;
331+ if let Some ( ref mut blame_path) = blame_path {
332+ let blame_path_entry = BlamePathEntry {
333+ source_file_path : current_file_path. clone ( ) ,
334+ previous_source_file_path : Some ( current_file_path. clone ( ) ) ,
335+ commit_id : suspect,
336+ blob_id : id,
337+ previous_blob_id : previous_id,
338+ } ;
339+ blame_path. push ( blame_path_entry) ;
340+ }
298341 }
299342 TreeDiffChange :: Rewrite {
300343 source_location,
@@ -313,9 +356,26 @@ pub fn file(
313356 ) ?;
314357 hunks_to_blame = process_changes ( hunks_to_blame, changes, suspect, parent_id) ;
315358
359+ let mut has_blame_been_passed = false ;
360+
316361 for hunk in hunks_to_blame. iter_mut ( ) {
317362 if hunk. has_suspect ( & parent_id) {
318363 hunk. source_file_name = Some ( source_location. clone ( ) ) ;
364+
365+ has_blame_been_passed = true ;
366+ }
367+ }
368+
369+ if has_blame_been_passed {
370+ if let Some ( ref mut blame_path) = blame_path {
371+ let blame_path_entry = BlamePathEntry {
372+ source_file_path : current_file_path. clone ( ) ,
373+ previous_source_file_path : Some ( source_location. clone ( ) ) ,
374+ commit_id : suspect,
375+ blob_id : id,
376+ previous_blob_id : source_id,
377+ } ;
378+ blame_path. push ( blame_path_entry) ;
319379 }
320380 }
321381 }
@@ -351,6 +411,7 @@ pub fn file(
351411 entries : coalesce_blame_entries ( out) ,
352412 blob : blamed_file_blob,
353413 statistics : stats,
414+ blame_path,
354415 } )
355416}
356417
@@ -435,7 +496,9 @@ fn coalesce_blame_entries(lines_blamed: Vec<BlameEntry>) -> Vec<BlameEntry> {
435496/// The union of [`gix_diff::tree::recorder::Change`] and [`gix_diff::tree_with_rewrites::Change`],
436497/// keeping only the blame-relevant information.
437498enum TreeDiffChange {
438- Addition ,
499+ Addition {
500+ id : ObjectId ,
501+ } ,
439502 Deletion ,
440503 Modification {
441504 previous_id : ObjectId ,
@@ -453,7 +516,7 @@ impl From<gix_diff::tree::recorder::Change> for TreeDiffChange {
453516 use gix_diff:: tree:: recorder:: Change ;
454517
455518 match value {
456- Change :: Addition { .. } => Self :: Addition ,
519+ Change :: Addition { oid , .. } => Self :: Addition { id : oid } ,
457520 Change :: Deletion { .. } => Self :: Deletion ,
458521 Change :: Modification { previous_oid, oid, .. } => Self :: Modification {
459522 previous_id : previous_oid,
@@ -468,7 +531,7 @@ impl From<gix_diff::tree_with_rewrites::Change> for TreeDiffChange {
468531 use gix_diff:: tree_with_rewrites:: Change ;
469532
470533 match value {
471- Change :: Addition { .. } => Self :: Addition ,
534+ Change :: Addition { id , .. } => Self :: Addition { id } ,
472535 Change :: Deletion { .. } => Self :: Deletion ,
473536 Change :: Modification { previous_id, id, .. } => Self :: Modification { previous_id, id } ,
474537 Change :: Rewrite {
0 commit comments