Expand description
§sublime_git_tools
A high-level Rust interface to Git operations with robust error handling, built on libgit2.
§Overview
sublime_git_tools provides a user-friendly API for working with Git repositories. It wraps the
powerful but complex libgit2 library to offer a more ergonomic interface for common Git operations.
This crate is designed for Rust applications that need to:
- Create, clone, or manipulate Git repositories
- Manage branches, commits, and tags
- Track file changes between commits or branches
- Push/pull with remote repositories
- Get detailed commit histories
- Detect changes in specific parts of a repository
§Main Features
§Repository Management
use sublime_git_tools::Repo;
// Create a new repository
let repo = Repo::create("/path/to/new/repo")?;
// Open an existing repository
let repo = Repo::open("./my-project")?;
// Clone a remote repository
let repo = Repo::clone("https://github.com/example/repo.git", "./cloned-repo")?;§Branch and Commit Operations
use sublime_git_tools::Repo;
// Create a new branch
repo.create_branch("feature/new-feature")?;
// Checkout a branch
repo.checkout("feature/new-feature")?;
// Add files and commit
repo.add("src/main.rs")?;
let commit_id = repo.commit("feat: update main.rs")?;
// Or add all changes and commit in one step
let commit_id = repo.commit_changes("feat: implement new feature")?;§File Change Detection
use sublime_git_tools::Repo;
// Get all changed files since a tag or commit
let changed_files = repo.get_all_files_changed_since_sha("HEAD~1")?;
// Get all changed files with their status (Added, Modified, Deleted)
let changed_files_with_status = repo
.get_all_files_changed_since_sha_with_status("HEAD~1")?;
// Get changes in specific packages since a branch
let packages = vec!["packages/pkg1".to_string(), "packages/pkg2".to_string()];
let package_changes = repo
.get_all_files_changed_since_branch(&packages, "main")?;§Commit History
use sublime_git_tools::Repo;
// Get all commits since a specific tag
let commits = repo.get_commits_since(
Some("HEAD~1".to_string()),
&None
)?;
// Get commits affecting a specific file
let file_commits = repo.get_commits_since(
None,
&Some("src/main.rs".to_string())
)?;§sublime_git_tools API Specification
§Table of Contents
- Overview
- Repository Management
- Branch Operations
- Commit Operations
- Tag Operations
- File Status and Change Detection
- Remote Operations
- Advanced Git Operations
- Types Reference
- Error Handling
- Examples
§Overview
sublime_git_tools is a high-level Rust interface to Git operations with robust error handling, built on libgit2. It provides a user-friendly API for working with Git repositories, wrapping the powerful but complex libgit2 library to offer a more ergonomic interface for common Git operations.
This crate is designed for Rust applications that need to:
- Create, clone, or manipulate Git repositories
- Manage branches, commits, and tags
- Track file changes between commits or branches
- Push/pull with remote repositories
- Get detailed commit histories
- Detect changes in specific parts of a repository
- Handle SSH authentication for remote operations
- Perform advanced Git operations like merging and status checking
§Repository Management
§Creating and Opening Repositories
§Repo::create
Creates a new Git repository at the specified path with an initial commit on the ‘main’ branch.
pub fn create(path: &str) -> Result<Self, RepoError>Parameters:
path: The path where the repository should be created
Returns:
Result<Self, RepoError>: A newRepoinstance or an error
Example:
let repo = Repo::create("/path/to/new/repo")?;
println!("Repository created at: {}", repo.get_repo_path().display());Possible errors:
CanonicalPathFailure: Failed to canonicalize the provided pathCreateRepoFailure: Failed to initialize the Git repository
§Repo::open
Opens an existing Git repository at the specified path.
pub fn open(path: &str) -> Result<Self, RepoError>Parameters:
path: The path to the existing repository
Returns:
Result<Self, RepoError>: ARepoinstance or an error
Example:
let repo = Repo::open("./my-project")?;
let branch = repo.get_current_branch()?;
println!("Current branch: {}", branch);Possible errors:
CanonicalPathFailure: Failed to canonicalize the provided pathOpenRepoFailure: Failed to open the Git repository
§Repo::clone
Clones a Git repository from a URL to a local path.
pub fn clone(url: &str, path: &str) -> Result<Self, RepoError>Parameters:
url: The URL of the repository to clonepath: The local path where the repository should be cloned
Returns:
Result<Self, RepoError>: ARepoinstance or an error
Example:
let repo = Repo::clone("https://github.com/example/repo.git", "./cloned-repo")?;Possible errors:
CanonicalPathFailure: Failed to canonicalize the provided pathCloneRepoFailure: Failed to clone the Git repository
§Repo::clone_with_options
Clones a Git repository from a URL to a local path with advanced options including shallow clone support.
pub fn clone_with_options(
url: &str,
path: &str,
depth: Option<i32>
) -> Result<Self, RepoError>Parameters:
url: The URL of the repository to clonepath: The local path where the repository should be cloneddepth: Optional depth for shallow clone (e.g.,Some(1)for only the latest commit,Nonefor full clone)
Returns:
Result<Self, RepoError>: ARepoinstance or an error
Examples:
// Full clone (equivalent to Repo::clone)
let repo = Repo::clone_with_options(
"https://github.com/example/repo.git",
"./cloned-repo",
None
)?;
// Shallow clone with depth 1 (only latest commit)
let repo = Repo::clone_with_options(
"https://github.com/example/large-repo.git",
"./shallow-clone",
Some(1)
)?;
// Shallow clone with depth 10 (last 10 commits)
let repo = Repo::clone_with_options(
"https://github.com/example/repo.git",
"./partial-clone",
Some(10)
)?;Performance Benefits: Shallow clones can significantly reduce:
- Clone time (especially for repositories with extensive history)
- Disk space usage
- Network bandwidth consumption
This is particularly useful for:
- CI/CD pipelines that only need the latest code
- Deployment scenarios
- Quick repository inspection
- Limited disk space environments
Limitations:
- Cannot push from a shallow clone without converting to full clone first
- Some operations requiring full history may fail
- Can be converted to full clone later using
git fetch --unshallow
Possible errors:
CanonicalPathFailure: Failed to canonicalize the provided pathCloneRepoFailure: Failed to clone the Git repository- Network connection failures
- Authentication failures
- Insufficient disk space
§Repo::clone_with_progress
Clones a Git repository with real-time progress tracking callbacks.
pub fn clone_with_progress<F>(
url: &str,
path: &str,
depth: Option<i32>,
progress_cb: F
) -> Result<Self, RepoError>
where
F: FnMut(usize, usize) + 'staticParameters:
url: The URL of the repository to clonepath: The local path where the repository should be cloneddepth: Optional depth for shallow cloneprogress_cb: Callback function that receives(current_objects, total_objects)for progress updates
Returns:
Result<Self, RepoError>: ARepoinstance or an error
Examples:
use sublime_git_tools::Repo;
// Clone with progress updates
let repo = Repo::clone_with_progress(
"https://github.com/example/repo.git",
"./cloned-repo",
None,
|current, total| {
println!("Progress: {}/{} objects ({:.1}%)",
current, total,
(current as f64 / total as f64) * 100.0);
}
)?;
// Shallow clone with progress
let repo = Repo::clone_with_progress(
"https://github.com/example/large-repo.git",
"./shallow",
Some(1),
|current, total| {
if total > 0 {
let percent = (current as f64 / total as f64) * 100.0;
print!("\rReceiving objects: {:.0}%", percent);
}
}
)?;Progress Callback Details: The callback is invoked periodically during the clone operation with:
current: Number of objects received so fartotal: Total number of objects to receive (may be 0 initially)
The callback is called during:
- Receiving objects from the remote
- Resolving deltas
- Indexing objects
Possible errors:
CanonicalPathFailure: Failed to canonicalize the provided pathCloneRepoFailure: Failed to clone the Git repository- Network connection failures
- Authentication failures
- Insufficient disk space
§Repository Information
§Repo::get_repo_path
Returns the path to the repository root directory.
pub fn get_repo_path(&self) -> &PathReturns:
&Path: Reference to the repository path
Example:
let repo = Repo::open("./my-project")?;
let path = repo.get_repo_path();
println!("Repository path: {}", path.display());§Configuration
§Repo::config
Sets the Git configuration for username and email in the repository.
pub fn config(&self, username: &str, email: &str) -> Result<&Self, RepoError>Parameters:
username: The Git username to setemail: The Git email to set
Returns:
Result<&Self, RepoError>: Reference to self for chaining or an error
Example:
repo.config("Jane Doe", "jane@example.com")?;Possible errors:
ConfigError: Failed to access or modify Git configuration
§Repo::list_config
Lists all configuration entries in the repository.
pub fn list_config(&self) -> Result<HashMap<String, String>, RepoError>Returns:
Result<HashMap<String, String>, RepoError>: Map of configuration key-value pairs or an error
Example:
let config_entries = repo.list_config()?;
for (key, value) in config_entries {
println!("{}: {}", key, value);
}Possible errors:
ConfigError: Failed to access Git configurationConfigEntriesError: Failed to retrieve configuration entries
§Branch Operations
§Creating and Managing Branches
§Repo::create_branch
Creates a new branch from the current HEAD commit.
pub fn create_branch(&self, branch_name: &str) -> Result<&Self, RepoError>Parameters:
branch_name: The name of the new branch to create
Returns:
Result<&Self, RepoError>: Reference to self for chaining or an error
Example:
repo.create_branch("feature/new-feature")?;Possible errors:
HeadError: Failed to get repository HEADPeelError: Failed to peel HEAD to commitBranchError: Failed to create the branch
§Repo::checkout
Switches to the specified branch.
pub fn checkout(&self, branch_name: &str) -> Result<&Self, RepoError>Parameters:
branch_name: The name of the branch to checkout
Returns:
Result<&Self, RepoError>: Reference to self for chaining or an error
Example:
repo.checkout("feature-branch")?;Possible errors:
BranchError: Failed to find the branchCheckoutBranchError: Failed to checkout the branchCheckoutError: Failed to perform checkout
§Branch Information
§Repo::list_branches
Lists all local branches in the repository.
pub fn list_branches(&self) -> Result<Vec<String>, RepoError>Returns:
Result<Vec<String>, RepoError>: Vector of branch names or an error
Example:
let branches = repo.list_branches()?;
for branch in branches {
println!("Branch: {}", branch);
}Possible errors:
BranchListError: Failed to list branchesBranchNameError: Failed to get branch name
§Repo::get_current_branch
Gets the name of the currently checked out branch.
pub fn get_current_branch(&self) -> Result<String, RepoError>Returns:
Result<String, RepoError>: The current branch name or an error
Example:
let current_branch = repo.get_current_branch()?;
println!("Current branch: {}", current_branch);Possible errors:
HeadError: Failed to get repository HEADBranchNameError: Failed to get branch name from HEAD
§Repo::branch_exists
Checks if a branch exists in the repository.
pub fn branch_exists(&self, branch_name: &str) -> Result<bool, RepoError>Parameters:
branch_name: The name of the branch to check
Returns:
Result<bool, RepoError>: True if branch exists, false otherwise, or an error
Example:
if repo.branch_exists("main")? {
println!("Branch 'main' exists");
}Possible errors:
BranchError: Failed to access branch information
§Repo::get_branch_from_commit
Gets the branch name that contains a specific commit.
pub fn get_branch_from_commit(&self, sha: &str) -> Result<Option<String>, RepoError>Parameters:
sha: The commit SHA to search for
Returns:
Result<Option<String>, RepoError>: Branch name if found, None if not found, or an error
Example:
if let Some(branch) = repo.get_branch_from_commit("abcdef123456")? {
println!("Commit is on branch: {}", branch);
}Possible errors:
CommitOidError: Failed to parse commit SHABranchListError: Failed to list branchesBranchNameError: Failed to get branch name
§Repo::get_branches_containing_commit
Gets all branches that contain a specific commit.
pub fn get_branches_containing_commit(&self, sha: &str) -> Result<Vec<String>, RepoError>Parameters:
sha: The commit SHA to search for
Returns:
Result<Vec<String>, RepoError>: Vector of branch names containing the commit or an error
Example:
let branches = repo.get_branches_containing_commit("abcdef123456")?;
for branch in branches {
println!("Branch {} contains the commit", branch);
}Possible errors:
CommitOidError: Failed to parse commit SHABranchListError: Failed to list branchesGraphError: Failed to perform graph operations
§Commit Operations
§Staging and Committing
§Repo::add
Adds a specific file to the staging area.
pub fn add(&self, file_path: &str) -> Result<&Self, RepoError>Parameters:
file_path: The path to the file to add
Returns:
Result<&Self, RepoError>: Reference to self for chaining or an error
Example:
repo.add("src/main.rs")?;Possible errors:
IndexError: Failed to access the indexAddFilesError: Failed to add file to indexWriteIndexError: Failed to write index
§Repo::add_all
Adds all changed files to the staging area.
pub fn add_all(&self) -> Result<&Self, RepoError>Returns:
Result<&Self, RepoError>: Reference to self for chaining or an error
Example:
repo.add_all()?;Possible errors:
IndexError: Failed to access the indexAddFilesError: Failed to add files to indexWriteIndexError: Failed to write index
§Repo::commit
Creates a commit with the currently staged changes.
pub fn commit(&self, message: &str) -> Result<String, RepoError>Parameters:
message: The commit message
Returns:
Result<String, RepoError>: The commit SHA or an error
Example:
let commit_id = repo.commit("feat: add new functionality")?;
println!("Created commit: {}", commit_id);Possible errors:
SignatureError: Failed to get repository signatureHeadError: Failed to get repository HEADIndexError: Failed to access the indexTreeError: Failed to create tree from indexCommitError: Failed to create commit
§Repo::commit_changes
Adds all changes and creates a commit in one operation.
pub fn commit_changes(&self, message: &str) -> Result<String, RepoError>Parameters:
message: The commit message
Returns:
Result<String, RepoError>: The commit SHA or an error
Example:
let commit_id = repo.commit_changes("fix: resolve authentication issue")?;
println!("Created commit: {}", commit_id);Possible errors:
SignatureError: Failed to get repository signatureHeadError: Failed to get repository HEADIndexError: Failed to access the indexAddFilesError: Failed to add files to indexWriteIndexError: Failed to write indexTreeError: Failed to create tree from indexCommitError: Failed to create commit
§Commit Information
§Repo::get_current_sha
Gets the SHA of the current HEAD commit.
pub fn get_current_sha(&self) -> Result<String, RepoError>Returns:
Result<String, RepoError>: The current commit SHA or an error
Example:
let current_sha = repo.get_current_sha()?;
println!("Current commit: {}", current_sha);Possible errors:
HeadError: Failed to get repository HEADCommitOidError: Failed to get commit OID
§Repo::get_previous_sha
Gets the SHA of the previous commit (parent of HEAD).
pub fn get_previous_sha(&self) -> Result<String, RepoError>Returns:
Result<String, RepoError>: The previous commit SHA or an error
Example:
let previous_sha = repo.get_previous_sha()?;
println!("Previous commit: {}", previous_sha);Possible errors:
HeadError: Failed to get repository HEADPeelError: Failed to peel HEAD to commitCommitOidError: Failed to get commit OID
§Commit History
§Repo::get_commits_since
Gets commits since a specific reference, optionally filtered by file path.
pub fn get_commits_since(
&self,
since: Option<String>,
path: &Option<String>
) -> Result<Vec<RepoCommit>, RepoError>Parameters:
since: Optional reference (tag, branch, or commit SHA) to start frompath: Optional file path to filter commits
Returns:
Result<Vec<RepoCommit>, RepoError>: Vector of commits or an error
Example:
// Get all commits since a tag
let commits = repo.get_commits_since(Some("v1.0.0".to_string()), &None)?;
// Get commits affecting a specific file
let file_commits = repo.get_commits_since(None, &Some("src/main.rs".to_string()))?;Possible errors:
RevWalkError: Failed to perform revision walkReferenceError: Failed to resolve referenceCommitOidError: Failed to parse commit
§Repo::get_commits_between
Gets commits between two references (commits in to_ref but not in from_ref).
pub fn get_commits_between(
&self,
from_ref: &str,
to_ref: &str,
relative: &Option<String>
) -> Result<Vec<RepoCommit>, RepoError>Parameters:
from_ref: Starting reference (commits after this point)to_ref: Ending reference (commits up to this point)relative: Optional file path to filter commits
Returns:
Result<Vec<RepoCommit>, RepoError>: Vector of commits between references
Example:
// Get commits between two tags
let commits = repo.get_commits_between("v1.0.0", "v1.1.0", &None)?;
// Get commits between branches affecting specific path
let path_commits = repo.get_commits_between("main", "feature-branch", &Some("src/".to_string()))?;Possible errors:
RevWalkError: Failed to perform revision walkReferenceError: Failed to resolve referenceCommitOidError: Failed to parse commitPeelError: Failed to peel reference to commit
§Tag Operations
§Creating Tags
§Repo::create_tag
Creates a new tag at the current HEAD commit.
pub fn create_tag(&self, tag: &str, message: Option<String>) -> Result<&Self, RepoError>Parameters:
tag: The name of the tag to createmessage: Optional message for annotated tag (None for lightweight tag)
Returns:
Result<&Self, RepoError>: Reference to self for chaining or an error
Example:
// Create annotated tag
repo.create_tag("v1.0.0", Some("Release version 1.0.0".to_string()))?;
// Create lightweight tag
repo.create_tag("v1.0.1", None)?;Possible errors:
SignatureError: Failed to get repository signatureHeadError: Failed to get repository HEADPeelError: Failed to peel HEAD to commitCreateTagError: Failed to create tag
§Tag Information
§Repo::get_last_tag
Gets the most recent tag in the repository.
pub fn get_last_tag(&self) -> Result<String, RepoError>Returns:
Result<String, RepoError>: The most recent tag name or an error
Example:
let last_tag = repo.get_last_tag()?;
println!("Last tag: {}", last_tag);Possible errors:
LastTagError: Failed to get tags or no tags found
§Repo::get_remote_or_local_tags
Gets tags from local or remote repositories.
pub fn get_remote_or_local_tags(
&self,
local: Option<bool>
) -> Result<Vec<RepoTags>, RepoError>Parameters:
local: Optional flag to specify local (true) or remote (false) tags. None for both.
Returns:
Result<Vec<RepoTags>, RepoError>: Vector of tags or an error
Example:
// Get local tags
let local_tags = repo.get_remote_or_local_tags(Some(true))?;
// Get remote tags
let remote_tags = repo.get_remote_or_local_tags(Some(false))?;
// Get all tags
let all_tags = repo.get_remote_or_local_tags(None)?;Possible errors:
TagError: Failed to retrieve tagsRemoteError: Failed to access remote (for remote tags)
§File Status and Change Detection
§Repository Status
§Repo::status_porcelain
Gets the repository status in porcelain format (simple, parseable format).
pub fn status_porcelain(&self) -> Result<Vec<String>, RepoError>Returns:
Result<Vec<String>, RepoError>: Vector of status lines or an error
Example:
let status_lines = repo.status_porcelain()?;
for line in status_lines {
println!("{}", line);
}Possible errors:
StatusError: Failed to get repository status
§Repo::get_status_detailed
Gets detailed status information for all files in the repository.
pub fn get_status_detailed(&self) -> Result<Vec<GitChangedFile>, RepoError>Returns:
Result<Vec<GitChangedFile>, RepoError>: Vector of file status information or an error
Example:
let detailed_status = repo.get_status_detailed()?;
for file in detailed_status {
println!("File: {}, Status: {:?}, Staged: {}, Workdir: {}",
file.path, file.status, file.staged, file.workdir);
}Possible errors:
StatusError: Failed to get repository status
§Repo::get_staged_files
Gets a list of files that are currently staged for commit.
pub fn get_staged_files(&self) -> Result<Vec<String>, RepoError>Returns:
Result<Vec<String>, RepoError>: Vector of staged file paths or an error
Example:
let staged_files = repo.get_staged_files()?;
println!("Files ready for commit: {:?}", staged_files);Possible errors:
StatusError: Failed to get repository status
§Changed Files
§Repo::get_all_files_changed_since_sha_with_status
Gets all files that changed since a specific commit/reference with their status information.
pub fn get_all_files_changed_since_sha_with_status(
&self,
git_ref: &str
) -> Result<Vec<GitChangedFile>, RepoError>Parameters:
git_ref: The reference (commit SHA, tag, or branch) to compare against
Returns:
Result<Vec<GitChangedFile>, RepoError>: Vector of changed files with status or an error
Example:
let changed_files = repo.get_all_files_changed_since_sha_with_status("v1.0.0")?;
for file in changed_files {
println!("Changed file: {} ({:?})", file.path, file.status);
}Possible errors:
ReferenceError: Failed to resolve referenceDiffError: Failed to perform diff operationCommitOidError: Failed to parse commit
§Repo::get_all_files_changed_since_sha
Gets a simple list of files that changed since a specific commit/reference.
pub fn get_all_files_changed_since_sha(&self, git_ref: &str) -> Result<Vec<String>, RepoError>Parameters:
git_ref: The reference (commit SHA, tag, or branch) to compare against
Returns:
Result<Vec<String>, RepoError>: Vector of changed file paths or an error
Example:
let changed_files = repo.get_all_files_changed_since_sha("v1.0.0")?;
for file in changed_files {
println!("Changed: {}", file);
}Possible errors:
ReferenceError: Failed to resolve referenceDiffError: Failed to perform diff operation
§Repo::get_files_changed_between
Gets files changed between two references.
pub fn get_files_changed_between(&self, from_ref: &str, to_ref: &str) -> Result<Vec<GitChangedFile>, RepoError>Parameters:
from_ref: The starting referenceto_ref: The ending reference
Returns:
Result<Vec<GitChangedFile>, RepoError>: Vector of changed files with status or an error
Example:
let files = repo.get_files_changed_between("main", "feature-branch")?;
for file in files {
println!("File: {}, Status: {:?}", file.path, file.status);
}Possible errors:
ReferenceError: Failed to resolve referencesDiffError: Failed to perform diff operation
§Repo::get_files_changed_in_commit
Gets files changed in a specific commit.
pub fn get_files_changed_in_commit(&self, commit_hash: &str) -> Result<Vec<GitChangedFile>, RepoError>Parameters:
commit_hash: The commit hash to analyze
Returns:
Result<Vec<GitChangedFile>, RepoError>: Vector of changed files with status or an error
Example:
let files = repo.get_files_changed_in_commit("abc123")?;
for file in files {
println!("File: {}, Status: {:?}", file.path, file.status);
}Possible errors:
ReferenceError: Failed to resolve commit referenceCommitError: Failed to access commitTreeError: Failed to access treeDiffError: Failed to perform diff operation
§Package-specific Changes
§Repo::get_all_files_changed_since_branch
Gets files changed in specific package directories since a branch.
pub fn get_all_files_changed_since_branch(
&self,
packages_paths: &[String],
branch: &str
) -> Result<Vec<String>, RepoError>Parameters:
packages_paths: Vector of package directory paths to checkbranch: The branch to compare against
Returns:
Result<Vec<String>, RepoError>: Vector of changed files in the specified packages or an error
Example:
let packages = vec!["packages/pkg1".to_string(), "packages/pkg2".to_string()];
let package_changes = repo.get_all_files_changed_since_branch(&packages, "main")?;
for file in package_changes {
println!("Package change: {}", file);
}Possible errors:
ReferenceError: Failed to resolve branch referenceDiffError: Failed to perform diff operation
§Remote Operations
§Pushing and Pulling
§Repo::push
Pushes the current branch to a remote repository.
pub fn push(&self, remote_name: &str, follow_tags: Option<bool>) -> Result<bool, RepoError>Parameters:
remote_name: The name of the remote (e.g., “origin”)follow_tags: Optional flag to push tags along with commits
Returns:
Result<bool, RepoError>: True if push was successful, or an error
Example:
// Push without tags
let success = repo.push("origin", Some(false))?;
// Push with tags
repo.push("origin", Some(true))?;Possible errors:
HeadError: Failed to get repository HEADBranchNameError: Failed to get branch nameRemoteError: Failed to find or access remotePushError: Failed to push to remote
§Repo::pull
Pulls changes from a remote repository.
pub fn pull(&self, remote_name: &str, branch_name: Option<&str>) -> Result<bool, RepoError>Parameters:
remote_name: The name of the remote (e.g., “origin”)branch_name: Optional specific branch to pull (current branch if None)
Returns:
Result<bool, RepoError>: True if pull was successful, or an error
Example:
// Pull current branch
let success = repo.pull("origin", None)?;
// Pull specific branch
repo.pull("origin", Some("feature-branch"))?;Possible errors:
RemoteError: Failed to find or access remoteHeadError: Failed to get repository HEADMergeError: Failed to merge pulled changes
§Fetching
§Repo::fetch
Fetches changes from a remote repository without merging.
pub fn fetch(
&self,
remote_name: &str,
refspecs: Option<&[&str]>,
prune: bool
) -> Result<bool, RepoError>Parameters:
remote_name: The name of the remote (e.g., “origin”)refspecs: Optional reference specs to fetch (all refs if None)prune: Whether to prune deleted remote branches
Returns:
Result<bool, RepoError>: True if fetch was successful, or an error
Example:
// Fetch all refs
repo.fetch("origin", None, false)?;
// Fetch specific refspecs with pruning
repo.fetch("origin", Some(&["refs/heads/main:refs/remotes/origin/main"]), true)?;Possible errors:
RemoteError: Failed to find or access remoteGitFailure: Failed to fetch from remote
§SSH Operations
§Repo::push_with_ssh_config
Pushes to a remote repository using SSH authentication with custom key paths.
pub fn push_with_ssh_config(
&self,
remote_name: &str,
follow_tags: Option<bool>,
ssh_key_paths: Vec<PathBuf>
) -> Result<bool, RepoError>Parameters:
remote_name: The name of the remote (e.g., “origin”)follow_tags: Optional flag to push tags along with commitsssh_key_paths: Vector of SSH key file paths to try in order
Returns:
Result<bool, RepoError>: True if push was successful, or an error
Example:
use std::path::PathBuf;
let key_paths = vec![
PathBuf::from("/home/user/.ssh/id_ed25519"),
PathBuf::from("/home/user/.ssh/id_rsa"),
];
let success = repo.push_with_ssh_config("origin", Some(true), key_paths)?;Possible errors:
HeadError: Failed to get repository HEADBranchNameError: Failed to get branch nameRemoteError: Failed to find or access remotePushError: Failed to push to remote
§Advanced Git Operations
§Merging
§Repo::merge
Merges a branch into the current branch.
pub fn merge(&self, branch_name: &str) -> Result<(), RepoError>Parameters:
branch_name: The name of the branch to merge
Returns:
Result<(), RepoError>: Success or an error
Example:
match repo.merge("feature-branch") {
Ok(_) => println!("Merge completed successfully"),
Err(RepoError::MergeConflictError(_)) => {
println!("Merge conflicts detected");
},
Err(e) => println!("Merge failed: {}", e),
}Possible errors:
HeadError: Failed to get repository HEADBranchError: Failed to find branchMergeError: Failed to perform mergeMergeConflictError: Merge conflicts detected
§Repository Analysis
§Repo::get_merge_base
Finds the merge base (common ancestor) between two branches.
pub fn get_merge_base(&self, branch1: &str, branch2: &str) -> Result<String, RepoError>Parameters:
branch1: The first branch namebranch2: The second branch name
Returns:
Result<String, RepoError>: The merge base commit SHA or an error
Example:
let merge_base = repo.get_merge_base("main", "feature-branch")?;
println!("Merge base: {}", merge_base);Possible errors:
ReferenceError: Failed to resolve branch referencesGraphError: Failed to find merge baseCommitOidError: Failed to get commit OID
§Repo::get_diverged_commit
Finds the common ancestor commit between the current branch and a reference.
pub fn get_diverged_commit(&self, git_ref: &str) -> Result<String, RepoError>Parameters:
git_ref: The reference to compare against (branch, tag, or commit SHA)
Returns:
Result<String, RepoError>: The common ancestor commit SHA or an error
Example:
let diverged_commit = repo.get_diverged_commit("feature-branch")?;
println!("Common ancestor: {}", diverged_commit);Possible errors:
ReferenceError: Failed to resolve referenceHeadError: Failed to get repository HEADGraphError: Failed to find common ancestorCommitOidError: Failed to get commit OID
§Types Reference
§Repository Types
§Repo
The main repository struct that wraps libgit2 functionality.
pub struct Repo {
repo: Repository,
local_path: PathBuf,
}Fields:
repo: Internal libgit2 repository handlelocal_path: Path to the repository root directory
§File Status Types
§GitFileStatus
Represents the status of a file in Git.
pub enum GitFileStatus {
Added,
Modified,
Deleted,
Untracked,
}Variants:
Added: File has been added to the repositoryModified: File has been modifiedDeleted: File has been deletedUntracked: File is not tracked by Git
§GitChangedFile
Represents a changed file with detailed status information.
pub struct GitChangedFile {
pub path: String,
pub status: GitFileStatus,
pub staged: bool,
pub workdir: bool,
}Fields:
path: The path to the changed filestatus: The status of the file (Added, Modified, Deleted, or Untracked)staged: Whether the file is staged in the indexworkdir: Whether the file has changes in the working directory
§Commit and Tag Types
§RepoCommit
Represents a commit in the Git repository.
pub struct RepoCommit {
pub hash: String,
pub author_name: String,
pub author_email: String,
pub author_date: String,
pub message: String,
}Fields:
hash: The commit hash (SHA)author_name: The name of the commit authorauthor_email: The email of the commit authorauthor_date: The date of the commit in RFC2822 formatmessage: The commit message
§RepoTags
Represents a tag in the Git repository.
pub struct RepoTags {
pub hash: String,
pub tag: String,
}Fields:
hash: The hash of the commit that the tag points totag: The name of the tag
§Error Types
§RepoError
Comprehensive error type for all Git operations.
pub enum RepoError {
CanonicalPathFailure(std::io::Error),
GitFailure(git2::Error),
CreateRepoFailure(git2::Error),
OpenRepoFailure(git2::Error),
CloneRepoFailure(git2::Error),
ConfigError(git2::Error),
ConfigEntriesError(git2::Error),
HeadError(git2::Error),
PeelError(git2::Error),
BranchError(git2::Error),
SignatureError(git2::Error),
IndexError(git2::Error),
AddFilesError(git2::Error),
WriteIndexError(git2::Error),
TreeError(git2::Error),
CommitError(git2::Error),
WriteTreeError(git2::Error),
BranchListError(git2::Error),
BranchNameError(git2::Error),
CheckoutBranchError(git2::Error),
CheckoutError(git2::Error),
LastTagError(git2::Error),
CreateTagError(git2::Error),
StatusError(git2::Error),
CommitOidError(git2::Error),
GraphError(git2::Error),
PushError(git2::Error),
RemoteError(git2::Error),
ReferenceError(git2::Error),
DiffError(git2::Error),
RevWalkError(git2::Error),
TagError(git2::Error),
MergeError(git2::Error),
MergeConflictError(git2::Error),
}§Error Handling
All operations return Result<T, RepoError> to provide comprehensive error information. The error types are specific to the operation that failed, allowing for targeted error handling:
use sublime_git_tools::{Repo, RepoError};
match repo.checkout("feature-branch") {
Ok(_) => println!("Switched to feature-branch"),
Err(RepoError::BranchNameError(_)) => println!("Branch does not exist"),
Err(RepoError::CheckoutBranchError(_)) => println!("Failed to checkout branch"),
Err(e) => println!("Other error: {}", e),
}§Examples
§Basic Workflow
use sublime_git_tools::Repo;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create or open repository
let repo = Repo::create("/tmp/example-repo")?;
// Configure repository
repo.config("John Doe", "john@example.com")?;
// Create and commit changes
std::fs::write("/tmp/example-repo/README.md", "# Example Project")?;
repo.add("README.md")?;
let commit_id = repo.commit("Initial commit")?;
println!("Created commit: {}", commit_id);
Ok(())
}§Branch Management
use sublime_git_tools::Repo;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let repo = Repo::open("./my-project")?;
// Create and switch to feature branch
repo.create_branch("feature/new-feature")?;
repo.checkout("feature/new-feature")?;
// Make changes and commit
repo.add_all()?;
repo.commit("Add new feature")?;
// Switch back to main and merge
repo.checkout("main")?;
repo.merge("feature/new-feature")?;
// List all branches
let branches = repo.list_branches()?;
for branch in branches {
println!("Branch: {}", branch);
}
Ok(())
}§Tracking Changes
use sublime_git_tools::Repo;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let repo = Repo::open("./my-project")?;
// Get last tag
let last_tag = repo.get_last_tag()?;
println!("Last release: {}", last_tag);
// Get changes since last release
let changed_files = repo.get_all_files_changed_since_sha_with_status(&last_tag)?;
println!("Changes since {}:", last_tag);
for file in changed_files {
let status_char = match file.status {
GitFileStatus::Added => "+",
GitFileStatus::Modified => "M",
GitFileStatus::Deleted => "-",
GitFileStatus::Untracked => "?",
};
println!("{} {}", status_char, file.path);
}
// Get commit history
let commits = repo.get_commits_since(Some(last_tag), &None)?;
println!("\nCommits since last release:");
for commit in commits {
println!("- {} ({})", commit.message, &commit.hash[0..7]);
}
Ok(())
}§Remote Operations Examples
use sublime_git_tools::Repo;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Clone a repository
let repo = Repo::clone("https://github.com/example/repo.git", "/tmp/cloned-repo")?;
// Configure the repository
repo.config("Jane Doe", "jane@example.com")?;
// Create a new branch with changes
repo.create_branch("feature/remote-example")?;
repo.checkout("feature/remote-example")?;
// Make and commit changes
std::fs::write("/tmp/cloned-repo/example.txt", "Example content")?;
repo.add_all()?;
repo.commit("feat: add example file")?;
// Push the branch to remote
repo.push("origin", None)?;
// Fetch and pull from remote
repo.fetch("origin", None, false)?;
repo.checkout("main")?;
repo.pull("origin", None)?;
// List remote tags
let remote_tags = repo.get_remote_or_local_tags(Some(false))?;
println!("Remote tags:");
for tag in remote_tags {
println!("- {} ({})", tag.tag, &tag.hash[0..7]);
}
Ok(())
}§sublime_git_tools
A high-level Rust interface to Git operations with robust error handling, built on libgit2.
§Overview
sublime_git_tools provides a user-friendly API for working with Git repositories. It wraps the
powerful but complex libgit2 library to offer a more ergonomic interface for common Git operations.
This crate is designed for Rust applications that need to:
- Create, clone, or manipulate Git repositories
- Manage branches, commits, and tags
- Track file changes between commits or branches
- Push/pull with remote repositories
- Get detailed commit histories
- Detect changes in specific parts of a repository
- Handle SSH authentication for remote operations
- Perform advanced Git operations like merging and status checking
§Table of Contents
- Repository Management
- Configuration
- Branch Operations
- Commit Operations
- Tag Operations
- File Status and Change Detection
- Remote Operations
- Advanced Git Operations
- Error Handling
- Cross-Platform Support
§API Reference
For complete API documentation with detailed method signatures, parameters, return types, and comprehensive examples, see the SPEC.md file.
§Repository Management
§Creating, Opening, and Cloning Repositories
use sublime_git_tools::Repo;
// Create a new repository
let repo = Repo::create("/path/to/new/repo")?;
println!("Repository created at: {}", repo.get_repo_path().display());
// Open an existing repository
let repo = Repo::open("./my-project")?;
// Clone a remote repository
let repo = Repo::clone("https://github.com/example/repo.git", "./cloned-repo")?;
// Get repository path
let repo_path = repo.get_repo_path();
println!("Repository is located at: {}", repo_path.display());§Configuration
§Setting and Viewing Git Configuration
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Configure user name and email
repo.config("Jane Doe", "jane@example.com")?;
// List all configuration entries
let config_entries = repo.list_config()?;
for (key, value) in config_entries {
println!("{}: {}", key, value);
}§Branch Operations
§Creating and Managing Branches
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Create a new branch
repo.create_branch("feature/new-feature")?;
// Check if a branch exists
if repo.branch_exists("feature/new-feature")? {
println!("Branch exists!");
}
// Checkout a branch
repo.checkout("feature/new-feature")?;
// Get current branch name
let current_branch = repo.get_current_branch()?;
println!("Currently on branch: {}", current_branch);
// List all branches
let branches = repo.list_branches()?;
for branch in branches {
println!("Branch: {}", branch);
}§Advanced Branch Information
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Get branch from a specific commit
let commit_sha = "abcdef123456";
if let Some(branch) = repo.get_branch_from_commit(commit_sha)? {
println!("Commit {} is on branch: {}", commit_sha, branch);
}
// Get all branches containing a commit
let branches = repo.get_branches_containing_commit(commit_sha)?;
for branch in branches {
println!("Branch {} contains commit {}", branch, commit_sha);
}
// Find merge base between branches
let merge_base = repo.get_merge_base("main", "feature/new-feature")?;
println!("Merge base: {}", merge_base);§Commit Operations
§Staging and Committing
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Add a specific file
repo.add("src/main.rs")?;
// Add all changes
repo.add_all()?;
// Commit staged changes
let commit_id = repo.commit("feat: add new functionality")?;
println!("Created commit: {}", commit_id);
// Add all changes and commit in one step
let commit_id = repo.commit_changes("fix: resolve issue with authentication")?;
println!("Created commit: {}", commit_id);§Commit Information
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Get current commit SHA
let current_sha = repo.get_current_sha()?;
println!("Current commit: {}", current_sha);
// Get previous commit SHA
let previous_sha = repo.get_previous_sha()?;
println!("Previous commit: {}", previous_sha);
// Get commit history since a specific reference
let commits = repo.get_commits_since(Some("v1.0.0".to_string()), &None)?;
for commit in commits {
println!("{}: {} (by {} on {})",
commit.hash,
commit.message,
commit.author_name,
commit.author_date
);
}
// Get commits affecting a specific file
let file_commits = repo.get_commits_since(None, &Some("src/main.rs".to_string()))?;
for commit in file_commits {
println!("Commit {} modified src/main.rs: {}", commit.hash, commit.message);
}§Tag Operations
§Creating and Managing Tags
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Create an annotated tag
repo.create_tag("v1.0.0", Some("Release version 1.0.0".to_string()))?;
// Create a lightweight tag
repo.create_tag("v1.0.1", None)?;
// Get the last tag
let last_tag = repo.get_last_tag()?;
println!("Last tag: {}", last_tag);
// Get all local tags
let local_tags = repo.get_remote_or_local_tags(Some(true))?;
for tag in local_tags {
println!("Local tag: {} -> {}", tag.tag, tag.hash);
}
// Get all remote tags
let remote_tags = repo.get_remote_or_local_tags(Some(false))?;
for tag in remote_tags {
println!("Remote tag: {} -> {}", tag.tag, tag.hash);
}§File Status and Change Detection
§Repository Status
use sublime_git_tools::{Repo, GitFileStatus};
let repo = Repo::open("./my-project")?;
// Get porcelain status (simple format)
let status_lines = repo.status_porcelain()?;
for line in status_lines {
println!("{}", line);
}
// Get detailed status with file information
let detailed_status = repo.get_status_detailed()?;
for file in detailed_status {
println!("File: {}, Status: {:?}, Staged: {}, Workdir: {}",
file.path,
file.status,
file.staged,
file.workdir
);
}
// Get only staged files
let staged_files = repo.get_staged_files()?;
for file in staged_files {
println!("Staged: {}", file);
}§Change Detection Between References
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Get all changed files since a commit/tag with status
let changed_files = repo.get_all_files_changed_since_sha_with_status("v1.0.0")?;
for file in changed_files {
println!("Changed file: {} ({:?})", file.path, file.status);
}
// Get simple list of changed files since a commit/tag
let changed_files = repo.get_all_files_changed_since_sha("v1.0.0")?;
for file in changed_files {
println!("Changed: {}", file);
}
// Get files changed between two references
let files = repo.get_files_changed_between("main", "feature-branch")?;
for file in files {
println!("File: {}, Status: {:?}, Staged: {}, Workdir: {}",
file.path, file.status, file.staged, file.workdir);
}
// Get changes in specific packages/directories since a branch
let packages = vec!["packages/pkg1".to_string(), "packages/pkg2".to_string()];
let package_changes = repo.get_all_files_changed_since_branch(&packages, "main")?;
for file in package_changes {
println!("Package change: {}", file);
}§Remote Operations
§Basic Remote Operations
use sublime_git_tools::Repo;
use std::path::PathBuf;
let repo = Repo::open("./my-project")?;
// Push to remote (without tags)
let success = repo.push("origin", Some(false))?;
if success {
println!("Push completed successfully");
}
// Push with tags
repo.push("origin", Some(true))?;
// Fetch from remote
repo.fetch("origin", None, false)?;
// Fetch a specific branch
repo.fetch("origin", Some("feature-branch"), false)?;
// Pull from remote (current branch)
let success = repo.pull("origin", None)?;
if success {
println!("Pull completed successfully");
}
// Pull a specific branch
repo.pull("origin", Some("feature-branch"))?;§SSH Authentication
use sublime_git_tools::Repo;
use std::path::PathBuf;
let repo = Repo::open("./my-project")?;
// Push using SSH key paths (tries multiple keys in order)
let ssh_key_paths = vec![
PathBuf::from("/home/user/.ssh/id_ed25519"),
PathBuf::from("/home/user/.ssh/id_rsa"),
];
let success = repo.push_with_ssh_config("origin", Some(true), ssh_key_paths)?;
if success {
println!("SSH push completed successfully");
}§Advanced Git Operations
§Merging
use sublime_git_tools::{Repo, RepoError};
let repo = Repo::open("./my-project")?;
// Merge a branch
match repo.merge("feature-branch") {
Ok(_) => println!("Merge completed successfully"),
Err(RepoError::MergeConflictError(_)) => {
println!("Merge conflicts detected - manual resolution required");
// Handle conflicts...
},
Err(e) => println!("Merge failed: {}", e),
}
// Find diverged commit (common ancestor)
let diverged_commit = repo.get_diverged_commit("feature-branch")?;
println!("Common ancestor: {}", diverged_commit);§Error Handling
The crate uses a comprehensive error type (RepoError) that provides detailed information about Git operation failures:
use sublime_git_tools::{Repo, RepoError};
match Repo::open("/non/existent/path") {
Ok(repo) => {
// Repository operations...
match repo.checkout("non-existent-branch") {
Ok(_) => println!("Switched to branch successfully"),
Err(RepoError::BranchNameError(_)) => {
println!("Branch does not exist");
},
Err(RepoError::CheckoutBranchError(e)) => {
println!("Failed to checkout branch: {}", e);
},
Err(e) => println!("Other checkout error: {}", e),
}
},
Err(RepoError::OpenRepoFailure(e)) => {
println!("Failed to open repository: {}", e);
},
Err(e) => println!("Other error: {}", e),
}
// Handle merge conflicts
match repo.merge("feature-branch") {
Ok(_) => println!("Merge successful"),
Err(RepoError::MergeConflictError(_)) => {
println!("Merge conflicts require manual resolution");
// Get conflicted files
let status = repo.get_status_detailed()?;
for file in status {
if matches!(file.status, GitFileStatus::Modified) && file.staged && file.workdir {
println!("Conflict in: {}", file.path);
}
}
},
Err(RepoError::MergeError(e)) => {
println!("Merge failed: {}", e);
},
Err(e) => println!("Unexpected error: {}", e),
}§Available Error Types
The RepoError enum provides specific error variants for different Git operations:
CreateRepoFailure- Repository creation errorsOpenRepoFailure- Repository opening errorsCloneRepoFailure- Repository cloning errorsBranchError/BranchNameError/CheckoutBranchError- Branch operation errorsCommitError/CommitOidError- Commit operation errorsMergeError/MergeConflictError- Merge operation errorsPushError/RemoteError- Remote operation errorsTagError/CreateTagError/LastTagError- Tag operation errorsConfigError/ConfigEntriesError- Configuration errorsStatusError/DiffError- Status and diff errors- And many more for comprehensive error coverage
§Data Types
§Repository Types
use sublime_git_tools::{Repo, RepoCommit, RepoTags, GitChangedFile, GitFileStatus};
// RepoCommit - represents a commit
let commit = RepoCommit {
hash: "abcdef123456".to_string(),
author_name: "John Doe".to_string(),
author_email: "john@example.com".to_string(),
author_date: "Wed, 01 Jan 2023 12:00:00 +0000".to_string(),
message: "feat: add new feature".to_string(),
};
// RepoTags - represents a tag
let tag = RepoTags {
hash: "abcdef123456".to_string(),
tag: "v1.0.0".to_string(),
};
// GitChangedFile - represents a file with change information
let changed_file = GitChangedFile {
path: "src/main.rs".to_string(),
status: GitFileStatus::Modified,
staged: true,
workdir: false,
};
// GitFileStatus - file status enumeration
match changed_file.status {
GitFileStatus::Added => println!("File was added"),
GitFileStatus::Modified => println!("File was modified"),
GitFileStatus::Deleted => println!("File was deleted"),
GitFileStatus::Untracked => println!("File is untracked"),
}§Common Workflows
§Feature Branch Workflow
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// 1. Create and switch to feature branch
repo.create_branch("feature/user-authentication")?;
repo.checkout("feature/user-authentication")?;
// 2. Make changes and commit
repo.add("src/auth.rs")?;
repo.commit("feat: add user authentication module")?;
// 3. Push feature branch
repo.push("origin", Some(false))?;
// 4. Switch back to main and merge
repo.checkout("main")?;
repo.merge("feature/user-authentication")?;
// 5. Tag the release
repo.create_tag("v1.1.0", Some("Add user authentication".to_string()))?;
repo.push("origin", Some(true))?; // Push with tags§Change Detection Workflow
use sublime_git_tools::Repo;
let repo = Repo::open("./my-project")?;
// Check what changed since last release
let last_tag = repo.get_last_tag()?;
let changed_files = repo.get_all_files_changed_since_sha_with_status(&last_tag)?;
println!("Changes since {}: ", last_tag);
for file in changed_files {
match file.status {
GitFileStatus::Added => println!("+ {}", file.path),
GitFileStatus::Modified => println!("M {}", file.path),
GitFileStatus::Deleted => println!("- {}", file.path),
GitFileStatus::Untracked => println!("? {}", file.path),
}
}
// Get commits for changelog
let commits = repo.get_commits_since(Some(last_tag), &None)?;
for commit in commits {
println!("- {} ({})", commit.message, commit.hash[..8].to_string());
}§Cross-Platform Support
The crate is designed to work on all major platforms:
- Windows - Full support with vendored OpenSSL and libgit2
- macOS - Full support with vendored dependencies
- Linux - Full support with vendored dependencies
SSH operations are supported across all platforms with the vendored SSH implementation.
§Performance Considerations
- The crate uses vendored libgit2 for consistent behavior across platforms
- Repository operations are optimized for common use cases
- Large repositories may benefit from specific Git configuration tuning
- File change detection operations scale with repository size
§License
This crate is licensed under the terms specified in the workspace configuration.
§Contributing
Please see the workspace-level contribution guidelines for information on how to contribute to this crate.
Structs§
- GitChanged
File - Represents a changed file in the Git repository with staging information
- GitDiff
Stats - Represents diff statistics for a file
- Repo
- Represents a Git repository with high-level operation methods
- Repo
Commit - Represents a commit in the Git repository
- Repo
Tags - Represents a tag in the Git repository
- System
EnvProvider - System environment provider using
std::env::var
Enums§
- GitFile
Status - Represents the status of a file in Git
- Repo
Error - Errors that can occur when working with Git repositories
Traits§
- EnvProvider
- Trait for accessing environment variables
Type Aliases§
- Result
- Result type alias for git operations.