diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:35 +0000 |
commit | 7e5d7eea9c580ef4b41a765bde624af431942b96 (patch) | |
tree | 2c0d9ca12878fc4525650aa4e54d77a81a07cc09 /vendor/git2/examples/log.rs | |
parent | Adding debian version 1.70.0+dfsg1-9. (diff) | |
download | rustc-7e5d7eea9c580ef4b41a765bde624af431942b96.tar.xz rustc-7e5d7eea9c580ef4b41a765bde624af431942b96.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/git2/examples/log.rs')
-rw-r--r-- | vendor/git2/examples/log.rs | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/vendor/git2/examples/log.rs b/vendor/git2/examples/log.rs new file mode 100644 index 000000000..ad3bb354d --- /dev/null +++ b/vendor/git2/examples/log.rs @@ -0,0 +1,310 @@ +/* + * libgit2 "log" example - shows how to walk history and get commit info + * + * 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::{Commit, DiffOptions, ObjectType, Repository, Signature, Time}; +use git2::{DiffFormat, Error, Pathspec}; +use std::str; +use structopt::StructOpt; + +#[derive(StructOpt)] +struct Args { + #[structopt(name = "topo-order", long)] + /// sort commits in topological order + flag_topo_order: bool, + #[structopt(name = "date-order", long)] + /// sort commits in date order + flag_date_order: bool, + #[structopt(name = "reverse", long)] + /// sort commits in reverse + flag_reverse: bool, + #[structopt(name = "author", long)] + /// author to sort by + flag_author: Option<String>, + #[structopt(name = "committer", long)] + /// committer to sort by + flag_committer: Option<String>, + #[structopt(name = "pat", long = "grep")] + /// pattern to filter commit messages by + flag_grep: Option<String>, + #[structopt(name = "dir", long = "git-dir")] + /// alternative git directory to use + flag_git_dir: Option<String>, + #[structopt(name = "skip", long)] + /// number of commits to skip + flag_skip: Option<usize>, + #[structopt(name = "max-count", short = "n", long)] + /// maximum number of commits to show + flag_max_count: Option<usize>, + #[structopt(name = "merges", long)] + /// only show merge commits + flag_merges: bool, + #[structopt(name = "no-merges", long)] + /// don't show merge commits + flag_no_merges: bool, + #[structopt(name = "no-min-parents", long)] + /// don't require a minimum number of parents + flag_no_min_parents: bool, + #[structopt(name = "no-max-parents", long)] + /// don't require a maximum number of parents + flag_no_max_parents: bool, + #[structopt(name = "max-parents")] + /// specify a maximum number of parents for a commit + flag_max_parents: Option<usize>, + #[structopt(name = "min-parents")] + /// specify a minimum number of parents for a commit + flag_min_parents: Option<usize>, + #[structopt(name = "patch", long, short)] + /// show commit diff + flag_patch: bool, + #[structopt(name = "commit")] + arg_commit: Vec<String>, + #[structopt(name = "spec", last = true)] + arg_spec: Vec<String>, +} + +fn run(args: &Args) -> Result<(), Error> { + let path = args.flag_git_dir.as_ref().map(|s| &s[..]).unwrap_or("."); + let repo = Repository::open(path)?; + let mut revwalk = repo.revwalk()?; + + // Prepare the revwalk based on CLI parameters + let base = if args.flag_reverse { + git2::Sort::REVERSE + } else { + git2::Sort::NONE + }; + revwalk.set_sorting( + base | if args.flag_topo_order { + git2::Sort::TOPOLOGICAL + } else if args.flag_date_order { + git2::Sort::TIME + } else { + git2::Sort::NONE + }, + )?; + for commit in &args.arg_commit { + if commit.starts_with('^') { + let obj = repo.revparse_single(&commit[1..])?; + revwalk.hide(obj.id())?; + continue; + } + let revspec = repo.revparse(commit)?; + if revspec.mode().contains(git2::RevparseMode::SINGLE) { + revwalk.push(revspec.from().unwrap().id())?; + } else { + let from = revspec.from().unwrap().id(); + let to = revspec.to().unwrap().id(); + revwalk.push(to)?; + if revspec.mode().contains(git2::RevparseMode::MERGE_BASE) { + let base = repo.merge_base(from, to)?; + let o = repo.find_object(base, Some(ObjectType::Commit))?; + revwalk.push(o.id())?; + } + revwalk.hide(from)?; + } + } + if args.arg_commit.is_empty() { + revwalk.push_head()?; + } + + // Prepare our diff options and pathspec matcher + let (mut diffopts, mut diffopts2) = (DiffOptions::new(), DiffOptions::new()); + for spec in &args.arg_spec { + diffopts.pathspec(spec); + diffopts2.pathspec(spec); + } + let ps = Pathspec::new(args.arg_spec.iter())?; + + // Filter our revwalk based on the CLI parameters + macro_rules! filter_try { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => return Some(Err(e)), + } + }; + } + let revwalk = revwalk + .filter_map(|id| { + let id = filter_try!(id); + let commit = filter_try!(repo.find_commit(id)); + let parents = commit.parents().len(); + if parents < args.min_parents() { + return None; + } + if let Some(n) = args.max_parents() { + if parents >= n { + return None; + } + } + if !args.arg_spec.is_empty() { + match commit.parents().len() { + 0 => { + let tree = filter_try!(commit.tree()); + let flags = git2::PathspecFlags::NO_MATCH_ERROR; + if ps.match_tree(&tree, flags).is_err() { + return None; + } + } + _ => { + let m = commit.parents().all(|parent| { + match_with_parent(&repo, &commit, &parent, &mut diffopts) + .unwrap_or(false) + }); + if !m { + return None; + } + } + } + } + if !sig_matches(&commit.author(), &args.flag_author) { + return None; + } + if !sig_matches(&commit.committer(), &args.flag_committer) { + return None; + } + if !log_message_matches(commit.message(), &args.flag_grep) { + return None; + } + Some(Ok(commit)) + }) + .skip(args.flag_skip.unwrap_or(0)) + .take(args.flag_max_count.unwrap_or(!0)); + + // print! + for commit in revwalk { + let commit = commit?; + print_commit(&commit); + if !args.flag_patch || commit.parents().len() > 1 { + continue; + } + let a = if commit.parents().len() == 1 { + let parent = commit.parent(0)?; + Some(parent.tree()?) + } else { + None + }; + let b = commit.tree()?; + let diff = repo.diff_tree_to_tree(a.as_ref(), Some(&b), Some(&mut diffopts2))?; + diff.print(DiffFormat::Patch, |_delta, _hunk, line| { + match line.origin() { + ' ' | '+' | '-' => print!("{}", line.origin()), + _ => {} + } + print!("{}", str::from_utf8(line.content()).unwrap()); + true + })?; + } + + Ok(()) +} + +fn sig_matches(sig: &Signature, arg: &Option<String>) -> bool { + match *arg { + Some(ref s) => { + sig.name().map(|n| n.contains(s)).unwrap_or(false) + || sig.email().map(|n| n.contains(s)).unwrap_or(false) + } + None => true, + } +} + +fn log_message_matches(msg: Option<&str>, grep: &Option<String>) -> bool { + match (grep, msg) { + (&None, _) => true, + (&Some(_), None) => false, + (&Some(ref s), Some(msg)) => msg.contains(s), + } +} + +fn print_commit(commit: &Commit) { + println!("commit {}", commit.id()); + + if commit.parents().len() > 1 { + print!("Merge:"); + for id in commit.parent_ids() { + print!(" {:.8}", id); + } + println!(); + } + + let author = commit.author(); + println!("Author: {}", author); + print_time(&author.when(), "Date: "); + println!(); + + for line in String::from_utf8_lossy(commit.message_bytes()).lines() { + println!(" {}", line); + } + println!(); +} + +fn print_time(time: &Time, prefix: &str) { + let (offset, sign) = match time.offset_minutes() { + n if n < 0 => (-n, '-'), + n => (n, '+'), + }; + let (hours, minutes) = (offset / 60, offset % 60); + let ts = time::Timespec::new(time.seconds() + (time.offset_minutes() as i64) * 60, 0); + let time = time::at(ts); + + println!( + "{}{} {}{:02}{:02}", + prefix, + time.strftime("%a %b %e %T %Y").unwrap(), + sign, + hours, + minutes + ); +} + +fn match_with_parent( + repo: &Repository, + commit: &Commit, + parent: &Commit, + opts: &mut DiffOptions, +) -> Result<bool, Error> { + let a = parent.tree()?; + let b = commit.tree()?; + let diff = repo.diff_tree_to_tree(Some(&a), Some(&b), Some(opts))?; + Ok(diff.deltas().len() > 0) +} + +impl Args { + fn min_parents(&self) -> usize { + if self.flag_no_min_parents { + return 0; + } + self.flag_min_parents + .unwrap_or(if self.flag_merges { 2 } else { 0 }) + } + + fn max_parents(&self) -> Option<usize> { + if self.flag_no_max_parents { + return None; + } + self.flag_max_parents + .or(if self.flag_no_merges { Some(1) } else { None }) + } +} + +fn main() { + let args = Args::from_args(); + match run(&args) { + Ok(()) => {} + Err(e) => println!("error: {}", e), + } +} |