1use std::io;
2use std::path::Path;
3use std::process::{Command, Stdio};
4
5pub fn is_git_repo(path: &Path) -> bool {
6 Command::new("git")
7 .arg("-C")
8 .arg(path)
9 .arg("rev-parse")
10 .arg("--is-inside-work-tree")
11 .stdout(Stdio::null())
12 .stderr(Stdio::null())
13 .status()
14 .map(|s| s.success())
15 .unwrap_or(false)
16}
17
18pub fn create_ghost_snapshot(repo_path: &Path) -> io::Result<()> {
21 let add_status = Command::new("git")
23 .arg("-C")
24 .arg(repo_path)
25 .arg("add")
26 .arg("-A")
27 .stdout(Stdio::null())
28 .stderr(Stdio::null())
29 .status()?;
30
31 if !add_status.success() {
32 return Err(io::Error::new(io::ErrorKind::Other, "Git add failed"));
33 }
34
35 let tree_output = Command::new("git")
37 .arg("-C")
38 .arg(repo_path)
39 .arg("write-tree")
40 .stderr(Stdio::null())
41 .output()?;
42
43 if !tree_output.status.success() {
44 return Err(io::Error::new(
45 io::ErrorKind::Other,
46 "Git write-tree failed",
47 ));
48 }
49 let tree_sha = String::from_utf8_lossy(&tree_output.stdout)
50 .trim()
51 .to_string();
52
53 let commit_output = Command::new("git")
55 .arg("-C")
56 .arg(repo_path)
57 .arg("commit-tree")
58 .arg(&tree_sha)
59 .arg("-p")
60 .arg("HEAD")
61 .arg("-m")
62 .arg("Hematite Ghost Snapshot")
63 .stderr(Stdio::null())
64 .output()?;
65
66 if !commit_output.status.success() {
67 return Err(io::Error::new(
68 io::ErrorKind::Other,
69 "Git commit-tree failed",
70 ));
71 }
72 let commit_sha = String::from_utf8_lossy(&commit_output.stdout)
73 .trim()
74 .to_string();
75
76 let update_status = Command::new("git")
78 .arg("-C")
79 .arg(repo_path)
80 .arg("update-ref")
81 .arg("refs/hematite/ghost")
82 .arg(&commit_sha)
83 .stdout(Stdio::null())
84 .stderr(Stdio::null())
85 .status()?;
86
87 if !update_status.success() {
88 return Err(io::Error::new(
89 io::ErrorKind::Other,
90 "Git update-ref failed",
91 ));
92 }
93
94 Ok(())
95}
96
97pub fn revert_from_ghost(repo_path: &Path, file_path: &str) -> io::Result<String> {
99 let status = Command::new("git")
100 .arg("-C")
101 .arg(repo_path)
102 .arg("checkout")
103 .arg("refs/hematite/ghost")
104 .arg("--")
105 .arg(file_path)
106 .stdout(Stdio::null())
107 .stderr(Stdio::null())
108 .status()?;
109
110 if !status.success() {
111 return Err(io::Error::new(
112 io::ErrorKind::Other,
113 "Git checkout from ghost ref failed",
114 ));
115 }
116
117 Ok(format!("Restored {} from Git Ghost ref", file_path))
118}
119
120pub fn get_active_branch(repo_path: &Path) -> io::Result<String> {
121 let output = Command::new("git")
122 .arg("-C")
123 .arg(repo_path)
124 .arg("rev-parse")
125 .arg("--abbrev-ref")
126 .arg("HEAD")
127 .stderr(Stdio::null())
128 .output()?;
129 if !output.status.success() {
130 return Err(io::Error::new(io::ErrorKind::Other, "Git rev-parse failed"));
131 }
132 Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
133}