@@ -25,6 +25,10 @@ use std::ops::Range;
25
25
/// - The first commit returned here is the first eligible commit to be responsible for parts of `file_path`.
26
26
/// * `file_path`
27
27
/// - A *slash-separated* worktree-relative path to the file to blame.
28
+ /// * `range`
29
+ /// - A 1-based inclusive range, in order to mirror `git`’s behaviour. `Some(20..40)` represents
30
+ /// 21 lines, spanning from line 20 up to and including line 40. This will be converted to
31
+ /// `19..40` internally as the algorithm uses 0-based ranges that are exclusive at the end.
28
32
/// * `resource_cache`
29
33
/// - Used for diffing trees.
30
34
///
@@ -61,6 +65,7 @@ pub fn file<E>(
61
65
traverse : impl IntoIterator < Item = Result < gix_traverse:: commit:: Info , E > > ,
62
66
resource_cache : & mut gix_diff:: blob:: Platform ,
63
67
file_path : & BStr ,
68
+ range : Option < Range < u32 > > ,
64
69
) -> Result < Outcome , Error >
65
70
where
66
71
E : Into < Box < dyn std:: error:: Error + Send + Sync + ' static > > ,
@@ -85,19 +90,17 @@ where
85
90
. tokenize ( )
86
91
. map ( |token| interner. intern ( token) )
87
92
. count ( )
88
- } ;
93
+ } as u32 ;
89
94
90
95
// Binary or otherwise empty?
91
96
if num_lines_in_blamed == 0 {
92
97
return Ok ( Outcome :: default ( ) ) ;
93
98
}
94
99
95
- let mut hunks_to_blame = vec ! [ {
96
- let range_in_blamed_file = 0 ..num_lines_in_blamed as u32 ;
97
- UnblamedHunk {
98
- range_in_blamed_file: range_in_blamed_file. clone( ) ,
99
- suspects: [ ( suspect, range_in_blamed_file) ] . into( ) ,
100
- }
100
+ let range_in_blamed_file = one_based_inclusive_to_zero_based_exclusive_range ( range, num_lines_in_blamed) ?;
101
+ let mut hunks_to_blame = vec ! [ UnblamedHunk {
102
+ range_in_blamed_file: range_in_blamed_file. clone( ) ,
103
+ suspects: [ ( suspect, range_in_blamed_file) ] . into( ) ,
101
104
} ] ;
102
105
103
106
let mut out = Vec :: new ( ) ;
@@ -260,6 +263,25 @@ where
260
263
} )
261
264
}
262
265
266
+ /// This function assumes that `range` has 1-based inclusive line numbers and converts it to the
267
+ /// format internally used: 0-based line numbers stored in ranges that are exclusive at the
268
+ /// end.
269
+ fn one_based_inclusive_to_zero_based_exclusive_range (
270
+ range : Option < Range < u32 > > ,
271
+ max_lines : u32 ,
272
+ ) -> Result < Range < u32 > , Error > {
273
+ let Some ( range) = range else { return Ok ( 0 ..max_lines) } ;
274
+ if range. start == 0 {
275
+ return Err ( Error :: InvalidLineRange ) ;
276
+ }
277
+ let start = range. start - 1 ;
278
+ let end = range. end ;
279
+ if start >= max_lines || end > max_lines || start == end {
280
+ return Err ( Error :: InvalidLineRange ) ;
281
+ }
282
+ Ok ( start..end)
283
+ }
284
+
263
285
/// Pass ownership of each unblamed hunk of `from` to `to`.
264
286
///
265
287
/// This happens when `from` didn't actually change anything in the blamed file.
0 commit comments