gitoxide_core/repository/
commit.rs1use std::{io::Write, process::Stdio};
2
3use anyhow::{anyhow, bail, Context, Result};
4
5pub fn verify(repo: gix::Repository, rev_spec: Option<&str>) -> Result<()> {
9 let rev_spec = rev_spec.unwrap_or("HEAD");
10 let commit = repo
11 .rev_parse_single(format!("{rev_spec}^{{commit}}").as_str())?
12 .object()?
13 .into_commit();
14 let (signature, signed_data) = commit
15 .signature()
16 .context("Could not parse commit to obtain signature")?
17 .ok_or_else(|| anyhow!("Commit at {rev_spec} is not signed"))?;
18
19 let mut signature_storage = tempfile::NamedTempFile::new()?;
20 signature_storage.write_all(signature.as_ref())?;
21 let signed_storage = signature_storage.into_temp_path();
22
23 let mut cmd: std::process::Command = gix::command::prepare("gpg").into();
24 cmd.args(["--keyid-format=long", "--status-fd=1", "--verify"])
25 .arg(&signed_storage)
26 .arg("-")
27 .stdin(Stdio::piped());
28 gix::trace::debug!("About to execute {cmd:?}");
29 let mut child = cmd.spawn()?;
30 child
31 .stdin
32 .take()
33 .expect("configured")
34 .write_all(signed_data.to_bstring().as_ref())?;
35
36 if !child.wait()?.success() {
37 bail!("Command {cmd:?} failed");
38 }
39 Ok(())
40}
41
42pub fn describe(
43 mut repo: gix::Repository,
44 rev_spec: Option<&str>,
45 mut out: impl std::io::Write,
46 mut err: impl std::io::Write,
47 describe::Options {
48 all_tags,
49 all_refs,
50 first_parent,
51 always,
52 statistics,
53 max_candidates,
54 long_format,
55 dirty_suffix,
56 }: describe::Options,
57) -> Result<()> {
58 repo.object_cache_size_if_unset(4 * 1024 * 1024);
59 let commit = match rev_spec {
60 Some(spec) => repo.rev_parse_single(spec)?.object()?.try_into_commit()?,
61 None => repo.head_commit()?,
62 };
63 use gix::commit::describe::SelectRef::*;
64 let select_ref = if all_refs {
65 AllRefs
66 } else if all_tags {
67 AllTags
68 } else {
69 Default::default()
70 };
71 let resolution = commit
72 .describe()
73 .names(select_ref)
74 .traverse_first_parent(first_parent)
75 .id_as_fallback(always)
76 .max_candidates(max_candidates)
77 .try_resolve()?
78 .with_context(|| format!("Did not find a single candidate ref for naming id '{}'", commit.id))?;
79
80 if statistics {
81 writeln!(err, "traversed {} commits", resolution.outcome.commits_seen)?;
82 }
83
84 let mut describe_id = resolution.format_with_dirty_suffix(dirty_suffix)?;
85 describe_id.long(long_format);
86
87 writeln!(out, "{describe_id}")?;
88 Ok(())
89}
90
91pub mod describe {
92 #[derive(Debug, Clone)]
93 pub struct Options {
94 pub all_tags: bool,
95 pub all_refs: bool,
96 pub first_parent: bool,
97 pub always: bool,
98 pub long_format: bool,
99 pub statistics: bool,
100 pub max_candidates: usize,
101 pub dirty_suffix: Option<String>,
102 }
103}