3
3
#![ forbid( unsafe_code) ]
4
4
5
5
use std:: {
6
+ collections:: BTreeMap ,
6
7
ops:: { Add , AddAssign , Range , SubAssign } ,
7
8
path:: PathBuf ,
8
9
} ;
@@ -222,6 +223,30 @@ impl UnblamedHunk {
222
223
}
223
224
}
224
225
226
+ struct UnblamedHunkRecorder {
227
+ hunks : BTreeMap < ObjectId , Vec < UnblamedHunk > > ,
228
+ }
229
+
230
+ impl UnblamedHunkRecorder {
231
+ fn new ( ) -> Self {
232
+ Self { hunks : BTreeMap :: new ( ) }
233
+ }
234
+
235
+ fn add_hunk ( & mut self , object_id : ObjectId , unblamed_hunk : UnblamedHunk ) {
236
+ self . hunks
237
+ . entry ( object_id)
238
+ . and_modify ( |unblamed_hunks| unblamed_hunks. push ( unblamed_hunk. clone ( ) ) )
239
+ . or_insert_with ( || vec ! [ unblamed_hunk. clone( ) ] ) ;
240
+ }
241
+
242
+ fn add_hunks ( & mut self , object_id : ObjectId , new_unblamed_hunks : Vec < UnblamedHunk > ) {
243
+ self . hunks
244
+ . entry ( object_id)
245
+ . and_modify ( |unblamed_hunks| unblamed_hunks. extend ( new_unblamed_hunks. clone ( ) ) )
246
+ . or_insert_with ( || new_unblamed_hunks) ;
247
+ }
248
+ }
249
+
225
250
#[ derive( Clone , Debug , PartialEq ) ]
226
251
pub enum Change {
227
252
Unchanged ( Range < u32 > ) ,
@@ -742,6 +767,7 @@ pub fn blame_file<E>(
742
767
traverse : impl IntoIterator < Item = Result < gix_traverse:: commit:: Info , E > > ,
743
768
resource_cache : & mut gix_diff:: blob:: Platform ,
744
769
worktree_path : PathBuf ,
770
+ start_id : ObjectId ,
745
771
file_path : & BStr ,
746
772
) -> Result < Vec < BlameEntry > , E > {
747
773
// TODO
@@ -770,24 +796,33 @@ pub fn blame_file<E>(
770
796
// TODO Verify that `imara-diff` tokenizes lines the same way `lines` does.
771
797
let number_of_lines = std:: fs:: read_to_string ( absolute_path) . unwrap ( ) . lines ( ) . count ( ) ;
772
798
773
- let mut lines_to_blame: Vec < UnblamedHunk > = vec ! [ UnblamedHunk :: new(
774
- 0 ..number_of_lines. try_into( ) . unwrap( ) ,
775
- Offset :: Added ( 0 ) ,
776
- ) ] ;
799
+ let mut unblamed_hunk_recorder = UnblamedHunkRecorder :: new ( ) ;
800
+ unblamed_hunk_recorder. add_hunk (
801
+ start_id,
802
+ UnblamedHunk :: new ( 0 ..number_of_lines. try_into ( ) . unwrap ( ) , Offset :: Added ( 0 ) ) ,
803
+ ) ;
777
804
let mut out: Vec < BlameEntry > = vec ! [ ] ;
778
805
779
806
for item in traverse {
780
807
let item = item?;
781
808
let suspect = item. id ;
782
809
810
+ // TODO
811
+ // When we call `.remove(&suspect)`, we need to make sure that all commits that have
812
+ // `suspect` as their parent have already been processed. `git` accomplishes that by
813
+ // keeping unprocessed commits in a priority queue sorted by commit date.
814
+ let Some ( hunks_to_blame) = unblamed_hunk_recorder. hunks . remove ( & suspect) else {
815
+ continue ;
816
+ } ;
817
+
783
818
let parent_ids = item. parent_ids ;
784
819
if parent_ids. is_empty ( ) {
785
820
// I’m not entirely sure if this is correct yet. `suspect`, at this point, is the `id` of
786
821
// the last `item` that was yielded by `traverse`, so it makes sense to assign the
787
822
// remaining lines to it, even though we don’t explicitly check whether that is true
788
823
// here. We could perhaps use `needed_to_obtain` to compare `suspect` against an empty
789
824
// tree to validate this assumption.
790
- out. extend ( lines_to_blame . iter ( ) . map ( |hunk| {
825
+ out. extend ( hunks_to_blame . iter ( ) . map ( |hunk| {
791
826
BlameEntry :: new (
792
827
hunk. range_in_blamed_file . clone ( ) ,
793
828
// TODO
@@ -798,8 +833,6 @@ pub fn blame_file<E>(
798
833
)
799
834
} ) ) ;
800
835
801
- lines_to_blame = vec ! [ ] ;
802
-
803
836
break ;
804
837
}
805
838
@@ -813,14 +846,16 @@ pub fn blame_file<E>(
813
846
814
847
let [ ref modification] : [ gix_diff:: tree:: recorder:: Change ] = changes_for_file_path[ ..] else {
815
848
// None of the changes affected the file we’re currently blaming.
849
+ unblamed_hunk_recorder. add_hunks ( last_parent_id, hunks_to_blame) ;
850
+
816
851
continue ;
817
852
} ;
818
853
819
854
match modification {
820
855
gix_diff:: tree:: recorder:: Change :: Addition { .. } => {
821
856
// Every line that has not been blamed yet on a commit, is expected to have been
822
857
// added when the file was added to the repository.
823
- out. extend ( lines_to_blame . iter ( ) . map ( |hunk| {
858
+ out. extend ( hunks_to_blame . iter ( ) . map ( |hunk| {
824
859
BlameEntry :: new (
825
860
hunk. range_in_blamed_file . clone ( ) ,
826
861
// TODO
@@ -831,20 +866,20 @@ pub fn blame_file<E>(
831
866
)
832
867
} ) ) ;
833
868
834
- lines_to_blame = vec ! [ ] ;
835
-
836
869
break ;
837
870
}
838
871
gix_diff:: tree:: recorder:: Change :: Deletion { .. } => todo ! ( ) ,
839
872
gix_diff:: tree:: recorder:: Change :: Modification { previous_oid, oid, .. } => {
840
873
let changes = get_changes ( & odb, resource_cache, * oid, * previous_oid, file_path) ;
841
874
842
- lines_to_blame = process_changes ( & mut out, & lines_to_blame, & changes, suspect) ;
875
+ let new_hunks_to_blame = process_changes ( & mut out, & hunks_to_blame, & changes, suspect) ;
876
+
877
+ unblamed_hunk_recorder. add_hunks ( last_parent_id, new_hunks_to_blame. clone ( ) ) ;
843
878
}
844
879
}
845
880
}
846
881
847
- assert_eq ! ( lines_to_blame , vec! [ ] ) ;
882
+ assert ! ( unblamed_hunk_recorder . hunks . is_empty ( ) ) ;
848
883
849
884
// I don’t know yet whether it would make sense to use a data structure instead that preserves
850
885
// order on insertion.
0 commit comments