use super::super::{Error, checkpoint};
use super::product::Product;
use super::span::Span;
use crate::template::GitRunner;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
pub(super) fn commit(
parent_worktree: &Path,
compactor_id: &str,
compactor_ref: &str,
span: &Span,
product: &Product,
git: &dyn GitRunner,
) -> Result<String, Error> {
let tmp = scratch_worktree(compactor_id);
let tmp_str = tmp.to_string_lossy().into_owned();
git.run(
parent_worktree,
&[
"worktree",
"add",
"--no-checkout",
"--detach",
&tmp_str,
&span.point,
],
)
.map_err(|source| Error::Git {
op: "compaction land scratch worktree",
source,
})?;
let minted = mint(&tmp, compactor_id, compactor_ref, span, product, git);
let _ = git.run(
parent_worktree,
&["worktree", "remove", "--force", &tmp_str],
);
let _ = std::fs::remove_dir_all(&tmp);
minted
}
fn mint(
tmp: &Path,
compactor_id: &str,
compactor_ref: &str,
span: &Span,
product: &Product,
git: &dyn GitRunner,
) -> Result<String, Error> {
let err = |op| move |source| Error::Git { op, source };
git.run(tmp, &["read-tree", &span.point])
.map_err(err("compaction land read-tree"))?;
if !product.deletions.is_empty() {
let mut args = vec!["rm", "--cached", "-q", "--ignore-unmatch", "--"];
args.extend(product.deletions.iter().map(String::as_str));
git.run(tmp, &args)
.map_err(err("compaction land apply deletions"))?;
}
if !product.summaries.is_empty() {
let source_arg = format!("--source={compactor_ref}");
let mut args = vec!["restore", "--staged", source_arg.as_str(), "--"];
args.extend(product.summaries.iter().map(String::as_str));
git.run(tmp, &args)
.map_err(err("compaction land stage summary"))?;
}
if let Some(extract) = &product.extract {
let abs = tmp.join(&extract.path);
std::fs::create_dir_all(abs.parent().expect("summary/ has a parent"))?;
std::fs::write(&abs, &extract.text)?;
git.run(tmp, &["add", "--", &extract.path])
.map_err(err("compaction land stage extract"))?;
}
let tree = git
.run_capture(tmp, &["write-tree"])
.map_err(err("compaction land write-tree"))?;
let subject = format!("{}{compactor_id}]", checkpoint::BASE_SUBJECT_PREFIX);
let sha = git
.run_capture(
tmp,
&[
"commit-tree",
tree.trim(),
"-p",
&span.bound,
"-m",
&subject,
],
)
.map_err(err("compaction land commit-tree"))?;
Ok(sha.trim().to_string())
}
fn scratch_worktree(compactor_id: &str) -> PathBuf {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
std::env::temp_dir().join(format!("litany-compaction-base-{compactor_id}-{nanos}"))
}