summaryrefslogtreecommitdiffstats
path: root/vendor/git2/examples/log.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:35 +0000
commit7e5d7eea9c580ef4b41a765bde624af431942b96 (patch)
tree2c0d9ca12878fc4525650aa4e54d77a81a07cc09 /vendor/git2/examples/log.rs
parentAdding debian version 1.70.0+dfsg1-9. (diff)
downloadrustc-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.rs310
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),
+ }
+}