1use std::path::{Path, PathBuf};
2use std::process::Command;
3
4use crate::error::{Error, Result};
5
6pub fn run(args: &[&str]) -> Result<String> {
12 let output = Command::new("git")
13 .args(args)
14 .output()
15 .map_err(|e| Error::GitCommandFailed(format!("failed to execute git: {e}")))?;
16
17 if output.status.success() {
18 Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
19 } else {
20 let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
21 Err(Error::GitCommandFailed(stderr))
22 }
23}
24
25pub fn run_with_paths(git_dir: &Path, work_tree: &Path, args: &[&str]) -> Result<String> {
31 let output = Command::new("git")
32 .current_dir(work_tree)
33 .env("GIT_DIR", git_dir)
34 .env("GIT_WORK_TREE", work_tree)
35 .env_remove("GIT_INDEX_FILE")
37 .env_remove("GIT_OBJECT_DIRECTORY")
38 .env_remove("GIT_ALTERNATE_OBJECT_DIRECTORIES")
39 .args(args)
40 .output()
41 .map_err(|e| Error::GitCommandFailed(format!("failed to execute git: {e}")))?;
42
43 if output.status.success() {
44 Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
45 } else {
46 let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
47 Err(Error::GitCommandFailed(stderr))
48 }
49}
50
51#[must_use]
53pub fn is_in_repo() -> bool {
54 run(&["rev-parse", "--is-inside-work-tree"])
55 .is_ok_and(|s| s == "true")
56}
57
58pub fn repo_root() -> Result<PathBuf> {
64 if !is_in_repo() {
65 return Err(Error::NotInGitRepo);
66 }
67 let root = run(&["rev-parse", "--show-toplevel"])?;
68 Ok(PathBuf::from(root))
69}
70
71pub fn git_dir() -> Result<PathBuf> {
77 if !is_in_repo() {
78 return Err(Error::NotInGitRepo);
79 }
80 let dir = run(&["rev-parse", "--git-dir"])?;
81 Ok(PathBuf::from(dir))
82}
83
84pub fn initial_commit_sha() -> Result<String> {
90 if !is_in_repo() {
91 return Err(Error::NotInGitRepo);
92 }
93 let sha = run(&["rev-list", "--max-parents=0", "HEAD"])
94 .map_err(|_| Error::NoCommits)?;
95
96 Ok(sha.lines().next().unwrap_or(&sha).to_string())
98}
99
100pub fn last_commit_message() -> Result<String> {
106 run(&["log", "-1", "--format=%B"])
107}