use std::path::Path;
use std::process::Command;
fn run(workspace: &Path, args: &[&str]) -> Result<String, String> {
let out = Command::new("git")
.args(args)
.current_dir(workspace)
.output()
.map_err(|e| format!("spawn git: {e}"))?;
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
let pick_line = |s: &str| {
s.lines()
.map(str::trim)
.find(|l| !l.is_empty())
.map(str::to_string)
};
if out.status.success() {
let last_stdout = stdout
.lines()
.rfind(|l| !l.trim().is_empty())
.map(str::trim);
let last_stderr = stderr
.lines()
.rfind(|l| !l.trim().is_empty())
.map(str::trim);
Ok(last_stdout.or(last_stderr).unwrap_or("ok").to_string())
} else {
Err(pick_line(&stderr)
.or_else(|| pick_line(&stdout))
.unwrap_or_else(|| format!("git {} failed", args.first().copied().unwrap_or(""))))
}
}
pub fn fetch_all(workspace: &Path) -> Result<String, String> {
run(workspace, &["fetch", "--all", "--prune"])
}
pub fn pull_ff_only(workspace: &Path) -> Result<String, String> {
run(workspace, &["pull", "--ff-only"])
}
pub fn push(workspace: &Path) -> Result<String, String> {
run(workspace, &["push"])
}
pub fn push_set_upstream(workspace: &Path, branch: &str) -> Result<String, String> {
run(workspace, &["push", "--set-upstream", "origin", branch])
}
#[cfg(test)]
mod tests {
use super::*;
use std::process::Command;
fn init_bare(d: &Path) {
let _ = Command::new("git")
.args(["init", "--bare", "-q"])
.current_dir(d)
.output();
}
fn init_repo(d: &Path) {
for args in [
&["init", "-q", "-b", "main"][..],
&["config", "user.email", "t@example.com"][..],
&["config", "user.name", "Test"][..],
&["config", "commit.gpgsign", "false"][..],
] {
let _ = Command::new("git").args(args).current_dir(d).output();
}
}
#[test]
fn fetch_all_on_repo_without_remote_succeeds_silently() {
let d = tempfile::tempdir().unwrap();
init_repo(d.path());
let r = fetch_all(d.path());
assert!(r.is_ok(), "{r:?}");
}
#[test]
fn push_set_upstream_creates_remote_branch() {
let bare = tempfile::tempdir().unwrap();
init_bare(bare.path());
let work = tempfile::tempdir().unwrap();
init_repo(work.path());
let _ = Command::new("git")
.args(["remote", "add", "origin", bare.path().to_str().unwrap()])
.current_dir(work.path())
.output();
std::fs::write(work.path().join("a.txt"), "alpha").unwrap();
let _ = Command::new("git")
.args(["add", "."])
.current_dir(work.path())
.output();
let _ = Command::new("git")
.args(["commit", "-m", "first"])
.current_dir(work.path())
.output();
let r = push_set_upstream(work.path(), "main");
assert!(r.is_ok(), "{r:?}");
}
}