use std::io::Write;
use std::path::Path;
use std::process::Command;
use std::time::Instant;
use anyhow::{Result, bail};
use crate::trace as loom_trace;
pub fn apply_patch(workdir: &Path, patch: &str) -> Result<()> {
apply_patch_with_flags(workdir, patch, &[])
}
pub fn apply_patch_reverse(workdir: &Path, patch: &str) -> Result<()> {
apply_patch_with_flags(workdir, patch, &["--reverse"])
}
pub fn apply_cached_patch(workdir: &Path, patch: &str) -> Result<()> {
apply_patch_with_flags(workdir, patch, &["--cached"])
}
pub fn apply_cached_patch_reverse(workdir: &Path, patch: &str) -> Result<()> {
apply_patch_with_flags(workdir, patch, &["--cached", "--reverse"])
}
fn apply_patch_with_flags(workdir: &Path, patch: &str, flags: &[&str]) -> Result<()> {
let mut args = vec!["apply"];
args.extend(flags);
let start = Instant::now();
let mut child = Command::new("git")
.current_dir(workdir)
.args(&args)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
if let Some(mut stdin) = child.stdin.take() {
stdin.write_all(patch.as_bytes())?;
}
let output = child.wait_with_output()?;
let duration_ms = start.elapsed().as_millis();
let stderr = String::from_utf8_lossy(&output.stderr);
loom_trace::log_command(
"git",
&args.join(" "),
duration_ms,
output.status.success(),
&stderr,
);
if !output.status.success() {
bail!("git apply failed");
}
Ok(())
}
pub fn restore_staged_patch(workdir: &Path, patch: &str) -> Result<()> {
if !patch.is_empty()
&& let Err(e) = apply_cached_patch(workdir, patch)
{
eprintln!(
"Warning: could not restore pre-existing staged changes: {}",
e
);
}
Ok(())
}