use std::path::Path;
use anyhow::Result;
use super::{UntrackedFiles, acquisition, lock_launch, ownership};
use crate::git::Git;
use crate::progress;
pub struct SkillWorktree {
launch: Git,
checkout: ownership::Checkout,
untracked: UntrackedFiles,
recovered: bool,
}
pub struct ReviewConflict {
pub fingerprint: String,
pub error: anyhow::Error,
}
impl SkillWorktree {
pub fn create(launch: &Git) -> Result<Self> {
let _lock = lock_launch(launch)?;
let commit = launch.run(&["rev-parse", "--verify", "HEAD"])?;
Self::checkout(launch, &commit)
}
pub fn at(launch: &Git, commit: &str) -> Result<Self> {
let _lock = lock_launch(launch)?;
Self::checkout(launch, commit)
}
pub fn on_pull_request(launch: &Git, branch: &str, commit: &str) -> Result<Self> {
let _lock = lock_launch(launch)?;
let root = launch.run(&["rev-parse", "--show-toplevel"])?;
let retained = acquisition::recover_pull_request(launch, branch, commit)?;
let recovered = retained.is_some();
let checkout = match retained {
Some(checkout) => checkout,
None => {
let path = tempfile::Builder::new()
.prefix("thirdshift-review-")
.tempdir()?
.keep();
acquisition::add_review(launch, branch, &path, commit)?
}
};
Ok(Self {
launch: Git::new(root),
checkout,
untracked: UntrackedFiles::Preserve,
recovered,
})
}
fn checkout(launch: &Git, commit: &str) -> Result<Self> {
let root = launch.run(&["rev-parse", "--show-toplevel"])?;
let path = tempfile::Builder::new()
.prefix("thirdshift-skill-")
.tempdir()?
.keep();
progress::step(format_args!(
"creating skill worktree {} at {commit}",
path.display()
));
let checkout = acquisition::add(launch, None, &path, commit)?;
Ok(Self {
launch: Git::new(root),
checkout,
untracked: UntrackedFiles::Preserve,
recovered: false,
})
}
pub fn pull_request_conflict(
launch: &Git,
branch: &str,
commit: &str,
) -> Result<Option<ReviewConflict>> {
let _lock = lock_launch(launch)?;
let _refs_lock = launch.lock_worktree_refs()?;
ownership::pull_request_conflict(launch, branch, commit)
}
pub fn recovered(&self) -> bool {
self.recovered
}
pub fn restart_recommendation(&self, commit: &str) -> Result<()> {
if self.recovered {
self.checkout.ordinary(&self.launch, |_| {
ownership::restart_retained(&self.launch, &self.checkout, commit)
})?;
}
Ok(())
}
pub fn path(&self) -> &Path {
self.checkout.path()
}
pub fn discard_validation_artifacts(&mut self) {
self.untracked = UntrackedFiles::DiscardValidationArtifacts;
}
pub fn head(&self) -> Result<String> {
self.checkout
.ordinary(&self.launch, |operation| operation.head())
}
pub fn merge_base_commit(&self, base_commit: &str) -> Result<String> {
self.checkout.ordinary(&self.launch, |operation| {
operation.run(&["merge-base", base_commit, "HEAD"])
})
}
}
impl Drop for SkillWorktree {
fn drop(&mut self) {
self.checkout
.cleanup_preserving_work(&self.launch, self.untracked);
}
}