Skip to main content

ambient_ci/
git.rs

1//! Utility functions for git operations.
2
3use std::{path::Path, process::Command};
4
5use clingwrap::runner::{CommandError, CommandRunner};
6
7/// Is a directory a git checkout?
8pub fn is_git(dirname: &Path) -> bool {
9    dirname.exists() && git_head(dirname).is_ok()
10}
11
12/// Return commit id for HEAD in a git checkout.
13pub fn git_head(dirname: &Path) -> Result<String, GitError> {
14    let stdout = git(&["rev-parse", "HEAD"], dirname)?;
15    let head = stdout.trim().into();
16    Ok(head)
17}
18
19/// Is the git checkout clean?
20pub fn git_is_clean(dirname: &Path) -> bool {
21    if let Ok(stdout) = git(&["status", "--short"], dirname) {
22        stdout.is_empty()
23    } else {
24        false // some error happened, assume checkout is not clean
25    }
26}
27
28// Run git command in a directory; if successful, return stdout as text.
29fn git(args: &[&str], dirname: &Path) -> Result<String, GitError> {
30    let mut cmd = Command::new("git");
31    cmd.args(args).current_dir(dirname);
32
33    let mut runner = CommandRunner::new(cmd);
34    runner.capture_stdout();
35    runner.capture_stderr();
36    runner
37        .execute()
38        .map_err(GitError::Execute)
39        .map(|output| String::from_utf8_lossy(&output.stdout).into())
40}
41
42/// Possible errors from git operations.
43#[derive(Debug, thiserror::Error)]
44pub enum GitError {
45    /// Can't run git.
46    #[error("failed to run git")]
47    Execute(#[source] CommandError),
48}