GitCommand

Struct GitCommand 

Source
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.exe from PATH or common installation directories
  • Unix-like: Uses git from PATH
  • Error handling: Provides clear messages if Git is not installed

Implementations§

Source§

impl GitCommand

Source

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");
Source

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"]);
Source

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.

Source

pub fn args<I, S>(self, args: I) -> Self
where I: IntoIterator<Item = S>, S: Into<String>,

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");
Source

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 name
  • value - 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 location
  • GIT_WORK_TREE: Override working tree location
  • GIT_CONFIG_*: Override configuration file locations
  • LC_ALL: Set locale for consistent output parsing
Source

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.

Source

pub const fn with_timeout(self, duration: Option<Duration>) -> Self

Set a custom timeout for the command (None for no timeout)

Source

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 successfully
Source

pub async fn execute(self) -> Result<GitCommandOutput>

Execute the command and return the output

Source

pub async fn execute_stdout(self) -> Result<String>

Execute the command and return only stdout as a trimmed string

Source

pub async fn execute_success(self) -> Result<()>

Execute the command and check for success without capturing output

Source§

impl GitCommand

Source

pub fn clone(url: &str, target: impl AsRef<Path>) -> Self

Create a clone command

Source

pub fn fetch() -> Self

Create a clone command with specific depth

Create a fetch command

Source

pub fn checkout(ref_name: &str) -> Self

Create a checkout command

Source

pub fn checkout_branch(branch_name: &str, remote_ref: &str) -> Self

Create a checkout command that forces branch creation/update

Source

pub fn reset_hard() -> Self

Create a reset command

Source

pub fn list_tags() -> Self

Create a tag list command

Source

pub fn list_branches() -> Self

Create a branch list command

Source

pub fn rev_parse(ref_name: &str) -> Self

Create a rev-parse command

Source

pub fn current_commit() -> Self

Create a command to get the current commit hash

Source

pub fn remote_url() -> Self

Create a command to get the remote URL

Source

pub fn set_remote_url(url: &str) -> Self

Create a command to set the remote URL

Source

pub fn ls_remote(url: &str) -> Self

Create a ls-remote command for repository verification

Source

pub fn verify_ref(ref_name: &str) -> Self

Create a command to verify a reference exists

Source

pub fn current_branch() -> Self

Create a command to get the current branch

Source

pub fn init() -> Self

Create an init command

Source

pub fn add(pathspec: &str) -> Self

Create an add command

Source

pub fn commit(message: &str) -> Self

Create a commit command

Source

pub fn push() -> Self

Create a push command

Source

pub fn status() -> Self

Create a status command

Source

pub fn diff() -> Self

Create a diff command

Source

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 clone
  • target - 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?;
Source

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 created
  • reference - 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
Source

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.

Source

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);
    }
}
Source

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.

Trait Implementations§

Source§

impl Default for GitCommand

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,