tracel_xtask/utils/
git.rs1use std::{
2 path::PathBuf,
3 process::{Command, Stdio},
4};
5
6use anyhow::Context;
7
8pub fn git_repo_root_or_cwd() -> anyhow::Result<PathBuf> {
9 let out = Command::new("git")
10 .arg("rev-parse")
11 .arg("--show-toplevel")
12 .stdout(Stdio::piped())
13 .stderr(Stdio::null())
14 .output();
15
16 match out {
17 Ok(o) if o.status.success() => {
18 let s = String::from_utf8(o.stdout).context("utf8 git rev-parse output")?;
19 Ok(PathBuf::from(s.trim()))
20 }
21 _ => std::env::current_dir().context("getting current directory"),
22 }
23}
24
25pub fn git_current_commit_hash(len: Option<usize>) -> anyhow::Result<String> {
27 let cwd = git_repo_root_or_cwd()?;
28 let out = Command::new("git")
29 .arg("rev-parse")
30 .arg("--verify")
31 .arg("HEAD")
32 .current_dir(&cwd)
33 .stdout(Stdio::piped())
34 .stderr(Stdio::piped())
35 .output()
36 .context("running `git rev-parse --verify HEAD`")?;
37
38 if !out.status.success() {
39 anyhow::bail!(
40 "git rev-parse failed: {}",
41 String::from_utf8_lossy(&out.stderr).trim()
42 );
43 }
44
45 let s = String::from_utf8(out.stdout).context("utf8 git rev-parse output")?;
46 let sha = s.trim().to_string();
47 if sha.len() != 40 || !sha.chars().all(|c| c.is_ascii_hexdigit()) {
48 anyhow::bail!("unexpected commit hash format: {sha}");
49 }
50
51 let max_len = 40;
52 let final_len = len.map_or(max_len, |n| n.min(max_len));
53
54 Ok(sha[..final_len].to_string())
55}
56
57pub fn git_is_repo_dirty() -> anyhow::Result<bool> {
59 let cwd = git_repo_root_or_cwd()?;
60 let out = Command::new("git")
61 .arg("status")
62 .arg("--porcelain") .current_dir(&cwd)
64 .stdout(Stdio::piped())
65 .stderr(Stdio::piped())
66 .output()
67 .context("running `git status --porcelain`")?;
68
69 if !out.status.success() {
70 anyhow::bail!(
71 "git status failed: {}",
72 String::from_utf8_lossy(&out.stderr).trim()
73 );
74 }
75
76 let s = String::from_utf8(out.stdout).context("utf8 git status output")?;
77 Ok(!s.trim().is_empty())
78}