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
- ✅ Enhanced file status checking with separate staged/unstaged tracking
- ✅ Precise Git state representation using IndexStatus and WorktreeStatus enums
- ✅ File staging (add files, add all, add updates)
- ✅ Commit creation with hash return
- ✅ Complete branch operations with type-safe Branch API
- ✅ Branch management (create, delete, checkout, list)
- ✅ Commit history & log operations with multi-level API
- ✅ Advanced commit querying with filtering and analysis
- ✅ Repository configuration management with type-safe API
- ✅ Type-safe error handling with custom GitError enum
- ✅ Universal
Hash
type for Git objects - ✅ Immutable collections (Box<[T]>) for memory efficiency
- ✅ Const enum conversions with zero runtime cost
- ✅ Comprehensive test coverage (106+ tests)
Installation
Add this to your Cargo.toml
:
[]
= "*"
Or use cargo add
to automatically add the latest version:
Quick Start
use ;
API Documentation
Repository Lifecycle
Repository::init(path, bare) -> Result<Repository>
Initialize a new Git repository.
// Initialize a regular repository
let repo = init?;
// Initialize a bare repository
let bare_repo = init?;
Repository::open(path) -> Result<Repository>
Open an existing Git repository.
let repo = open?;
Status Operations
Repository::status() -> Result<GitStatus>
Get the current repository status with enhanced staged/unstaged file tracking.
let status = repo.status?;
// Check if repository is clean
if status.is_clean else
// Get files by staging state
let staged_files: = status.staged_files.collect;
let unstaged_files: = status.unstaged_files.collect;
let untracked_files: = status.untracked_entries.collect;
// Filter by specific status types
let modified_in_index: = status
.files_with_index_status
.collect;
let modified_in_worktree: = status
.files_with_worktree_status
.collect;
// Work with all file entries directly
for entry in status.entries
The GitStatus
struct contains:
entries: Box<[FileEntry]>
- Immutable collection of file entriesis_clean()
- Returns true if no changeshas_changes()
- Returns true if any changes existstaged_files()
- Iterator over files with index changes (staged)unstaged_files()
- Iterator over files with worktree changes (unstaged)untracked_entries()
- Iterator over untracked filesignored_files()
- Iterator over ignored filesfiles_with_index_status(status)
- Filter by specific index statusfiles_with_worktree_status(status)
- Filter by specific worktree status
File Status Types
The enhanced status API uses separate enums for index (staged) and worktree (unstaged) states:
// Index (staging area) status
// Worktree (working directory) status
// File entry combining both states
Both enums support const character conversion:
// Convert to/from git porcelain characters
let status = from_char; // IndexStatus::Modified
let char = status.to_char; // 'M'
// Display formatting
println!; // Prints: M
println!; // Prints: ?
Staging Operations
Repository::add(paths) -> Result<()>
Add specific files to the staging area.
// Add single file
repo.add?;
// Add multiple files
repo.add?;
// Add with Path objects
use Path;
repo.add?;
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?;
Configuration Operations
Repository::config() -> RepoConfig
Get a configuration manager for the repository to set and get git configuration values.
// Configure git user (convenience method)
repo.config.set_user?;
// Get user configuration
let = repo.config.get_user?;
println!;
// Set any git configuration value
repo.config.set?;
repo.config.set?;
// Get any git configuration value
let autocrlf = repo.config.get?;
println!;
// Remove a configuration value
repo.config.unset?;
Configuration Methods
set_user(name, email)
- Convenience method to set both user.name and user.emailget_user()
- Get user configuration as a tuple (name, email)set(key, value)
- Set any git configuration valueget(key)
- Get any git configuration value as Stringunset(key)
- Remove a git configuration value
All configuration operations are scoped to the specific repository.
Commit Operations
Repository::commit(message) -> Result<Hash>
Create a commit with the given message.
let hash = repo.commit?;
println!;
println!;
Repository::commit_with_author(message, author) -> Result<Hash>
Create a commit with a custom author.
let hash = repo.commit_with_author?;
Branch Operations
Repository::branches() -> Result<BranchList>
List all branches in the repository.
let branches = repo.branches?;
// Check total count
println!;
println!;
println!;
// Iterate over all branches
for branch in branches.iter
// Filter by type
let local_branches: = branches.local.collect;
let remote_branches: = branches.remote.collect;
Repository::current_branch() -> Result<Option<Branch>>
Get the currently checked out branch.
if let Some = repo.current_branch?
Repository::create_branch(name, start_point) -> Result<Branch>
Create a new branch.
// Create branch from current HEAD
let branch = repo.create_branch?;
// Create branch from specific commit/branch
let branch = repo.create_branch?;
let branch = repo.create_branch?;
Repository::checkout(branch) -> Result<()>
Switch to an existing branch.
let branches = repo.branches?;
if let Some = branches.find
Repository::checkout_new(name, start_point) -> Result<Branch>
Create a new branch and switch to it immediately.
// Create and checkout new branch from current HEAD
let branch = repo.checkout_new?;
// Create and checkout from specific starting point
let branch = repo.checkout_new?;
println!;
Repository::delete_branch(branch, force) -> Result<()>
Delete a branch.
let branches = repo.branches?;
if let Some = branches.find
Branch Types
The branch API uses structured types for type safety:
// Branch represents a single branch
// Branch type enumeration
// BranchList contains all branches with efficient methods
Branch Search and Filtering
let branches = repo.branches?;
// Find specific branches
if let Some = branches.find
// Find by short name (useful for remote branches)
if let Some = branches.find_by_short_name
// Filter by type
println!;
for branch in branches.local
if branches.remote_count > 0
// Get current branch
if let Some = branches.current
Commit History Operations
Repository::log() -> Result<CommitLog>
Get all commits in the repository.
let commits = repo.log?;
println!;
for commit in commits.iter
Repository::recent_commits(count) -> Result<CommitLog>
Get the most recent N commits.
let recent = repo.recent_commits?;
for commit in recent.iter
Repository::log_with_options(options) -> Result<CommitLog>
Advanced commit queries with filtering options.
use ;
// Search commits with message containing "fix"
let bug_fixes = repo.log_with_options?;
// Get commits by specific author
let author_commits = repo.log_with_options?;
// Get commits from date range
let since = now - days;
let recent_commits = repo.log_with_options?;
// Get commits affecting specific paths
let file_commits = repo.log_with_options?;
Repository::log_range(from, to) -> Result<CommitLog>
Get commits between two specific commits.
// Get all commits between two hashes
let range_commits = repo.log_range?;
println!;
Repository::log_for_paths(paths) -> Result<CommitLog>
Get commits that affected specific files or directories.
// Get commits that modified specific files
let file_commits = repo.log_for_paths?;
// Get commits that affected a directory
let dir_commits = repo.log_for_paths?;
Repository::show_commit(hash) -> Result<CommitDetails>
Get detailed information about a specific commit including file changes.
let details = repo.show_commit?;
println!;
println!;
println!;
println!;
if let Some = &details.commit.message.body
println!;
for file in &details.files_changed
println!;
Commit Types and Filtering
The commit API provides rich types for working with commit data:
// Commit represents a single commit
// Author information with timestamp
// Parsed commit message
// Detailed commit information
CommitLog Filtering
CommitLog
provides iterator-based filtering methods:
let commits = repo.log?;
// Filter by message content
let bug_fixes: = commits.with_message_containing.collect;
let features: = commits.with_message_containing.collect;
// Filter by date
use ;
let last_week = now - weeks;
let recent: = commits.since.collect;
// Filter by commit type
let merge_commits: = commits.merges_only.collect;
let regular_commits: = commits.no_merges.collect;
// Search by hash
if let Some = commits.find_by_hash
if let Some = commits.find_by_short_hash
LogOptions Builder
LogOptions
provides a builder pattern for advanced queries:
let options = new
.max_count // Limit number of commits
.since // Since date
.until // Until date
.author // Filter by author
.committer // Filter by committer
.grep // Search in commit messages
.follow_renames // Follow file renames
.merges_only // Only merge commits
.no_merges // Exclude merge commits
.paths; // Filter by paths
let filtered_commits = repo.log_with_options?;
Hash Type
The Hash
type represents Git object hashes (commits, trees, blobs, etc.).
let hash = repo.commit?;
// 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!; // Displays full hash
Error Handling
All operations return Result<T, GitError>
for proper error handling.
use ;
match repo.commit
Complete Workflow Example
use ;
use fs;
Examples
The examples/
directory contains comprehensive demonstrations of library functionality:
Running Examples
# Complete workflow from init to commit
# Repository lifecycle operations
# Enhanced status API with staged/unstaged tracking
# Staging operations (add, add_all, add_update)
# Commit workflows and Hash type usage
# Branch operations (create, delete, checkout, list)
# Repository configuration management
# Commit history and log operations with advanced querying
# Error handling patterns and recovery strategies
Example Files
basic_usage.rs
- Demonstrates the fundamental rustic-git workflow: initialize a repository, create files, check status, stage changes, and create commitsrepository_operations.rs
- Shows repository lifecycle operations including initializing regular and bare repositories, opening existing repos, and handling errorsstatus_checking.rs
- Comprehensive demonstration of GitStatus and FileStatus usage with all query methods and filtering capabilitiesstaging_operations.rs
- Shows all staging methods (add, add_all, add_update) with before/after status comparisonscommit_workflows.rs
- Demonstrates commit operations and Hash type methods, including custom authors and hash managementbranch_operations.rs
- Complete branch management demonstration: create, checkout, delete branches, and BranchList filteringconfig_operations.rs
- Repository configuration management demonstration: user setup, configuration values, and repository-scoped settingscommit_history.rs
- Comprehensive commit history & log operations showing all querying APIs, filtering, analysis, and advanced LogOptions usageerror_handling.rs
- Comprehensive error handling patterns showing GitError variants, recovery strategies, and best practices
All examples use OS-appropriate temporary directories and include automatic cleanup for safe execution.
Testing
Run the test suite:
All tests create temporary repositories in OS-appropriate temporary directories and clean up after themselves.
Contributing
We welcome contributions! Please follow these guidelines when contributing to rustic-git:
Code Standards
- Rust Edition: Use Rust edition 2024
- Style Guide: Follow the Rust style guide for naming conventions and formatting
- Code Quality: Implement best practices for code organization and maintainability
- No Emojis: Do not use emoji in code or commit messages
Design Principles
- Repository-centric API: Static lifecycle methods (
init
,open
) returnRepository
instances, instance methods for git operations - Module-based organization: Separate files for repository.rs, error.rs, with lib.rs for re-exports only
- Co-located unit tests: Tests within each module (
#[cfg(test)] mod tests
) rather than separate test files - Early validation: Always call
Repository::ensure_git()
before git operations to validate git availability - Path handling: Use
PathBuf
for internal storage,&Path
for method parameters and returns,impl AsRef<Path>
for flexibility - Error handling: Custom
GitError
enum withFrom<io::Error>
trait for ergonomic error propagation - Command execution: Use
std::process::Command
with proper error handling and stderr capture
Development Workflow
Before submitting a pull request, ensure your code passes all checks:
# Format code
# Build project
# Run all tests
# Run linting (no warnings allowed)
# Verify all examples work
Pull Request Guidelines
- Ensure all tests pass and examples run successfully
- Follow conventional commit format:
type(scope): description
- Use types like
feat
,fix
,docs
,style
,refactor
,test
,chore
- Keep commit messages concise and in present tense
- Make sure your changes align with the project's design principles
Roadmap
Future planned features:
- Diff operations
- Remote operations (clone, push, pull)
- Merge and rebase operations
- Tag operations
- Stash operations
Status
rustic-git provides a complete git workflow including repository management, status checking, staging operations, commits, branch operations, and commit history analysis.