use std::io::Write;
use std::path::Path;
use std::process::{Command, Stdio};
pub fn stdout(args: &[&str]) -> Option<String> {
let out = Command::new("git")
.args(args)
.stderr(Stdio::null())
.output()
.ok()?;
if !out.status.success() {
return None;
}
let text = String::from_utf8_lossy(&out.stdout).trim().to_string();
(!text.is_empty()).then_some(text)
}
pub fn succeeds(args: &[&str]) -> bool {
exit_code(args) == Some(0)
}
pub fn succeeds_in(dir: &Path, args: &[&str]) -> bool {
Command::new("git")
.current_dir(dir)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
pub fn exit_code(args: &[&str]) -> Option<i32> {
Command::new("git")
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.ok()?
.code()
}
pub fn stdout_bytes_in(dir: &Path, args: &[&str]) -> Option<Vec<u8>> {
let out = Command::new("git")
.current_dir(dir)
.args(args)
.stdin(Stdio::null())
.stderr(Stdio::null())
.output()
.ok()?;
out.status.success().then_some(out.stdout)
}
pub fn stdout_with_input_in(dir: &Path, args: &[&str], input: &[u8]) -> Option<String> {
let mut child = Command::new("git")
.current_dir(dir)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()
.ok()?;
{
let mut stdin = child.stdin.take()?;
stdin.write_all(input).ok()?;
}
let out = child.wait_with_output().ok()?;
if !out.status.success() {
return None;
}
Some(String::from_utf8_lossy(&out.stdout).trim().to_string())
}
pub fn remote(args: &[&str]) -> Option<(i32, String)> {
use std::time::{Duration, Instant};
let mut cmd = Command::new("git");
cmd.args(["-c", "http.lowSpeedLimit=1", "-c", "http.lowSpeedTime=10"])
.args(args)
.env("GIT_TERMINAL_PROMPT", "0")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null());
let set = |v: &str| std::env::var_os(v).is_some_and(|v| !v.is_empty());
let own_ssh = set("GIT_SSH_COMMAND")
|| set("GIT_SSH")
|| succeeds(&["config", "--get", "core.sshCommand"]);
if !own_ssh {
cmd.env(
"GIT_SSH_COMMAND",
"ssh -o BatchMode=yes -o ConnectTimeout=10",
);
}
let mut child = cmd.spawn().ok()?;
let mut out = child.stdout.take()?;
let reader = std::thread::spawn(move || {
let mut buf = Vec::new();
let _ = std::io::Read::read_to_end(&mut out, &mut buf);
buf
});
let deadline = Instant::now() + Duration::from_secs(15);
let status = loop {
match child.try_wait() {
Ok(Some(status)) => break status,
Ok(None) if Instant::now() < deadline => std::thread::sleep(Duration::from_millis(50)),
_ => {
let _ = child.kill();
let _ = child.wait();
return None;
}
}
};
let stdout = reader.join().ok()?;
Some((status.code()?, String::from_utf8_lossy(&stdout).to_string()))
}