limb 0.1.0

A focused CLI for git worktree management
Documentation
//! Implements `limb rename`. Moves a worktree's directory.

use anyhow::{Context as _, Result};

use crate::cli::RenameArgs;
use crate::context::Context;
use crate::{git, worktree};

/// Runs `limb rename`.
///
/// Thin wrapper over `git worktree move`, with a pre-check that the
/// destination does not already exist (for a cleaner error than git's).
/// Unlike `git worktree move`, accepts names (not paths) for the source.
///
/// # Errors
///
/// Returns an error if the worktree name does not match, the destination
/// already exists, the source has no parent directory, or the git
/// invocation fails.
pub fn run(ctx: &Context, args: &RenameArgs) -> Result<()> {
    if args.old == args.new {
        anyhow::bail!("old and new names are identical: {}", args.old);
    }
    let repo = ctx.repo()?;
    let target = worktree::require(&repo, &args.old)?;
    let parent = target
        .path
        .parent()
        .context("worktree has no parent directory")?;
    let new_path = parent.join(&args.new);

    if new_path.exists() {
        anyhow::bail!(
            "destination already exists: {}; try: `limb remove {}` first if that worktree is unwanted",
            new_path.display(),
            args.new
        );
    }

    if args.dry_run {
        eprintln!(
            "would rename: {}{}",
            target.path.display(),
            new_path.display()
        );
        return Ok(());
    }

    let old_str = target.path.to_string_lossy().into_owned();
    let new_str = new_path.to_string_lossy().into_owned();
    git::run(&repo, &["worktree", "move", &old_str, &new_str])?;

    if ctx.json {
        let out = serde_json::json!({
            "old_name": args.old,
            "new_name": args.new,
            "old_path": target.path.display().to_string(),
            "new_path": new_path.display().to_string(),
        });
        println!("{}", serde_json::to_string_pretty(&out)?);
    } else if !ctx.quiet {
        let ok = crate::style::OK;
        anstream::eprintln!(
            "{ok}✓{ok:#} renamed: {} → {}",
            target.path.display(),
            new_path.display()
        );
    }
    Ok(())
}