limb 0.2.0

A focused CLI for git worktree management
Documentation
//! Implements `limb remove`. Deletes a worktree.

use anyhow::Result;

use crate::cli::RemoveArgs;
use crate::context::Context;
use crate::hooks::{self, Event};
use crate::worktree;

/// Runs `limb remove`.
///
/// Resolves the target worktree, runs the `pre_remove` hook (if any),
/// invokes `git worktree remove` via [`worktree::remove`], then runs
/// `post_remove` on a best-effort basis.
///
/// # Errors
///
/// Returns an error if the name does not match, `pre_remove` exits
/// non-zero, or the git invocation fails (for example: dirty worktree
/// without `--force`).
pub fn run(ctx: &Context, args: &RemoveArgs) -> Result<()> {
    let repo = ctx.repo()?;
    let repo_config = ctx.repo_config_optional();

    let target = worktree::require(&repo, &args.name)?;

    if args.dry_run {
        if ctx.json {
            let out = serde_json::json!({
                "name": args.name,
                "path": target.path.display().to_string(),
                "dry_run": true,
            });
            println!("{}", serde_json::to_string_pretty(&out)?);
        } else if !ctx.quiet {
            eprintln!(
                "would remove worktree: {} at {}",
                args.name,
                target.path.display()
            );
        }
        return Ok(());
    }

    let cwd = repo_config
        .as_ref()
        .map_or_else(|| repo.clone(), |r| r.root.clone());
    let event = Event {
        cwd: &cwd,
        worktree_path: &target.path,
        worktree_name: &args.name,
    };

    let hooks_cfg = repo_config
        .as_ref()
        .map_or_else(Default::default, |r| r.hooks.clone());

    hooks::run_required(hooks_cfg.pre_remove.as_deref(), "pre_remove", &event)?;

    worktree::remove(&repo, &args.name, args.force, ctx.quiet)?;

    hooks::run_best_effort(hooks_cfg.post_remove.as_deref(), "post_remove", &event);

    if ctx.json {
        let out = serde_json::json!({
            "name": args.name,
            "path": target.path.display().to_string(),
        });
        println!("{}", serde_json::to_string_pretty(&out)?);
    } else if !ctx.quiet {
        let ok = crate::style::OK;
        anstream::eprintln!("{ok}✓{ok:#} removed: {}", args.name);
    }
    Ok(())
}