thor-wt 0.2.0

Worktree workflow commands for Thor
Documentation
use thor_core::{find_repo, list_worktrees};
use std::process::Command;

/// Status of a pull operation on one worktree.
pub enum PullStatus {
    Ok,
    Failed(String),
    Skipped,
}

/// Result of pulling one worktree.
pub struct PullResult {
    pub branch: String,
    pub status: PullStatus,
}

/// Pull --rebase in one or all worktrees.
pub async fn pull(all: bool) -> anyhow::Result<Vec<PullResult>> {
    if !all {
        // Pull in current directory only
        let output = Command::new("git")
            .args(["pull", "--rebase"])
            .output()?;

        let branch = Command::new("git")
            .args(["branch", "--show-current"])
            .output()
            .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
            .unwrap_or_else(|_| "unknown".to_string());

        let status = if output.status.success() {
            PullStatus::Ok
        } else {
            PullStatus::Failed(String::from_utf8_lossy(&output.stderr).trim().to_string())
        };

        return Ok(vec![PullResult { branch, status }]);
    }

    // Pull all worktrees
    let repo = find_repo()?;
    let worktrees = list_worktrees(&repo).await?;
    let mut results = Vec::new();

    for wt in &worktrees {
        let branch = wt.display_name();

        // Check if has upstream by trying to resolve @{u}
        let has_upstream = Command::new("git")
            .args(["rev-parse", "--abbrev-ref", "@{u}"])
            .current_dir(&wt.path)
            .output()
            .map(|o| o.status.success())
            .unwrap_or(false);

        if !has_upstream {
            results.push(PullResult { branch, status: PullStatus::Skipped });
            continue;
        }

        let output = Command::new("git")
            .args(["pull", "--rebase"])
            .current_dir(&wt.path)
            .output()?;

        let status = if output.status.success() {
            PullStatus::Ok
        } else {
            PullStatus::Failed(String::from_utf8_lossy(&output.stderr).trim().to_string())
        };

        results.push(PullResult { branch, status });
    }

    Ok(results)
}