specfold_core/workspace/vcs/
refs.rs1use super::repo::git;
4use std::path::Path;
5use std::process::{Command, Stdio};
6
7pub fn head(root: &Path) -> Option<String> {
9 let out = git(root, &["rev-parse", "HEAD"])?;
10 Some(String::from_utf8_lossy(&out).trim().to_string())
11}
12
13pub fn resolve(root: &Path, rev: &str) -> Option<String> {
15 let out = git(root, &["rev-parse", "--verify", "--quiet", &format!("{rev}^{{commit}}")])?;
16 let s = String::from_utf8_lossy(&out).trim().to_string();
17 if s.is_empty() { None } else { Some(s) }
18}
19
20const EMPTY_TREE: &str = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
22
23pub fn parent_or_empty(root: &Path, sha: &str) -> String {
25 match resolve(root, &format!("{sha}^")) {
26 Some(_) => format!("{sha}^"),
27 None => EMPTY_TREE.to_string(),
28 }
29}
30
31pub fn commit_diff(root: &Path, sha: &str) -> String {
33 format!("{}..{sha}", parent_or_empty(root, sha))
34}
35
36pub fn commit_log(root: &Path, sha: &str) -> String {
38 match resolve(root, &format!("{sha}^")) {
39 Some(_) => format!("{sha}^..{sha}"),
40 None => sha.to_string(),
41 }
42}
43
44pub fn upstream(root: &Path) -> Option<String> {
46 let out = git(root, &["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"])?;
47 let s = String::from_utf8(out).ok()?.trim().to_string();
48 if s.is_empty() { None } else { Some(s) }
49}
50
51pub fn is_ancestor_of_head(root: &Path, rev: &str) -> Option<bool> {
53 let status = Command::new("git")
54 .arg("-C")
55 .arg(root)
56 .args(["merge-base", "--is-ancestor", rev, "HEAD"])
57 .stderr(Stdio::null())
58 .status()
59 .ok()?;
60 match status.code() {
61 Some(0) => Some(true),
62 Some(1) => Some(false),
63 _ => None,
64 }
65}