Skip to main content

thoughts_tool/git/
shell_push.rs

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