diff options
Diffstat (limited to 'vendor/git2/examples/blame.rs')
-rw-r--r-- | vendor/git2/examples/blame.rs | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/vendor/git2/examples/blame.rs b/vendor/git2/examples/blame.rs new file mode 100644 index 0000000..7cb1b69 --- /dev/null +++ b/vendor/git2/examples/blame.rs @@ -0,0 +1,104 @@ +/* + * libgit2 "blame" example - shows how to use the blame API + * + * Written by the libgit2 contributors + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along + * with this software. If not, see + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#![deny(warnings)] + +use git2::{BlameOptions, Repository}; +use std::io::{BufRead, BufReader}; +use std::path::Path; +use structopt::StructOpt; + +#[derive(StructOpt)] +#[allow(non_snake_case)] +struct Args { + #[structopt(name = "path")] + arg_path: String, + #[structopt(name = "spec")] + arg_spec: Option<String>, + #[structopt(short = "M")] + /// find line moves within and across files + flag_M: bool, + #[structopt(short = "C")] + /// find line copies within and across files + flag_C: bool, + #[structopt(short = "F")] + /// follow only the first parent commits + flag_F: bool, +} + +fn run(args: &Args) -> Result<(), git2::Error> { + let repo = Repository::open(".")?; + let path = Path::new(&args.arg_path[..]); + + // Prepare our blame options + let mut opts = BlameOptions::new(); + opts.track_copies_same_commit_moves(args.flag_M) + .track_copies_same_commit_copies(args.flag_C) + .first_parent(args.flag_F); + + let mut commit_id = "HEAD".to_string(); + + // Parse spec + if let Some(spec) = args.arg_spec.as_ref() { + let revspec = repo.revparse(spec)?; + + let (oldest, newest) = if revspec.mode().contains(git2::RevparseMode::SINGLE) { + (None, revspec.from()) + } else if revspec.mode().contains(git2::RevparseMode::RANGE) { + (revspec.from(), revspec.to()) + } else { + (None, None) + }; + + if let Some(commit) = oldest { + opts.oldest_commit(commit.id()); + } + + if let Some(commit) = newest { + opts.newest_commit(commit.id()); + if !commit.id().is_zero() { + commit_id = format!("{}", commit.id()) + } + } + } + + let spec = format!("{}:{}", commit_id, path.display()); + let blame = repo.blame_file(path, Some(&mut opts))?; + let object = repo.revparse_single(&spec[..])?; + let blob = repo.find_blob(object.id())?; + let reader = BufReader::new(blob.content()); + + for (i, line) in reader.lines().enumerate() { + if let (Ok(line), Some(hunk)) = (line, blame.get_line(i + 1)) { + let sig = hunk.final_signature(); + println!( + "{} {} <{}> {}", + hunk.final_commit_id(), + String::from_utf8_lossy(sig.name_bytes()), + String::from_utf8_lossy(sig.email_bytes()), + line + ); + } + } + + Ok(()) +} + +fn main() { + let args = Args::from_args(); + match run(&args) { + Ok(()) => {} + Err(e) => println!("error: {}", e), + } +} |