thoughts_tool/git/
shell_push.rs

1use anyhow::{Context, Result, bail};
2use std::io::{BufRead, BufReader};
3use std::path::Path;
4use std::process::{Command, Stdio};
5
6pub fn build_push_command(repo_path: &Path, remote: &str, branch: &str) -> Command {
7    let mut cmd = Command::new("git");
8    cmd.current_dir(repo_path)
9        .arg("push")
10        .arg("--progress")
11        .arg(remote)
12        .arg(format!("HEAD:refs/heads/{branch}"));
13    cmd
14}
15
16fn print_progress_line(line: &str) {
17    if line.starts_with("To ")
18        || line.starts_with("Everything up-to-date")
19        || line.contains('%')
20        || line.starts_with("remote:")
21        || line.contains("Counting objects")
22    {
23        println!("    {}", line);
24    }
25}
26
27pub fn push_current_branch(repo_path: &Path, remote: &str, branch: &str) -> Result<()> {
28    which::which("git").context("git executable not found in PATH")?;
29
30    let mut cmd = build_push_command(repo_path, remote, branch);
31    let mut child = cmd
32        .stdout(Stdio::null())
33        .stderr(Stdio::piped())
34        .spawn()
35        .context("Failed to spawn git push")?;
36
37    if let Some(stderr) = child.stderr.take() {
38        let reader = BufReader::new(stderr);
39        for line in reader.lines() {
40            match line {
41                Ok(l) => print_progress_line(&l),
42                Err(_) => break,
43            }
44        }
45    }
46
47    let status = child.wait().context("Failed to wait for git push")?;
48    if !status.success() {
49        bail!("git push failed with exit code {:?}", status.code());
50    }
51    Ok(())
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn build_push_cmd_has_expected_args() {
60        let cmd = build_push_command(Path::new("/tmp/repo"), "origin", "main");
61        let args: Vec<String> = cmd
62            .get_args()
63            .map(|s| s.to_string_lossy().into_owned())
64            .collect();
65        assert_eq!(
66            args,
67            vec!["push", "--progress", "origin", "HEAD:refs/heads/main"]
68        );
69        assert_eq!(cmd.get_current_dir(), Some(Path::new("/tmp/repo")));
70    }
71}