securegit 0.8.5

Zero-trust git replacement with 12 built-in security scanners, LLM redteam bridge, universal undo, durable backups, and a 50-tool MCP server
Documentation
use crate::cli::UI;
use crate::ops::oplog;
use crate::ops::utils::short_oid;
use anyhow::{bail, Result};
use std::path::Path;

pub fn execute(
    path: &Path,
    commit_ref: &str,
    abort: bool,
    continue_pick: bool,
    skip: bool,
    no_commit: bool,
    ui: &UI,
) -> Result<()> {
    let desc = if abort {
        "abort cherry-pick".to_string()
    } else if continue_pick {
        "continue cherry-pick".to_string()
    } else if skip {
        "skip cherry-pick".to_string()
    } else {
        format!("cherry-pick {}", commit_ref)
    };
    oplog::with_oplog(path, "cherry-pick", &desc, || {
        execute_inner(path, commit_ref, abort, continue_pick, skip, no_commit, ui)
    })
}

fn execute_inner(
    path: &Path,
    commit_ref: &str,
    abort: bool,
    continue_pick: bool,
    skip: bool,
    no_commit: bool,
    ui: &UI,
) -> Result<()> {
    let repo = crate::ops::open_repo(path)?;

    if abort {
        if repo.state() != git2::RepositoryState::CherryPickSequence
            && repo.state() != git2::RepositoryState::CherryPick
        {
            bail!("No cherry-pick in progress to abort.");
        }
        repo.cleanup_state()?;
        // Reset index to HEAD
        let head = repo.head()?.peel_to_commit()?;
        repo.reset(head.as_object(), git2::ResetType::Hard, None)?;
        ui.success("Cherry-pick aborted");
        return Ok(());
    }

    if skip {
        if repo.state() != git2::RepositoryState::CherryPickSequence
            && repo.state() != git2::RepositoryState::CherryPick
        {
            bail!("No cherry-pick in progress to skip.");
        }
        // Reset to HEAD (discard current changes) and clean up state
        let head = repo.head()?.peel_to_commit()?;
        repo.reset(head.as_object(), git2::ResetType::Hard, None)?;
        repo.cleanup_state()?;
        ui.success("Cherry-pick skipped");
        return Ok(());
    }

    if continue_pick {
        if repo.state() != git2::RepositoryState::CherryPickSequence
            && repo.state() != git2::RepositoryState::CherryPick
        {
            bail!("No cherry-pick in progress to continue.");
        }
        let index = repo.index()?;
        if index.has_conflicts() {
            bail!("Conflicts still exist. Resolve them before continuing.");
        }
        // Commit the resolved state
        let mut index = repo.index()?;
        let tree_oid = index.write_tree()?;
        let tree = repo.find_tree(tree_oid)?;
        let sig = repo.signature().map_err(|_| {
            anyhow::anyhow!(
                "Author identity not configured. Run:\n  \
                 git config user.name \"Your Name\"\n  \
                 git config user.email \"you@example.com\""
            )
        })?;
        let head_commit = repo.head()?.peel_to_commit()?;
        repo.commit(
            Some("HEAD"),
            &sig,
            &sig,
            "cherry-pick (continued)",
            &tree,
            &[&head_commit],
        )?;
        repo.cleanup_state()?;
        ui.success("Cherry-pick continued and committed");
        return Ok(());
    }

    let obj = repo.revparse_single(commit_ref)?;
    let commit = obj.peel_to_commit()?;

    repo.cherrypick(&commit, None)?;

    let index = repo.index()?;
    if index.has_conflicts() {
        let short_id = short_oid(&commit.id());
        let _ = crate::ops::conflicts::record_conflicts(path, &short_id, "cherry-pick", commit_ref);
        ui.warning(format!("Cherry-pick of {} had conflicts", short_id));
        ui.info("Resolve conflicts, then run: securegit cherry-pick --continue");
        ui.info("Or abort with: securegit cherry-pick --abort");
        return Ok(());
    }

    if no_commit {
        repo.cleanup_state()?;
        let short_id = short_oid(&commit.id());
        ui.success(format!(
            "Cherry-pick {} applied to index (not committed)",
            short_id
        ));
        return Ok(());
    }

    // Auto-commit when clean
    let mut index = repo.index()?;
    let tree_oid = index.write_tree()?;
    let tree = repo.find_tree(tree_oid)?;
    let sig = repo.signature().map_err(|_| {
        anyhow::anyhow!(
            "Author identity not configured. Run:\n  \
             git config user.name \"Your Name\"\n  \
             git config user.email \"you@example.com\""
        )
    })?;
    let head_commit = repo.head()?.peel_to_commit()?;

    let msg = commit.message().unwrap_or("cherry-pick");
    match repo.commit(Some("HEAD"), &sig, &sig, msg, &tree, &[&head_commit]) {
        Ok(_) => {
            repo.cleanup_state()?;
            let short_id = short_oid(&commit.id());
            ui.success(format!(
                "[cherry-pick {}] {}",
                short_id,
                commit.summary().unwrap_or("")
            ));
        }
        Err(e) => {
            // Clean up cherry-pick state on commit failure
            let _ = repo.cleanup_state();
            bail!("Cherry-pick commit failed: {}", e);
        }
    }

    Ok(())
}