use std::process::ExitCode;
use crate::config::{RepoContext, config_without_shadow, parse_config};
use crate::exclude::{exclude_entry, remove_mapping_exclusion};
use crate::paths::normalize_lexically;
pub fn run(name: &str, delete: bool) -> ExitCode {
match remove_shadow(name, delete) {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("git-shadow: {e}");
ExitCode::FAILURE
}
}
}
fn remove_shadow(name: &str, delete: bool) -> Result<(), String> {
let ctx = RepoContext::discover()?;
if !ctx.config_path.exists() {
return Err(format!(
"shadow '{name}' not found; no config at {}",
ctx.config_path.display()
));
}
let content = std::fs::read_to_string(&ctx.config_path)
.map_err(|e| format!("failed to read {}: {e}", ctx.config_path.display()))?;
let config = parse_config(&content)
.map_err(|e| format!("failed to parse {}: {e}", ctx.config_path.display()))?;
let shadow = config
.shadows
.get(name)
.ok_or_else(|| format!("shadow '{name}' not found in {}", ctx.config_path.display()))?;
let mapping = shadow.mapping.clone();
let new_content = config_without_shadow(&content, name)?;
let new_config = parse_config(&new_content)
.map_err(|e| format!("refusing to write an invalid config: {e}"))?;
if new_config.shadows.len() != config.shadows.len() - 1 || new_config.shadows.contains_key(name)
{
return Err(format!(
"refusing to write config: removal would not drop exactly shadow '{name}'"
));
}
std::fs::write(&ctx.config_path, new_content)
.map_err(|e| format!("failed to write {}: {e}", ctx.config_path.display()))?;
println!("removed shadow '{name}' from {}", ctx.config_path.display());
match remove_mapping_exclusion(&ctx.parent_repo, &mapping) {
Ok(Some(exclude_path)) => {
println!(
"removed '{}' from {}",
exclude_entry(&mapping),
exclude_path.display()
);
}
Ok(None) => {}
Err(e) => {
return Err(format!(
"shadow was removed from config, but updating git exclude failed: {e}"
));
}
}
if delete {
let dir = normalize_lexically(&ctx.parent_repo.join(&mapping));
let parent = normalize_lexically(&ctx.parent_repo);
if !dir.starts_with(&parent) || dir == parent {
return Err(format!(
"refusing to delete {}: it is not inside the parent repository",
dir.display()
));
}
if dir.exists() {
std::fs::remove_dir_all(&dir)
.map_err(|e| format!("failed to delete {}: {e}", dir.display()))?;
println!("deleted {}", dir.display());
}
}
Ok(())
}