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