thoughts_tool/git/
shell_fetch.rs

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