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::ops::oplog;
use anyhow::Result;
use std::path::Path;

pub fn add(path: &Path, pathspecs: &[String], all: bool, update: bool) -> Result<()> {
    let desc = if all {
        "add -A".to_string()
    } else if update {
        "add -u".to_string()
    } else {
        format!("add {}", pathspecs.join(" "))
    };
    oplog::with_oplog(path, "add", &desc, || {
        add_inner(path, pathspecs, all, update)
    })
}

fn add_inner(path: &Path, pathspecs: &[String], all: bool, update: bool) -> Result<()> {
    let repo = crate::ops::open_repo(path)?;
    let mut index = repo.index()?;

    if all {
        // Stage all changes: new, modified, deleted
        index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
        // Also remove deleted files from index
        index.add_all(
            ["*"].iter(),
            git2::IndexAddOption::DEFAULT | git2::IndexAddOption::CHECK_PATHSPEC,
            None,
        )?;
    } else if update {
        // Stage only modified/deleted tracked files (no new files)
        index.update_all(["*"].iter(), None)?;
    } else if pathspecs.len() == 1 && pathspecs[0] == "." {
        index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
    } else {
        for spec in pathspecs {
            let full_path = path.join(spec);
            if full_path.is_dir() {
                // Recursively add everything under the directory
                index.add_all(
                    [format!("{}/**", spec), spec.to_string()].iter(),
                    git2::IndexAddOption::DEFAULT,
                    None,
                )?;
            } else {
                index.add_path(Path::new(spec))?;
            }
        }
    }

    index.write()?;
    Ok(())
}

pub fn reset_file(path: &Path, pathspecs: &[String]) -> Result<()> {
    oplog::with_oplog(
        path,
        "reset",
        &format!("unstage {}", pathspecs.join(" ")),
        || reset_file_inner(path, pathspecs),
    )
}

fn reset_file_inner(path: &Path, pathspecs: &[String]) -> Result<()> {
    let repo = crate::ops::open_repo(path)?;
    let head = repo.head()?.peel_to_commit()?;
    let head_tree = head.tree()?;

    let mut index = repo.index()?;

    for spec in pathspecs {
        if let Ok(entry) = head_tree.get_path(Path::new(spec)) {
            let idx_entry = git2::IndexEntry {
                ctime: git2::IndexTime::new(0, 0),
                mtime: git2::IndexTime::new(0, 0),
                dev: 0,
                ino: 0,
                mode: entry.filemode() as u32,
                uid: 0,
                gid: 0,
                file_size: 0,
                id: entry.id(),
                flags: 0,
                flags_extended: 0,
                path: spec.as_bytes().to_vec(),
            };
            index.add(&idx_entry)?;
        } else {
            index.remove_path(Path::new(spec))?;
        }
    }

    index.write()?;
    Ok(())
}