Skip to content

Commit 787cf6f

Browse files
committed
feat!: add range to blame::file()
1 parent e4fb21e commit 787cf6f

File tree

4 files changed

+54
-6
lines changed

4 files changed

+54
-6
lines changed

gix-blame/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ pub enum Error {
2727
Traverse(#[source] Box<dyn std::error::Error + Send + Sync>),
2828
#[error(transparent)]
2929
DiffTree(#[from] gix_diff::tree::Error),
30+
#[error("Invalid line range was given, line range is expected to be a 1-based inclusive range in the format '<start>,<end>'")]
31+
InvalidLineRange,
3032
}

gix-blame/src/file/function.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
6570
where
6671
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
@@ -85,19 +90,37 @@ 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+
// This function assumes that `range` has 1-based inclusive line numbers and converts it to the
101+
// format internally used: 0-based line numbers stored in ranges that are exclusive at the
102+
// end.
103+
let one_based_inclusive_to_zero_based_exclusive_range = || -> Result<Range<u32>, Error> {
104+
if let Some(range) = range {
105+
if range.start == 0 {
106+
return Err(Error::InvalidLineRange);
107+
}
108+
let start = range.start - 1;
109+
let end = range.end;
110+
if start >= num_lines_in_blamed || end > num_lines_in_blamed || start == end {
111+
return Err(Error::InvalidLineRange);
112+
}
113+
Ok(start..end)
114+
} else {
115+
Ok(0..num_lines_in_blamed)
100116
}
117+
};
118+
119+
let range_in_blamed_file = one_based_inclusive_to_zero_based_exclusive_range()?;
120+
121+
let mut hunks_to_blame = vec![UnblamedHunk {
122+
range_in_blamed_file: range_in_blamed_file.clone(),
123+
suspects: [(suspect, range_in_blamed_file)].into(),
101124
}];
102125

103126
let mut out = Vec::new();

gix-blame/tests/blame.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ macro_rules! mktest {
194194
commits,
195195
&mut resource_cache,
196196
format!("{}.txt", $case).as_str().into(),
197+
None,
197198
)?
198199
.entries;
199200

@@ -254,6 +255,7 @@ fn diff_disparity() {
254255
commits,
255256
&mut resource_cache,
256257
format!("{case}.txt").as_str().into(),
258+
None,
257259
)
258260
.unwrap()
259261
.entries;
@@ -267,6 +269,26 @@ fn diff_disparity() {
267269
}
268270
}
269271

272+
#[test]
273+
fn line_range() {
274+
let Fixture {
275+
odb,
276+
mut resource_cache,
277+
commits,
278+
} = Fixture::new().unwrap();
279+
280+
let lines_blamed = gix_blame::file(&odb, commits, &mut resource_cache, "simple.txt".into(), Some(1..2))
281+
.unwrap()
282+
.entries;
283+
284+
assert_eq!(lines_blamed.len(), 2);
285+
286+
let git_dir = fixture_path().join(".git");
287+
let baseline = Baseline::collect(git_dir.join("simple-lines-1-2.baseline")).unwrap();
288+
289+
assert_eq!(lines_blamed, baseline);
290+
}
291+
270292
fn fixture_path() -> PathBuf {
271293
gix_testtools::scripted_fixture_read_only("make_blame_repo.sh").unwrap()
272294
}

gix-blame/tests/fixtures/make_blame_repo.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ git commit -q -m c14.2
199199
git merge branch-that-has-one-of-the-changes || true
200200

201201
git blame --porcelain simple.txt > .git/simple.baseline
202+
git blame --porcelain -L 1,2 simple.txt > .git/simple-lines-1-2.baseline
202203
git blame --porcelain multiline-hunks.txt > .git/multiline-hunks.baseline
203204
git blame --porcelain deleted-lines.txt > .git/deleted-lines.baseline
204205
git blame --porcelain deleted-lines-multiple-hunks.txt > .git/deleted-lines-multiple-hunks.baseline

0 commit comments

Comments
 (0)