Skip to content

Commit 1f67966

Browse files
committed
feat!: add gix blame -L start,end
1 parent 787cf6f commit 1f67966

File tree

4 files changed

+59
-5
lines changed

4 files changed

+59
-5
lines changed

gitoxide-core/src/repository/blame.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::ffi::OsStr;
55
pub fn blame_file(
66
mut repo: gix::Repository,
77
file: &OsStr,
8+
range: Option<std::ops::Range<u32>>,
89
out: impl std::io::Write,
910
err: Option<&mut dyn std::io::Write>,
1011
) -> anyhow::Result<()> {
@@ -40,7 +41,7 @@ pub fn blame_file(
4041
.with_commit_graph(repo.commit_graph_if_enabled()?)
4142
.build()?;
4243
let mut resource_cache = repo.diff_resource_cache_for_tree_diff()?;
43-
let outcome = gix::blame::file(&repo.objects, traverse, &mut resource_cache, file.as_bstr())?;
44+
let outcome = gix::blame::file(&repo.objects, traverse, &mut resource_cache, file.as_bstr(), range)?;
4445
let statistics = outcome.statistics;
4546
write_blame_entries(out, outcome)?;
4647

src/plumbing/main.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,15 +1542,25 @@ pub fn main() -> Result<()> {
15421542
},
15431543
),
15441544
},
1545-
Subcommands::Blame { statistics, file } => prepare_and_run(
1545+
Subcommands::Blame {
1546+
statistics,
1547+
file,
1548+
range,
1549+
} => prepare_and_run(
15461550
"blame",
15471551
trace,
15481552
verbose,
15491553
progress,
15501554
progress_keep_open,
15511555
None,
15521556
move |_progress, out, err| {
1553-
core::repository::blame::blame_file(repository(Mode::Lenient)?, &file, out, statistics.then_some(err))
1557+
core::repository::blame::blame_file(
1558+
repository(Mode::Lenient)?,
1559+
&file,
1560+
range,
1561+
out,
1562+
statistics.then_some(err),
1563+
)
15541564
},
15551565
),
15561566
Subcommands::Completions { shell, out_dir } => {

src/plumbing/options/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use clap_complete::Shell;
44
use gitoxide_core as core;
55
use gix::bstr::BString;
66

7+
use crate::shared::AsRange;
8+
79
#[derive(Debug, clap::Parser)]
810
#[clap(name = "gix", about = "The git underworld", version = option_env!("GIX_VERSION"))]
911
#[clap(subcommand_required = true)]
@@ -162,6 +164,9 @@ pub enum Subcommands {
162164
statistics: bool,
163165
/// The file to create the blame information for.
164166
file: std::ffi::OsString,
167+
/// Only blame lines in the given 1-based inclusive range '<start>,<end>', e.g. '20,40'.
168+
#[clap(short='L', value_parser=AsRange)]
169+
range: Option<std::ops::Range<u32>>,
165170
},
166171
/// Generate shell completions to stdout or a directory.
167172
#[clap(visible_alias = "generate-completions", visible_alias = "shell-completions")]

src/shared.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,14 +408,40 @@ mod clap {
408408
.parse_ref(cmd, arg, value)
409409
}
410410
}
411+
412+
#[derive(Clone)]
413+
pub struct AsRange;
414+
415+
impl TypedValueParser for AsRange {
416+
type Value = std::ops::Range<u32>;
417+
418+
fn parse_ref(&self, cmd: &Command, arg: Option<&Arg>, value: &OsStr) -> Result<Self::Value, Error> {
419+
StringValueParser::new()
420+
.try_map(|arg| -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
421+
let parts = arg.split_once(',');
422+
if let Some((start, end)) = parts {
423+
let start = u32::from_str(start)?;
424+
let end = u32::from_str(end)?;
425+
426+
if start <= end {
427+
return Ok(start..end);
428+
}
429+
}
430+
431+
Err(Box::new(Error::new(ErrorKind::ValueValidation)))
432+
})
433+
.parse_ref(cmd, arg, value)
434+
}
435+
}
411436
}
412437
pub use self::clap::{
413-
AsBString, AsHashKind, AsOutputFormat, AsPartialRefName, AsPathSpec, AsTime, CheckPathSpec, ParseRenameFraction,
438+
AsBString, AsHashKind, AsOutputFormat, AsPartialRefName, AsPathSpec, AsRange, AsTime, CheckPathSpec,
439+
ParseRenameFraction,
414440
};
415441

416442
#[cfg(test)]
417443
mod value_parser_tests {
418-
use super::ParseRenameFraction;
444+
use super::{AsRange, ParseRenameFraction};
419445
use clap::Parser;
420446

421447
#[test]
@@ -441,4 +467,16 @@ mod value_parser_tests {
441467
let c = Cmd::parse_from(["cmd", "-a=75"]);
442468
assert_eq!(c.arg, Some(Some(0.75)));
443469
}
470+
471+
#[test]
472+
fn range() {
473+
#[derive(Debug, clap::Parser)]
474+
pub struct Cmd {
475+
#[clap(long, short='l', value_parser = AsRange)]
476+
pub arg: Option<std::ops::Range<u32>>,
477+
}
478+
479+
let c = Cmd::parse_from(["cmd", "-l=1,10"]);
480+
assert_eq!(c.arg, Some(1..10));
481+
}
444482
}

0 commit comments

Comments
 (0)