deepseek_rust_cli/tools/
git_ops.rs1use anyhow::Result;
2use tokio::process::Command;
3
4pub async fn git_status(path: Option<&str>) -> Result<String> {
7 let p = path.unwrap_or(".");
8 let output = Command::new("git")
9 .arg("-C")
10 .arg(p)
11 .arg("status")
12 .arg("-s")
13 .output()
14 .await?;
15
16 if !output.status.success() {
17 return Ok(format!(
18 "Git error: {}",
19 String::from_utf8_lossy(&output.stderr)
20 ));
21 }
22
23 let out = String::from_utf8_lossy(&output.stdout).to_string();
24 if out.is_empty() {
25 Ok("Working tree clean.".to_string())
26 } else {
27 Ok(out)
28 }
29}
30
31pub async fn git_diff(path: Option<&str>, staged: bool) -> Result<String> {
32 let p = path.unwrap_or(".");
33 let mut cmd = Command::new("git");
34 cmd.arg("-C").arg(p).arg("diff");
35 if staged {
36 cmd.arg("--staged");
37 }
38 let output = cmd.output().await?;
39
40 if !output.status.success() {
41 return Ok(format!(
42 "Git error: {}",
43 String::from_utf8_lossy(&output.stderr)
44 ));
45 }
46
47 let out = String::from_utf8_lossy(&output.stdout).to_string();
48 if out.is_empty() {
49 Ok("No changes.".to_string())
50 } else {
51 Ok(out)
52 }
53}
54
55pub async fn git_log(path: Option<&str>, count: Option<usize>) -> Result<String> {
56 let p = path.unwrap_or(".");
57 let n = count.unwrap_or(10);
58 let output = Command::new("git")
59 .arg("-C")
60 .arg(p)
61 .arg("log")
62 .arg(format!("-n{}", n))
63 .arg("--oneline")
64 .arg("--decorate")
65 .output()
66 .await?;
67
68 if !output.status.success() {
69 return Ok(format!(
70 "Git error: {}",
71 String::from_utf8_lossy(&output.stderr)
72 ));
73 }
74 Ok(String::from_utf8_lossy(&output.stdout).to_string())
75}
76
77pub async fn git_branch(
78 path: Option<&str>,
79 action: Option<&str>,
80 name: Option<&str>,
81) -> Result<String> {
82 let p = path.unwrap_or(".");
83 let mut cmd = Command::new("git");
84 cmd.arg("-C").arg(p);
85
86 match action {
87 Some("create") => {
88 if let Some(n) = name {
89 cmd.arg("branch").arg(n);
90 let output = cmd.output().await?;
91 if !output.status.success() {
92 return Ok(format!(
93 "Git error: {}",
94 String::from_utf8_lossy(&output.stderr)
95 ));
96 }
97 return Ok(format!("Branch '{}' created.", n));
98 }
99 }
100 Some("delete") => {
101 if let Some(n) = name {
102 cmd.arg("branch").arg("-d").arg(n);
103 let output = cmd.output().await?;
104 if !output.status.success() {
105 return Ok(format!(
106 "Git error: {}",
107 String::from_utf8_lossy(&output.stderr)
108 ));
109 }
110 return Ok(format!("Branch '{}' deleted.", n));
111 }
112 }
113 Some("switch") => {
114 if let Some(n) = name {
115 cmd.arg("checkout").arg(n);
116 let output = cmd.output().await?;
117 if !output.status.success() {
118 return Ok(format!(
119 "Git error: {}",
120 String::from_utf8_lossy(&output.stderr)
121 ));
122 }
123 return Ok(format!("Switched to branch '{}'.", n));
124 }
125 }
126 _ => {
127 cmd.arg("branch").arg("--list");
129 let output = cmd.output().await?;
130 if !output.status.success() {
131 return Ok(format!(
132 "Git error: {}",
133 String::from_utf8_lossy(&output.stderr)
134 ));
135 }
136 return Ok(String::from_utf8_lossy(&output.stdout).to_string());
137 }
138 }
139
140 Ok("No action specified.".to_string())
141}
142
143pub async fn git_add(path: Option<&str>, files: Option<&str>) -> Result<String> {
144 let p = path.unwrap_or(".");
145 let targets = files.unwrap_or(".");
146 let output = Command::new("git")
147 .arg("-C")
148 .arg(p)
149 .arg("add")
150 .arg(targets)
151 .output()
152 .await?;
153
154 if !output.status.success() {
155 return Ok(format!(
156 "Git error: {}",
157 String::from_utf8_lossy(&output.stderr)
158 ));
159 }
160 Ok(format!("Staged: {}", targets))
161}
162
163pub async fn git_commit(path: Option<&str>, message: &str) -> Result<String> {
164 let p = path.unwrap_or(".");
165 let output = Command::new("git")
166 .arg("-C")
167 .arg(p)
168 .arg("commit")
169 .arg("-m")
170 .arg(message)
171 .output()
172 .await?;
173
174 if !output.status.success() {
175 return Ok(format!(
176 "Git error: {}",
177 String::from_utf8_lossy(&output.stderr)
178 ));
179 }
180 let out = String::from_utf8_lossy(&output.stdout).to_string();
181 Ok(if out.is_empty() {
182 "Committed successfully.".to_string()
183 } else {
184 out
185 })
186}
187
188pub async fn git_push(
189 path: Option<&str>,
190 remote: Option<&str>,
191 branch: Option<&str>,
192) -> Result<String> {
193 let p = path.unwrap_or(".");
194 let r = remote.unwrap_or("origin");
195 let mut cmd = Command::new("git");
196 cmd.arg("-C").arg(p).arg("push").arg(r);
197 if let Some(b) = branch {
198 cmd.arg(b);
199 }
200
201 let output = cmd.output().await?;
202 if !output.status.success() {
203 return Ok(format!(
204 "Git error: {}",
205 String::from_utf8_lossy(&output.stderr)
206 ));
207 }
208 let out = String::from_utf8_lossy(&output.stdout).to_string();
209 let err = String::from_utf8_lossy(&output.stderr).to_string();
210 Ok(format!("{}{}", out, err))
211}
212
213pub async fn git_pull(
214 path: Option<&str>,
215 remote: Option<&str>,
216 branch: Option<&str>,
217) -> Result<String> {
218 let p = path.unwrap_or(".");
219 let r = remote.unwrap_or("origin");
220 let mut cmd = Command::new("git");
221 cmd.arg("-C").arg(p).arg("pull").arg(r);
222 if let Some(b) = branch {
223 cmd.arg(b);
224 }
225
226 let output = cmd.output().await?;
227 if !output.status.success() {
228 return Ok(format!(
229 "Git error: {}",
230 String::from_utf8_lossy(&output.stderr)
231 ));
232 }
233 let out = String::from_utf8_lossy(&output.stdout).to_string();
234 let err = String::from_utf8_lossy(&output.stderr).to_string();
235 Ok(format!("{}{}", out, err))
236}
237
238pub async fn git_checkout(path: Option<&str>, target: &str) -> Result<String> {
239 let p = path.unwrap_or(".");
240 let output = Command::new("git")
241 .arg("-C")
242 .arg(p)
243 .arg("checkout")
244 .arg(target)
245 .output()
246 .await?;
247
248 if !output.status.success() {
249 return Ok(format!(
250 "Git error: {}",
251 String::from_utf8_lossy(&output.stderr)
252 ));
253 }
254 Ok(format!("Checked out: {}", target))
255}
256
257pub async fn git_clone(url: &str, dest: Option<&str>) -> Result<String> {
258 let mut cmd = Command::new("git");
259 cmd.arg("clone").arg(url);
260 if let Some(d) = dest {
261 cmd.arg(d);
262 }
263
264 let output = cmd.output().await?;
265 if !output.status.success() {
266 return Ok(format!(
267 "Git error: {}",
268 String::from_utf8_lossy(&output.stderr)
269 ));
270 }
271 let out = String::from_utf8_lossy(&output.stdout).to_string();
272 let err = String::from_utf8_lossy(&output.stderr).to_string();
273 Ok(format!("{}{}", out, err))
274}
275
276pub async fn git_remote_list(path: Option<&str>) -> Result<String> {
277 let p = path.unwrap_or(".");
278 let output = Command::new("git")
279 .arg("-C")
280 .arg(p)
281 .arg("remote")
282 .arg("-v")
283 .output()
284 .await?;
285
286 if !output.status.success() {
287 return Ok(format!(
288 "Git error: {}",
289 String::from_utf8_lossy(&output.stderr)
290 ));
291 }
292 Ok(String::from_utf8_lossy(&output.stdout).to_string())
293}
294
295pub async fn git_stash(path: Option<&str>, action: Option<&str>) -> Result<String> {
296 let p = path.unwrap_or(".");
297 let mut cmd = Command::new("git");
298 cmd.arg("-C").arg(p).arg("stash");
299
300 match action {
301 Some("pop") => {
302 cmd.arg("pop");
303 }
304 Some("list") => {
305 cmd.arg("list");
306 }
307 _ => {} }
309
310 let output = cmd.output().await?;
311 if !output.status.success() {
312 return Ok(format!(
313 "Git error: {}",
314 String::from_utf8_lossy(&output.stderr)
315 ));
316 }
317 Ok(String::from_utf8_lossy(&output.stdout).to_string())
318}