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,
    message: &str,
    allow_empty: bool,
    amend: bool,
    ui: &UI,
) -> Result<git2::Oid> {
    let desc = if amend {
        format!("amend '{}'", message.lines().next().unwrap_or(""))
    } else {
        format!("'{}'", message.lines().next().unwrap_or(""))
    };
    oplog::with_oplog(path, "commit", &desc, || {
        execute_inner(path, message, allow_empty, amend, ui)
    })
}

fn execute_inner(
    path: &Path,
    message: &str,
    allow_empty: bool,
    amend: bool,
    ui: &UI,
) -> Result<git2::Oid> {
    let repo = crate::ops::open_repo(path)?;
    let mut index = repo.index()?;

    if amend {
        let head_commit = repo
            .head()
            .ok()
            .and_then(|h| h.peel_to_commit().ok())
            .ok_or_else(|| anyhow::anyhow!("No commit to amend (empty history)"))?;

        let tree_oid = index.write_tree()?;
        let tree = repo.find_tree(tree_oid)?;
        let sig = repo.signature()?;

        // Use the provided message, or fall back to the original commit message
        let final_msg = if message.is_empty() {
            head_commit.message().unwrap_or("(no message)").to_string()
        } else {
            message.to_string()
        };

        let parents: Vec<git2::Commit> = head_commit.parents().collect();
        let parent_refs: Vec<&git2::Commit> = parents.iter().collect();

        let oid = repo.commit(Some("HEAD"), &sig, &sig, &final_msg, &tree, &parent_refs)?;

        let sid = short_oid(&oid);
        let branch = repo
            .head()
            .ok()
            .and_then(|h| h.shorthand().map(|s| s.to_string()))
            .unwrap_or_else(|| "(detached)".to_string());

        ui.success(format!(
            "[{} {}] {}",
            branch,
            sid,
            final_msg.lines().next().unwrap_or("")
        ));

        return Ok(oid);
    }

    let head_tree = repo.head().ok().and_then(|h| h.peel_to_tree().ok());

    let diff = repo.diff_tree_to_index(head_tree.as_ref(), Some(&index), None)?;
    if diff.deltas().count() == 0 && !allow_empty {
        bail!("nothing to commit (use --allow-empty to override)");
    }

    let tree_oid = index.write_tree()?;
    let tree = repo.find_tree(tree_oid)?;

    let sig = repo.signature()?;

    let parent_commit = repo.head().ok().and_then(|h| h.peel_to_commit().ok());
    let parents: Vec<&git2::Commit> = parent_commit.iter().collect();

    let oid = repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &parents)?;

    let sid = short_oid(&oid);
    let branch = repo
        .head()
        .ok()
        .and_then(|h| h.shorthand().map(|s| s.to_string()))
        .unwrap_or_else(|| "(detached)".to_string());

    ui.success(format!(
        "[{} {}] {}",
        branch,
        sid,
        message.lines().next().unwrap_or("")
    ));

    let stats = diff.stats()?;
    let count = stats.files_changed();
    ui.field(
        "Changed",
        format!("{} file{}", count, if count == 1 { "" } else { "s" }),
    );

    Ok(oid)
}