Skip to main content

wsx_core/git/
ops.rs

1// Git operations: pull, push, rebase, merge
2
3use super::{git_cmd, info::current_branch};
4use anyhow::{bail, Result};
5use std::path::Path;
6
7fn run(cmd: &mut std::process::Command) -> Result<String> {
8    let out = super::output_with_timeout(cmd, std::time::Duration::from_secs(30))?;
9    let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
10    let stderr = String::from_utf8_lossy(&out.stderr).trim().to_string();
11    if out.status.success() {
12        Ok(if stdout.is_empty() { stderr } else { stdout })
13    } else {
14        let msg = if !stderr.is_empty() { stderr } else { stdout };
15        bail!("{}", msg.lines().next().unwrap_or("git error"))
16    }
17}
18
19pub fn pull(path: &Path) -> Result<String> {
20    run(git_cmd(path).args(["pull"]))
21}
22
23pub fn push(path: &Path) -> Result<String> {
24    let result = run(git_cmd(path).args(["push"]));
25    match result {
26        Ok(s) => Ok(s),
27        Err(e) => {
28            let msg = e.to_string();
29            if msg.contains("no upstream") || msg.contains("--set-upstream") {
30                let branch = current_branch(path).unwrap_or_else(|| "HEAD".to_string());
31                run(git_cmd(path).args(["push", "-u", "origin", &branch]))
32            } else {
33                Err(e)
34            }
35        }
36    }
37}
38
39pub fn pull_rebase(path: &Path, branch: &str) -> Result<String> {
40    run(git_cmd(path).args(["pull", "--rebase", "origin", branch]))
41}
42
43pub fn merge_from(path: &Path, source: &str) -> Result<String> {
44    run(git_cmd(path).args(["merge", source]))
45}
46
47pub fn merge_into(path: &Path, target: &str) -> Result<String> {
48    let current = current_branch(path).ok_or_else(|| anyhow::anyhow!("not on a branch"))?;
49    // checkout target
50    run(git_cmd(path).args(["checkout", target]))?;
51    // merge current into target; on failure, checkout back
52    let merge_result = run(git_cmd(path).args(["merge", &current]));
53    // ! must always return to original branch regardless of merge outcome
54    run(git_cmd(path).args(["checkout", &current]))?;
55    merge_result.map(|_| {
56        format!(
57            "Merged {} into {}, returned to {}",
58            current, target, current
59        )
60    })
61}