rustic-git 0.1.0

A Rustic Git - clean type-safe API over git cli
Documentation

Rustic Git

A Rust library for Git repository operations with a clean, type-safe API.

Overview

Rustic Git provides a simple, ergonomic interface for common Git operations. It follows a repository-centric design where you create a Repository instance and call methods on it to perform Git operations.

Features

  • ✅ Repository initialization and opening
  • ✅ File status checking with detailed parsing
  • ✅ File staging (add files, add all, add updates)
  • ✅ Commit creation with hash return
  • ✅ Type-safe error handling
  • ✅ Universal Hash type for Git objects
  • ✅ Comprehensive test coverage

Installation

Add this to your Cargo.toml:

[dependencies]
rustic-git = "0.1.0"

Quick Start

use rustic_git::{Repository, Result};

fn main() -> Result<()> {
    // Initialize a new repository
    let repo = Repository::init("/path/to/repo", false)?;
    
    // Or open an existing repository
    let repo = Repository::open("/path/to/existing/repo")?;
    
    // Check repository status
    let status = repo.status()?;
    if !status.is_clean() {
        println!("Modified files: {:?}", status.modified_files());
        println!("Untracked files: {:?}", status.untracked_files());
    }
    
    // Stage files
    repo.add(&["file1.txt", "file2.txt"])?;
    // Or stage all changes
    repo.add_all()?;
    
    // Create a commit
    let hash = repo.commit("Add new features")?;
    println!("Created commit: {}", hash.short());
    
    Ok(())
}

API Documentation

Repository Lifecycle

Repository::init(path, bare) -> Result<Repository>

Initialize a new Git repository.

// Initialize a regular repository
let repo = Repository::init("/path/to/repo", false)?;

// Initialize a bare repository  
let bare_repo = Repository::init("/path/to/bare-repo", true)?;

Repository::open(path) -> Result<Repository>

Open an existing Git repository.

let repo = Repository::open("/path/to/existing/repo")?;

Status Operations

Repository::status() -> Result<GitStatus>

Get the current repository status.

let status = repo.status()?;

// Check if repository is clean
if status.is_clean() {
    println!("No changes");
} else {
    println!("Repository has changes");
}

// Get files by status
let modified = status.modified_files();
let untracked = status.untracked_files();

// Or work with all files directly
for (file_status, filename) in &status.files {
    println!("{:?}: {}", file_status, filename);
}

The GitStatus struct contains:

  • files: Box<[(FileStatus, String)]> - All files with their status
  • is_clean() - Returns true if no changes
  • has_changes() - Returns true if any changes exist
  • modified_files() - Get all modified files
  • untracked_files() - Get all untracked files
  • files_with_status(status) - Get files with specific status

File Status Types

pub enum FileStatus {
    Modified,   // File has been modified
    Added,      // File has been added to index
    Deleted,    // File has been deleted
    Renamed,    // File has been renamed
    Copied,     // File has been copied
    Untracked,  // File is not tracked by git
    Ignored,    // File is ignored by git
}

Staging Operations

Repository::add(paths) -> Result<()>

Add specific files to the staging area.

// Add single file
repo.add(&["file.txt"])?;

// Add multiple files
repo.add(&["file1.txt", "file2.txt", "dir/file3.txt"])?;

// Add with Path objects
use std::path::Path;
repo.add(&[Path::new("file.txt")])?;

Repository::add_all() -> Result<()>

Add all changes to the staging area (equivalent to git add .).

repo.add_all()?;

Repository::add_update() -> Result<()>

Add all tracked files that have been modified (equivalent to git add -u).

repo.add_update()?;

Commit Operations

Repository::commit(message) -> Result<Hash>

Create a commit with the given message.

let hash = repo.commit("Fix critical bug")?;
println!("Commit created: {}", hash);
println!("Short hash: {}", hash.short());

Repository::commit_with_author(message, author) -> Result<Hash>

Create a commit with a custom author.

let hash = repo.commit_with_author(
    "Add new feature", 
    "Jane Developer <jane@example.com>"
)?;

Hash Type

The Hash type represents Git object hashes (commits, trees, blobs, etc.).

let hash = repo.commit("message")?;

// Get full hash as string
let full_hash: &str = hash.as_str();

// Get short hash (first 7 characters)
let short_hash: &str = hash.short();

// Display formatting
println!("Commit: {}", hash);  // Displays full hash

Error Handling

All operations return Result<T, GitError> for proper error handling.

use rustic_git::{Repository, GitError};

match repo.commit("message") {
    Ok(hash) => println!("Success: {}", hash),
    Err(GitError::CommandFailed(msg)) => eprintln!("Git command failed: {}", msg),
    Err(GitError::IoError(msg)) => eprintln!("IO error: {}", msg),
}

Complete Workflow Example

use rustic_git::{Repository, FileStatus};
use std::fs;

fn main() -> rustic_git::Result<()> {
    // Create a new repository
    let repo = Repository::init("./my-project", false)?;
    
    // Create some files
    fs::write("./my-project/README.md", "# My Project")?;
    fs::write("./my-project/src/main.rs", "fn main() { println!(\"Hello!\"); }")?;
    fs::create_dir_all("./my-project/src")?;
    
    // Check status
    let status = repo.status()?;
    println!("Found {} untracked files", status.untracked_files().len());
    
    // Stage all files
    repo.add_all()?;
    
    // Verify staging
    let status = repo.status()?;
    let added_files: Vec<_> = status.files.iter()
        .filter(|(s, _)| matches!(s, FileStatus::Added))
        .map(|(_, f)| f)
        .collect();
    println!("Staged files: {:?}", added_files);
    
    // Create initial commit
    let hash = repo.commit("Initial commit with project structure")?;
    println!("Created commit: {}", hash.short());
    
    // Verify clean state
    let status = repo.status()?;
    assert!(status.is_clean());
    println!("Repository is now clean!");
    
    Ok(())
}

Testing

Run the test suite:

cargo test

All tests create temporary repositories in /tmp/ and clean up after themselves.

Contributing

This library follows these design principles:

  • Repository-centric API: Static lifecycle methods (init, open) return Repository instances
  • Type safety: Strong typing with custom error types and structured return values
  • Ergonomic design: Clean, intuitive API that follows Rust conventions
  • Comprehensive testing: All functionality thoroughly tested
  • Modular organization: Commands organized in separate modules

License

MIT License - see LICENSE file for details.

Roadmap

Future planned features:

  • Commit history and log operations
  • Diff operations
  • Branch operations
  • Remote operations (clone, push, pull)
  • Merge and rebase operations
  • Tag operations
  • Stash operations

Version

Current version: 0.1.0 - Basic git workflow (init, status, add, commit)