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 git2::{build::CheckoutBuilder, Repository};
use std::path::Path;

pub fn execute(path: &Path, target: &str, create_branch: bool, force: bool, ui: &UI) -> Result<()> {
    let desc = if create_branch {
        format!("checkout -b '{}'", target)
    } else {
        format!("checkout '{}'", target)
    };
    oplog::with_oplog(path, "checkout", &desc, || {
        execute_inner(path, target, create_branch, force, ui)
    })
}

fn checkout_opts(force: bool) -> Option<CheckoutBuilder<'static>> {
    if force {
        let mut cb = CheckoutBuilder::new();
        cb.force();
        Some(cb)
    } else {
        None
    }
}

fn execute_inner(
    path: &Path,
    target: &str,
    create_branch: bool,
    force: bool,
    ui: &UI,
) -> Result<()> {
    let repo = crate::ops::open_repo(path)?;

    // Handle "checkout -" (previous branch)
    if target == "-" {
        return switch_to_previous(&repo, force, ui);
    }

    if create_branch {
        let head_commit = repo.head()?.peel_to_commit()?;
        let branch = repo.branch(target, &head_commit, false)?;
        let obj = branch.get().peel(git2::ObjectType::Commit)?;

        let mut opts = checkout_opts(force);
        repo.checkout_tree(&obj, opts.as_mut())?;
        repo.set_head(&format!("refs/heads/{}", target))?;
        ui.success(format!("Switched to a new branch '{}'", target));
        return Ok(());
    }

    // Try as local branch
    if let Ok(branch) = repo.find_branch(target, git2::BranchType::Local) {
        let obj = branch.get().peel(git2::ObjectType::Commit)?;
        let mut opts = checkout_opts(force);
        repo.checkout_tree(&obj, opts.as_mut())?;
        repo.set_head(&format!("refs/heads/{}", target))?;
        ui.success(format!("Switched to branch '{}'", target));
        return Ok(());
    }

    // Try as remote branch (create local tracking)
    for remote_name in repo.remotes()?.iter().flatten() {
        let remote_branch = format!("{}/{}", remote_name, target);
        if let Ok(branch) = repo.find_branch(&remote_branch, git2::BranchType::Remote) {
            let commit = branch.get().peel_to_commit()?;
            let local_branch = repo.branch(target, &commit, false)?;
            let obj = local_branch.get().peel(git2::ObjectType::Commit)?;
            let mut opts = checkout_opts(force);
            repo.checkout_tree(&obj, opts.as_mut())?;
            repo.set_head(&format!("refs/heads/{}", target))?;
            ui.success(format!(
                "Switched to a new branch '{}' tracking '{}'",
                target, remote_branch
            ));
            return Ok(());
        }
    }

    // Try as commit/tag
    if let Ok(obj) = repo.revparse_single(target) {
        let commit = obj.peel_to_commit()?;
        let mut opts = checkout_opts(force);
        repo.checkout_tree(commit.as_object(), opts.as_mut())?;
        repo.set_head_detached(commit.id())?;
        ui.info(format!(
            "HEAD is now at {} {}",
            short_oid(&commit.id()),
            commit.summary().unwrap_or("")
        ));
        return Ok(());
    }

    bail!("pathspec '{}' did not match any known refs", target);
}

fn switch_to_previous(repo: &Repository, force: bool, ui: &UI) -> Result<()> {
    // Read the reflog to find the previous branch
    let reflog = repo.reflog("HEAD")?;
    for i in 0..reflog.len() {
        if let Some(entry) = reflog.get(i) {
            let msg = entry.message().unwrap_or("");
            // Look for "checkout: moving from X to Y" — we want X
            if let Some(rest) = msg.strip_prefix("checkout: moving from ") {
                if let Some(from_branch) = rest.split(" to ").next() {
                    // Verify the branch still exists
                    if repo
                        .find_branch(from_branch, git2::BranchType::Local)
                        .is_ok()
                    {
                        let branch = repo.find_branch(from_branch, git2::BranchType::Local)?;
                        let obj = branch.get().peel(git2::ObjectType::Commit)?;
                        let mut opts = checkout_opts(force);
                        repo.checkout_tree(&obj, opts.as_mut())?;
                        repo.set_head(&format!("refs/heads/{}", from_branch))?;
                        ui.success(format!("Switched to branch '{}'", from_branch));
                        return Ok(());
                    }
                }
            }
        }
    }
    bail!("No previous branch found in reflog");
}