Skip to main content

anodizer_core/git/
status.rs

1use anyhow::{Result, bail};
2use std::process::Command;
3
4use super::git_output;
5
6/// Check whether the working tree has uncommitted changes.
7pub fn is_git_dirty() -> bool {
8    git_output(&["status", "--porcelain"])
9        .map(|s| !s.is_empty())
10        .unwrap_or(false)
11}
12
13/// Read `git config user.name`, or `None` if unset / git is unavailable.
14pub fn local_git_user_name() -> Option<String> {
15    git_output(&["config", "user.name"])
16        .ok()
17        .filter(|s| !s.is_empty())
18}
19
20/// Read `git config user.email`, or `None` if unset / git is unavailable.
21pub fn local_git_user_email() -> Option<String> {
22    git_output(&["config", "user.email"])
23        .ok()
24        .filter(|s| !s.is_empty())
25}
26
27/// Check whether `git` is available in PATH.
28pub fn check_git_available() -> Result<()> {
29    let output = Command::new("git").arg("--version").output();
30    match output {
31        Ok(o) if o.status.success() => Ok(()),
32        _ => bail!("git is not installed or not in PATH. Install git and try again."),
33    }
34}
35
36/// Check whether the current directory is inside a git repository.
37pub fn is_git_repo() -> bool {
38    git_output(&["rev-parse", "--git-dir"]).is_ok()
39}
40
41/// Return the `git status --porcelain` output showing dirty files.
42pub fn git_status_porcelain() -> String {
43    git_output(&["status", "--porcelain"]).unwrap_or_default()
44}
45
46/// Check whether the current repository is a shallow clone.
47///
48/// Returns `true` if the `.git/shallow` sentinel file exists, which git creates
49/// when a repository was cloned with `--depth`.
50pub fn is_shallow_clone() -> bool {
51    // Use `git rev-parse --git-dir` to find the actual .git directory,
52    // which handles worktrees and non-standard layouts.
53    let git_dir = git_output(&["rev-parse", "--git-dir"]).unwrap_or_else(|_| ".git".to_string());
54    std::path::Path::new(&git_dir).join("shallow").exists()
55}