pub struct GitCommand { /* private fields */ }Expand description
Type-safe builder for constructing and executing Git commands with consistent error handling.
This builder provides a fluent API for Git command construction that ensures consistent behavior across AGPM’s Git operations. It handles platform-specific differences, timeout management, error context, and output capture in a unified way.
§Features
- Fluent API: Chainable methods for building commands
- Error Context: Automatic error message enhancement with context
- Timeout Management: Configurable timeouts with sensible defaults
- Platform Independence: Works across Windows, macOS, and Linux
- Output Capture: Flexible handling of command output and errors
- Environment Variables: Support for Git-specific environment settings
§Examples
use agpm_cli::git::command_builder::GitCommand;
use std::path::Path;
// Simple command with output capture
let output = GitCommand::new()
.args(["status", "--porcelain"])
.current_dir(Path::new("/path/to/repo"))
.execute()
.await?;
// Command with timeout and error context
let result = GitCommand::new()
.args(["clone", "https://github.com/example/repo.git"])
.timeout(std::time::Duration::from_secs(60))
.with_context("Cloning community repository")
.execute_success()
.await?;
// Interactive command (no output capture)
GitCommand::new()
.args(["merge", "--interactive"])
.inherit_stdio()
.execute_success()
.await?;§Default Configuration
New commands are created with sensible defaults:
- Timeout: 5 minutes (300 seconds)
- Output capture: Enabled
- Working directory: Current process directory
- Environment: Inherits from parent process
§Platform Compatibility
The builder automatically handles platform-specific Git command location:
- Windows: Uses
git.exefrom PATH or common installation directories - Unix-like: Uses
gitfrom PATH - Error handling: Provides clear messages if Git is not installed
Implementations§
Source§impl GitCommand
impl GitCommand
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new Git command builder with default settings.
The new builder is initialized with:
- Empty argument list
- Current process directory as working directory
- Output capture enabled
- 5-minute timeout
- No additional environment variables
§Examples
use agpm_cli::git::command_builder::GitCommand;
let cmd = GitCommand::new()
.args(["status", "--short"])
.current_dir("/path/to/repo");Sourcepub fn current_dir(self, dir: impl AsRef<Path>) -> Self
pub fn current_dir(self, dir: impl AsRef<Path>) -> Self
Sets the working directory for Git command execution.
The command will be executed in the specified directory, which should typically be a Git repository root or subdirectory. If not set, the command executes in the current process working directory.
§Arguments
dir- Path to the directory where the Git command should run
§Examples
use agpm_cli::git::command_builder::GitCommand;
use std::path::Path;
let cmd = GitCommand::new()
.current_dir("/path/to/repository")
.args(["log", "--oneline"]);
// Can also use Path references
let repo_path = Path::new("/path/to/repo");
let cmd2 = GitCommand::new()
.current_dir(repo_path)
.args(["status"]);Sourcepub fn arg(self, arg: impl Into<String>) -> Self
pub fn arg(self, arg: impl Into<String>) -> Self
Adds a single argument to the Git command.
Arguments are passed to Git in the order they are added. This method is useful when building commands dynamically or when you need to add arguments conditionally.
§Arguments
arg- The argument to add (will be converted to String)
§Examples
use agpm_cli::git::command_builder::GitCommand;
let cmd = GitCommand::new()
.arg("clone")
.arg("--depth")
.arg("1")
.arg("https://github.com/example/repo.git");§Note
For adding multiple arguments at once, consider using args
which is more efficient for static argument lists.
Sourcepub fn args<I, S>(self, args: I) -> Self
pub fn args<I, S>(self, args: I) -> Self
Adds multiple arguments to the Git command.
This is the preferred method for adding multiple arguments at once,
as it’s more efficient than multiple calls to arg.
Arguments can be provided as any iterable of string-like types.
§Arguments
args- An iterable of arguments to add to the command
§Examples
use agpm_cli::git::command_builder::GitCommand;
// Using array literals
let cmd = GitCommand::new()
.args(["clone", "--recursive", "https://github.com/example/repo.git"]);
// Using vectors
let clone_args = vec!["clone", "--depth", "1"];
let cmd2 = GitCommand::new().args(clone_args);
// Mixing with other methods
let cmd3 = GitCommand::new()
.args(["fetch", "origin"])
.arg("--prune");Sourcepub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
pub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
Adds an environment variable for the Git command execution.
Environment variables are useful for configuring Git behavior without modifying global Git configuration. Common use cases include setting credentials, configuration overrides, and locale settings.
§Arguments
key- Environment variable namevalue- Environment variable value
§Examples
use agpm_cli::git::command_builder::GitCommand;
// Set Git configuration for this command only
let cmd = GitCommand::new()
.args(["clone", "https://github.com/example/repo.git"])
.env("GIT_CONFIG_GLOBAL", "/dev/null") // Ignore global config
.env("GIT_CONFIG_SYSTEM", "/dev/null"); // Ignore system config
// Set locale for consistent output parsing
let cmd2 = GitCommand::new()
.args(["log", "--oneline"])
.env("LC_ALL", "C");§Common Environment Variables
GIT_DIR: Override Git directory locationGIT_WORK_TREE: Override working tree locationGIT_CONFIG_*: Override configuration file locationsLC_ALL: Set locale for consistent output parsing
Sourcepub const fn inherit_stdio(self) -> Self
pub const fn inherit_stdio(self) -> Self
Disables output capture, allowing the command to inherit parent stdio.
By default, Git commands have their output captured for processing. This method disables capture, allowing the command to write directly to the terminal. This is useful for interactive commands or when you want to show Git output directly to the user.
§Use Cases
- Interactive Git commands (merge conflicts, rebase, etc.)
- Commands where you want real-time output streaming
- Debugging Git operations by seeing output immediately
§Examples
use agpm_cli::git::command_builder::GitCommand;
// Interactive merge that may require user input
GitCommand::new()
.args(["merge", "feature-branch"])
.inherit_stdio() // Allow user interaction
.execute_success()
.await?;
// Show clone progress to user
GitCommand::new()
.args(["clone", "--progress", "https://github.com/large/repo.git"])
.inherit_stdio() // Show progress bars
.execute_success()
.await?;§Note
When stdio is inherited, you cannot use execute to
capture output. Use execute_success instead.
Sourcepub const fn with_timeout(self, duration: Option<Duration>) -> Self
pub const fn with_timeout(self, duration: Option<Duration>) -> Self
Set a custom timeout for the command (None for no timeout)
Sourcepub fn with_context(self, context: impl Into<String>) -> Self
pub fn with_context(self, context: impl Into<String>) -> Self
Set a context for logging (e.g., dependency name)
The context is included in debug log messages to help distinguish between concurrent operations when processing multiple dependencies in parallel. This is especially useful when using worktrees for parallel Git operations.
§Arguments
context- A string identifier for this operation (typically dependency name)
§Examples
use agpm_cli::git::command_builder::GitCommand;
let output = GitCommand::fetch()
.with_context("my-dependency")
.current_dir("/path/to/repo")
.execute()
.await?;§Logging Output
With context, log messages will include the context identifier:
(my-dependency) Executing command: git -C /path/to/repo fetch --all --tags
(my-dependency) Command completed successfullySourcepub async fn execute(self) -> Result<GitCommandOutput>
pub async fn execute(self) -> Result<GitCommandOutput>
Execute the command and return the output
Sourcepub async fn execute_stdout(self) -> Result<String>
pub async fn execute_stdout(self) -> Result<String>
Execute the command and return only stdout as a trimmed string
Sourcepub async fn execute_success(self) -> Result<()>
pub async fn execute_success(self) -> Result<()>
Execute the command and check for success without capturing output
Source§impl GitCommand
impl GitCommand
Sourcepub fn checkout_branch(branch_name: &str, remote_ref: &str) -> Self
pub fn checkout_branch(branch_name: &str, remote_ref: &str) -> Self
Create a checkout command that forces branch creation/update
Sourcepub fn reset_hard() -> Self
pub fn reset_hard() -> Self
Create a reset command
Create a tag list command
Sourcepub fn list_branches() -> Self
pub fn list_branches() -> Self
Create a branch list command
Sourcepub fn current_commit() -> Self
pub fn current_commit() -> Self
Create a command to get the current commit hash
Sourcepub fn remote_url() -> Self
pub fn remote_url() -> Self
Create a command to get the remote URL
Sourcepub fn set_remote_url(url: &str) -> Self
pub fn set_remote_url(url: &str) -> Self
Create a command to set the remote URL
Sourcepub fn verify_ref(ref_name: &str) -> Self
pub fn verify_ref(ref_name: &str) -> Self
Create a command to verify a reference exists
Sourcepub fn current_branch() -> Self
pub fn current_branch() -> Self
Create a command to get the current branch
Sourcepub fn clone_bare(url: &str, target: impl AsRef<Path>) -> Self
pub fn clone_bare(url: &str, target: impl AsRef<Path>) -> Self
Create a git clone –bare command for bare repository.
Bare repositories are optimized for use as a source for worktrees, allowing multiple concurrent checkouts without conflicts. This is the preferred method for parallel operations in AGPM.
§Arguments
url- The remote repository URL to clonetarget- The local directory where the bare repository will be stored
§Returns
A configured GitCommand ready for execution.
§Examples
use agpm_cli::git::command_builder::GitCommand;
GitCommand::clone_bare(
"https://github.com/example/repo.git",
"/tmp/repo.git"
)
.execute_success()
.await?;§Worktree Usage
Bare repositories created with this command are designed to be used
with worktree_add for parallel operations:
use agpm_cli::git::command_builder::GitCommand;
// Clone bare repository
GitCommand::clone_bare("https://github.com/example/repo.git", "/tmp/repo.git")
.execute_success()
.await?;
// Create working directory from bare repo
GitCommand::worktree_add("/tmp/work1", Some("v1.0.0"))
.current_dir("/tmp/repo.git")
.execute_success()
.await?;Sourcepub fn worktree_add(
worktree_path: impl AsRef<Path>,
reference: Option<&str>,
) -> Self
pub fn worktree_add( worktree_path: impl AsRef<Path>, reference: Option<&str>, ) -> Self
Create a worktree add command for parallel-safe Git operations.
Worktrees allow multiple working directories to be checked out from a single bare repository, enabling safe parallel operations on different versions of the same repository without conflicts.
§Arguments
worktree_path- The path where the new worktree will be createdreference- Optional Git reference (branch, tag, or commit) to checkout
§Returns
A configured GitCommand that must be executed from a bare repository directory.
§Parallel Safety
Each worktree is independent and can be safely accessed concurrently:
- Different dependencies can use different worktrees simultaneously
- No conflicts between parallel checkout operations
- Each worktree maintains its own working directory state
§Examples
use agpm_cli::git::command_builder::GitCommand;
// Create worktree with specific version
GitCommand::worktree_add("/tmp/work-v1", Some("v1.0.0"))
.current_dir("/tmp/bare-repo.git")
.execute_success()
.await?;
// Create worktree with default branch
GitCommand::worktree_add("/tmp/work-main", None)
.current_dir("/tmp/bare-repo.git")
.execute_success()
.await?;§Concurrency Control
AGPM uses a global semaphore to limit concurrent Git operations and prevent resource exhaustion. This is handled automatically by the cache layer when using worktrees for parallel installations.
§Reference Types
The reference parameter supports various Git reference types:
- Tags:
"v1.0.0"(most common for package versions) - Branches:
"main","develop" - Commits:
"abc123"(specific commit hashes) - None: Uses repository’s default branch
Sourcepub fn worktree_remove(worktree_path: impl AsRef<Path>) -> Self
pub fn worktree_remove(worktree_path: impl AsRef<Path>) -> Self
Remove a worktree and clean up associated files.
This command removes a worktree that was created with worktree_add
and cleans up Git’s internal bookkeeping. The --force flag is used
to ensure removal even if the worktree has local modifications.
§Arguments
worktree_path- The path to the worktree to remove
§Returns
A configured GitCommand that must be executed from the bare repository.
§Examples
use agpm_cli::git::command_builder::GitCommand;
// Remove a worktree
GitCommand::worktree_remove("/tmp/work-v1")
.current_dir("/tmp/bare-repo.git")
.execute_success()
.await?;§Force Removal
The command uses --force to ensure removal succeeds even when:
- The worktree has uncommitted changes
- Files are locked or in use
- The worktree directory structure has been modified
This is appropriate for AGPM’s use case where worktrees are temporary and any local changes should be discarded.
Sourcepub fn worktree_list() -> Self
pub fn worktree_list() -> Self
List all worktrees associated with a repository.
This command returns information about all worktrees linked to the
current bare repository. The --porcelain flag provides machine-readable
output that’s easier to parse programmatically.
§Returns
A configured GitCommand that must be executed from a bare repository.
§Output Format
The porcelain output format provides structured information:
worktree /path/to/worktree1
HEAD abc123def456...
branch refs/heads/main
worktree /path/to/worktree2
HEAD def456abc123...
detached§Examples
use agpm_cli::git::command_builder::GitCommand;
let output = GitCommand::worktree_list()
.current_dir("/tmp/bare-repo.git")
.execute_stdout()
.await?;
// Parse output to find worktree paths
for line in output.lines() {
if line.starts_with("worktree ") {
let path = line.strip_prefix("worktree ").unwrap();
println!("Found worktree: {}", path);
}
}Sourcepub fn worktree_prune() -> Self
pub fn worktree_prune() -> Self
Prune stale worktree administrative files.
This command cleans up worktree entries that no longer have corresponding directories on disk. It’s useful for maintenance after worktrees have been manually deleted or when cleaning up after failed operations.
§Returns
A configured GitCommand that must be executed from a bare repository.
§When to Use
Prune worktrees when:
- Worktree directories have been manually deleted
- After bulk cleanup operations
- During cache maintenance
- When Git reports stale worktree references
§Examples
use agpm_cli::git::command_builder::GitCommand;
// Clean up stale worktree references
GitCommand::worktree_prune()
.current_dir("/tmp/bare-repo.git")
.execute_success()
.await?;§Performance
This is a lightweight operation that only updates Git’s internal bookkeeping. It doesn’t remove actual worktree directories.