Skip to main content

thoughts_tool/git/
shell_fetch.rs

1use anyhow::Context;
2use anyhow::Result;
3use anyhow::bail;
4use std::path::Path;
5use std::process::Command;
6use std::process::Stdio;
7
8/// Build a git fetch command for the given repo and remote
9pub fn build_fetch_command(repo_path: &Path, remote: &str) -> Command {
10    let mut cmd = Command::new("git");
11    cmd.current_dir(repo_path).arg("fetch").arg(remote);
12    cmd
13}
14
15/// Fetch from remote using system git (uses system SSH, triggers 1Password prompts)
16pub fn fetch(repo_path: &Path, remote: &str) -> Result<()> {
17    which::which("git").context("git executable not found in PATH")?;
18
19    let mut cmd = build_fetch_command(repo_path, remote);
20    let status = cmd
21        .stdout(Stdio::null())
22        .stderr(Stdio::inherit())
23        .status()
24        .with_context(|| {
25            format!(
26                "Failed to spawn git fetch for remote '{}' in '{}'",
27                remote,
28                repo_path.display()
29            )
30        })?;
31
32    if !status.success() {
33        bail!(
34            "git fetch failed for remote '{}' in '{}' with exit code {:?}",
35            remote,
36            repo_path.display(),
37            status.code()
38        );
39    }
40    Ok(())
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use std::path::Path;
47
48    #[test]
49    fn build_fetch_cmd_has_expected_args() {
50        let cmd = build_fetch_command(Path::new("/tmp/repo"), "origin");
51        let args: Vec<String> = cmd
52            .get_args()
53            .map(|s| s.to_string_lossy().into_owned())
54            .collect();
55        assert_eq!(args, vec!["fetch", "origin"]);
56        assert_eq!(cmd.get_current_dir(), Some(Path::new("/tmp/repo")));
57    }
58}