use super::discover;
use balls::config::Config;
use balls::error::{BallError, Result};
use balls::policy::LocalConfig;
use balls::remaster::{self, Reconciled};
use balls::store::Store;
pub fn cmd_remaster(target: Option<String>, commit: bool, detach: bool) -> Result<()> {
let store = discover()?;
if store.no_git || store.stealth {
return Err(BallError::Other(
"remaster requires a non-stealth git-backed repo".into(),
));
}
if detach {
if target.is_some() {
return Err(BallError::Other(
"remaster --detach takes no TARGET (it goes standalone)".into(),
));
}
remaster::detach(&store)?;
set_local_state_remote(&store, "origin")?;
println!(
"detached: balls/tasks re-rooted as a standalone local store; \
state_remote cleared to `origin`"
);
return Ok(());
}
let target = target.ok_or_else(|| {
BallError::Other("remaster needs a TARGET remote (or use --detach)".into())
})?;
let outcome = remaster::reconcile(&store, &target)?;
if commit {
let path = store.config_path();
let mut cfg = Config::load(&path)?;
cfg.state_remote = Some(target.clone());
cfg.save(&path)?;
println!(
"wrote state_remote=`{target}` to committed .balls/config.json \
— commit it to share the project link"
);
} else {
set_local_state_remote(&store, &target)?;
println!("set per-clone state_remote=`{target}` (.balls/local/config.json)");
}
match outcome {
Reconciled::AlreadyUpToDate => {
println!("already up to date with `{target}`");
}
Reconciled::Joined { replayed, renamed } => {
println!(
"joined `{target}`: {replayed} task(s) replayed, {renamed} renamed"
);
}
}
Ok(())
}
fn set_local_state_remote(store: &Store, remote: &str) -> Result<()> {
let mut local = LocalConfig::load(store)?.unwrap_or_default();
local.state_remote = Some(remote.to_string());
local.save(store)
}