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, target: &str, create: bool, detach: bool, ui: &UI) -> Result<()> {
    let desc = if create {
        format!("switch -c '{}'", target)
    } else {
        format!("switch '{}'", target)
    };
    oplog::with_oplog(path, "switch", &desc, || {
        execute_inner(path, target, create, detach, ui)
    })
}

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

    if create {
        let head_commit = repo.head()?.peel_to_commit()?;
        let branch = repo.branch(target, &head_commit, false)?;
        let obj = branch.get().peel(git2::ObjectType::Commit)?;
        repo.checkout_tree(&obj, None)?;
        repo.set_head(&format!("refs/heads/{}", target))?;
        ui.success(format!("Switched to a new branch '{}'", target));
        return Ok(());
    }

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

    // Try remote tracking branch — create local tracking branch
    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)?;
            repo.checkout_tree(&obj, None)?;
            repo.set_head(&format!("refs/heads/{}", target))?;
            ui.success(format!(
                "Switched to a new branch '{}' tracking '{}'",
                target, remote_branch
            ));
            return Ok(());
        }
    }

    // Allow --detach for arbitrary refs
    if detach {
        let obj = repo.revparse_single(target)?;
        let commit = obj.peel_to_commit()?;
        repo.checkout_tree(commit.as_object(), None)?;
        repo.set_head_detached(commit.id())?;
        ui.info(format!(
            "HEAD is now at {} {}",
            short_oid(&commit.id()),
            commit.summary().unwrap_or("")
        ));
        return Ok(());
    }

    bail!(
        "invalid reference: '{}'\n\
         hint: If you want to detach HEAD at the commit, use: securegit switch --detach {}",
        target,
        target
    );
}