Skip to main content

caixa_resolver/
git.rs

1//! Git helpers — shells out to the `git` CLI (matches `net.git-fetch-with-cli`).
2
3use std::path::{Path, PathBuf};
4use std::process::Command;
5
6use thiserror::Error;
7
8#[derive(Debug, Error)]
9pub enum GitError {
10    #[error("git command failed: {0}")]
11    Command(String),
12    #[error("io: {0}")]
13    Io(#[from] std::io::Error),
14}
15
16/// Clone `url` into `dest` if empty; otherwise `git fetch` in place.
17pub fn clone_or_fetch(url: &str, dest: &Path) -> Result<(), GitError> {
18    if dest.exists() && dest.join(".git").exists() {
19        run_git(dest, ["fetch", "--tags", "--prune", "--force"])?;
20        Ok(())
21    } else {
22        if let Some(parent) = dest.parent() {
23            std::fs::create_dir_all(parent)?;
24        }
25        run_git(
26            &PathBuf::from("."),
27            [
28                "clone",
29                "--quiet",
30                "--no-checkout",
31                url,
32                &dest.to_string_lossy(),
33            ],
34        )?;
35        Ok(())
36    }
37}
38
39/// Check out a specific ref in `repo` — accepts tag / branch / sha.
40pub fn checkout(repo: &Path, gitref: &str) -> Result<(), GitError> {
41    run_git(repo, ["checkout", "--quiet", "--detach", gitref])
42}
43
44/// Return the current HEAD SHA of `repo`.
45pub fn head_sha(repo: &Path) -> Result<String, GitError> {
46    let out = Command::new("git")
47        .arg("-C")
48        .arg(repo)
49        .args(["rev-parse", "HEAD"])
50        .output()?;
51    if !out.status.success() {
52        return Err(GitError::Command(
53            String::from_utf8_lossy(&out.stderr).into_owned(),
54        ));
55    }
56    Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
57}
58
59fn run_git<'a, A>(cwd: &Path, args: A) -> Result<(), GitError>
60where
61    A: IntoIterator<Item = &'a str>,
62{
63    let out = Command::new("git").current_dir(cwd).args(args).output()?;
64    if out.status.success() {
65        Ok(())
66    } else {
67        Err(GitError::Command(
68            String::from_utf8_lossy(&out.stderr).into_owned(),
69        ))
70    }
71}