@@ -25,6 +25,10 @@ use std::ops::Range;
2525/// - The first commit returned here is the first eligible commit to be responsible for parts of `file_path`.
2626/// * `file_path`
2727/// - 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.
2832/// * `resource_cache`
2933/// - Used for diffing trees.
3034///
@@ -61,6 +65,7 @@ pub fn file<E>(
6165 traverse : impl IntoIterator < Item = Result < gix_traverse:: commit:: Info , E > > ,
6266 resource_cache : & mut gix_diff:: blob:: Platform ,
6367 file_path : & BStr ,
68+ range : Option < Range < u32 > > ,
6469) -> Result < Outcome , Error >
6570where
6671 E : Into < Box < dyn std:: error:: Error + Send + Sync + ' static > > ,
@@ -85,19 +90,17 @@ where
8590 . tokenize ( )
8691 . map ( |token| interner. intern ( token) )
8792 . count ( )
88- } ;
93+ } as u32 ;
8994
9095 // Binary or otherwise empty?
9196 if num_lines_in_blamed == 0 {
9297 return Ok ( Outcome :: default ( ) ) ;
9398 }
9499
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( ) ,
101104 } ] ;
102105
103106 let mut out = Vec :: new ( ) ;
@@ -260,6 +263,25 @@ where
260263 } )
261264}
262265
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+
263285/// Pass ownership of each unblamed hunk of `from` to `to`.
264286///
265287/// This happens when `from` didn't actually change anything in the blamed file.
0 commit comments