Skip to main content

Crate vcs_git

Crate vcs_git 

Source
Expand description

vcs-git — automate Git from Rust by driving the git CLI.

You call typed async methods; vcs-git runs the real git, parses its output, and hands you structured values — so you get git’s own behaviour, config, and credentials, not a reimplementation of the object format. Async, structured errors, mockable. Every command runs inside an OS job (an OS-level container that kills the whole process tree if your program exits, via processkit) so a git subprocess is never orphaned, with an optional per-client timeout.

§What you can do

Status & branches · stage, commit, checkout · diff & log · merge / rebase / reset · worktrees · tags · blame · clone · config · cherry-pick / revert · parse & resolve conflict markers · a hardened (hooks-off) profile for untrusted repos. One tiny call to start:

use std::path::Path;
use vcs_git::{Git, GitApi};
let git = Git::new();
// `current_branch` is `Option` — `None` on a detached HEAD.
println!("{:?}", git.current_branch(Path::new(".")).await?); // e.g. Some("main")

§The surface (engineering reference)

§Recipes

Read state — depend on the trait so the same code takes a real client or a mock:

use std::path::Path;
use vcs_git::{Git, GitApi};
let git = Git::new();
let dir = Path::new(".");
let branch = git.current_branch(dir).await?;        // the checked-out branch
let dirty = !git.status(dir).await?.is_empty();     // any uncommitted change?

Mutate through the builder specs — fetch retries transient network failures:

use std::path::Path;
use vcs_git::{CommitPaths, Git, GitApi, GitPush, RefName};
let dir = Path::new(".");
git.fetch(dir).await?;
git.commit_paths(dir, CommitPaths::new(["src/a.rs"], "wip")).await?;
// Ref/revision inputs are validated newtypes — build them at the boundary.
git.push(dir, GitPush::branch(RefName::new("feature")?).set_upstream()).await?;

§Testing

Two seams: enable the mock feature for a mockall-generated MockGitApi (stub whole methods), or inject a ScriptedRunner with Git::with_runner to exercise the real argv-building and parsing against canned output. The cross-cutting testing patterns live in vcs-testkit’s guide.

§Features

  • mock — the mockall-generated MockGitApi (see Testing above).
  • tracing — a tracing event per command run.
  • serde — derives serde::Serialize on the public conflict model (ConflictSegment, ConflictRegion, ResolutionSide) so a caller can emit a parsed conflict as JSON. Serialize only — these types are a parser’s output, never a wire input.

§Safety

Every operation that takes a caller-supplied reference name or revision expression now does so through a validated newtype — RefName for branch/tag/ref names, RevSpec for revisions/ranges — so a flag-like or malformed value is rejected at construction, before it can reach an argv slot (a classifiable vcs_cli_support::is_invalid_input failure). The one context-dependent special value, git’s - “previous branch”, is modelled explicitly as CheckoutTarget::Previous rather than smuggled through a newtype. Remaining bare-positional inputs that are not refs/revisions (remote names, URLs, config keys) keep an internal reject_flag_like guard — refused before spawning if empty or starting with -. Flag-value slots (-b <name>) are consumed verbatim; paths — and a config value, which may legitimately begin with - (e.g. -1) and so can’t be flag-rejected — go through a -- option terminator instead (see config_set).

One named exception to the “revisions go through RevSpec” rule: DiffSpec::Rev — the diff target on GitApi::diff_text/GitApi::diff and Git::diff_text_within/ Git::diff_within — is a bare String from the shared, backend-agnostic vcs-diff crate, not a RevSpec. It is still guarded, just per-call rather than by the type: diff_text_budgeted runs the same reject_flag_like check inline before using it, and a trailing -- pins it as a revision rather than a pathspec. Behaviourally equivalent to RevSpec::new’s guarantee, just enforced at the call site because vcs-diff is intentionally a plain-data, dependency-free crate with no newtype of its own to construct through.

§In-depth guide

Beyond this page, this crate ships a full how-to guide — rendered on docs.rs from docs/. See the guide module (and its security / conflicts sub-guides).

Modules§

__mock_MockGitApi
__mock_MockGitApi_GitApi
blocking
Synchronous, best-effort helpers for contexts that cannot .await. Synchronous, best-effort helpers for Drop and other non-async contexts.
conflict
Typed model of git conflict markers — parse a conflicted file’s content into structured regions and write a chosen resolution back. Pure functions (no subprocess), so everything here is hermetic.
guide
vcs-git — Git CLI guide

Structs§

AnnotatedTag
Options for GitApi::tag_create_annotated (git tag -a).
BlameLine
One line of git blame --line-porcelain output: who last touched the line and where it came from.
Branch
A local branch from git branch.
BranchDelete
Options for GitApi::delete_branch (git branch -d/-D).
BranchStatus
A combined branch + working-tree snapshot from git status --porcelain=v2 --branch -z: HEAD, branch, upstream tracking, ahead/behind, and change counts — everything a prompt/status-bar needs, in one process spawn.
CancellationToken
A token which can be used to signal a cancellation request to one or more tasks.
Clean
Options for GitApi::clean (git clean) — deletes untracked files from the working tree.
CleanEntry
One path git clean would remove (-n, dry run) or removed (-f, forced), from a Would remove <path> / Removing <path> output line.
CloneSpec
Options for GitApi::clone_repo (git clone).
Commit
A commit, parsed from a \x1f-delimited git log line.
CommitPaths
Options for GitApi::commit_paths (git commit --only).
Credential
A resolved credential: a Secret plus an optional username. For a forge token only the secret is used; for git HTTPS the username pairs with the secret as the password (a personal-access token).
CredentialRequest
The context of a credential request: which service, and the remote host if the backend knows it (forge calls often defer host resolution to the CLI, so host is frequently None). #[non_exhaustive]: more context may be added.
DiffStat
Aggregate line/file counts from a diff stat (git diff --shortstat, jj diff --stat).
EnvToken
A provider that reads a bare token from a named environment variable, at request time. If the variable is unset/empty it yields None (fall back to ambient auth) rather than erroring — handy for “use $MY_TOKEN if present”.
Error
The crate’s error type: a pointer-sized handle to a structured ErrorReason.
FileDiff
One file’s entry in a parsed git-format unified diff (git diff or jj diff --git).
Git
The real Git client. Generic over the ProcessRunner so tests can inject a fake process executor; Git::new uses the real job-backed runner.
GitAt
A Git client with a working directory bound, so calls drop the leading dir argument — git.at(dir).status() is git.status(dir). Construct one with Git::at (or, through the facade, vcs_core::Repo::git_at). Cheap to copy: it only borrows the client and the path.
GitCapabilities
What the installed git binary supports, probed via GitApi::capabilities. A value type — the client holds no state, so probe once and keep the result (callers cache it).
GitPush
Options for GitApi::push (git push).
GitVersion
A parsed CLI version (major.minor.patch). Ord compares numerically, so a caller can gate a feature on a minimum version; Hash lets it key a map (e.g. a per-version capability cache).
Hunk
A single @@ … @@ hunk within a FileDiff.
JobRunner
The default runner: every run gets a fresh, private ProcessGroup owned by the run, so its tree is torn down when the run finishes (or its handle drops).
MergeCheck
A “is branch fully merged into base?” check for GitApi::is_merged.
MergeCheckPartial
Partial MergeCheck — names the branch being tested; chain into_base to name the base it must be merged into.
MergeCommit
Options for GitApi::merge_commit (git merge that commits the result).
MergeNoCommit
Options for GitApi::merge_no_commit (git merge --no-commit).
MockGitApi
The Git operations this crate exposes — the interface consumers code against and mock in tests.
OutputBudget
A configurable ceiling on how much output a potentially large content operation may buffer before it is refused — a diff (diff_text/diff), a file’s bytes at a revision (show_file/file_show), a forge PR/MR diff (pr_diff), and the diagnostic (error/progress) output of clone/fetch.
ProcessResult
The captured result of running a process to completion.
RefName
A validated git reference name (branch/tag/remote-tracking ref). Every GitApi operation that names a branch, tag, or ref to create, delete, rename, or look up by exact name takes a RefName (directly or inside its options struct), so a name from untrusted input (UIs, bots, agents) is validated once, at construction, and the type — not an internal guard — is the argv-injection barrier from then on. For a general commit-ish or range (checkout, reset_hard, log, diff ranges, …) use the more permissive RevSpec instead.
Remote
One configured Git remote, as listed by git remote -v.
RetryPolicy
A bounded retry strategy: how many attempts, the (exponential) backoff between them, and whether to add full jitter. Used by ManagedClient to retry is_lock_contention failures. The Default is none (no retry) — retry is opt-in.
RevSpec
A validated revision/range expression (HEAD~2, main..feature). Every GitApi operation that resolves a general commit-ish or range takes a RevSpec, so an untrusted revision is validated once, at construction. Deliberately minimal — git’s revision grammar is too rich to validate here — it only guarantees the expression is non-empty and cannot be parsed as a flag (no leading -). For a value that must be a genuine ref name (to create/delete/rename a branch or tag) use the stricter RefName. A rejected expression is an vcs_cli_support::is_invalid_input failure.
Secret
A secret value — an API token, a password — that redacts itself whenever it is formatted, so it can’t leak into a log line or an error message. Read the underlying value only at the point of use, via expose.
SparseCheckoutSet
Options for GitApi::sparse_checkout_set (git sparse-checkout set).
StashEntry
One entry from git stash list, parsed via --format=%gd%x1f%H%x1f%gs -z: the stash’s position, the stashed commit’s hash, and its label split into an optional branch and the rest of the message.
StashPush
Options for GitApi::stash_push (git stash push).
StaticCredential
A provider that always yields the same Credential for every request — the common “use this one token” case.
StatusEntry
One entry from git status --porcelain=v1 -z (XY <path>, NUL-delimited).
Submodule
One submodule declared in the superproject’s .gitmodules, parsed from the machine-unambiguous git config --file .gitmodules --list -z source rather than a hand-rolled text scan of the ini-style file.
SubmoduleStatus
One entry from git submodule status: the checked-out commit, the mount path, and the sync state derived from the line’s leading prefix character.
SubmoduleUpdate
A git submodule update specification — checks out the submodules recorded in the superproject’s index to the commits it pins, optionally initializing (--init) and recursing (--recursive) first, and optionally scoped to specific paths. Built fluently; see GitApi::submodule_update.
Worktree
A worktree from git worktree list --porcelain.
WorktreeAdd
Options for GitApi::worktree_add (git worktree add).
WorktreeRemove
Options for GitApi::worktree_remove (git worktree remove).

Enums§

BisectStep
The typed result of one git bisect classification step.
ChangeKind
How a file changed in a unified diff.
CheckoutTarget
What GitApi::checkout switches to: a validated ref/revision, or git’s - “previous branch” shortcut.
CleanIgnored
How Clean treats ignored files/directories — the -x/-X axis of git clean, orthogonal to directories.
CloneFilter
The partial-clone object filter for GitApi::clone_repo (git clone --filter=<value>).
CredentialService
Which backend/tool is asking for a credential — lets a provider return different secrets per service. #[non_exhaustive]: new backends may be added.
DiffLine
One line inside a Hunk, tagged by its role. The stored text excludes the leading /+/- marker and the line terminator — a CRLF-origin diff’s trailing \r is stripped along with the \n, so reconstruct exact bytes from FileDiff::raw, not from these lines.
DiffSpec
What a diff call compares — the working tree/copy, or a specific revision/revset (or range).
ErrorKind
The kind of an Error — a total, compact classification of the failure into one bucket per operational disposition, reached through Error::kind / ErrorReason::kind.
ErrorReason
The structured failure mode behind an Error — the enum you reach through Error::reason.
ProcessEvent
A lifecycle event produced by a running child process, yielded by RunningProcess::events, which merges the process’s lifecycle transitions and its two output streams into a single ordered sequence:
SubmoduleState
The sync state of a submodule, from the one-character prefix in the git submodule status output.

Constants§

BINARY
Name of the underlying CLI binary this crate drives.
EMPTY_TREE_SHA1
Git’s well-known SHA-1 empty-tree object id. This value exists only in a SHA-1 repository: a repo created with extensions.objectFormat=sha256 has a different empty-tree id (and this SHA-1 one resolves to no object there), so this constant is not a universal stand-in for HEAD when diffing an unborn working tree. For the id that matches a repository’s active object format, use Git::empty_tree_oid, which asks git for it — that is what diff_text(DiffSpec::WorkingTree) uses on an unborn repo.

Traits§

CredentialProvider
Supplies a Credential for a CredentialRequest, just-in-time. Returning Ok(None) means “I have nothing for this request” — the backend then falls back to its ambient CLI auth, exactly as if no provider were configured.
GitApi
The Git operations this crate exposes — the interface consumers code against and mock in tests.
ProcessRunner
Runs a Command — to a captured result (output_string / output_bytes) or a live handle (start).

Functions§

is_lock_contention
Whether err is a whole-repository lock-contention failure — another process held git’s index.lock or jj’s working-copy / op-heads lock, so the command couldn’t even start. Such a failure is pre-execution and therefore safe to retry even on a mutating operation (the repo was never modified). Per-ref lock failures (cannot lock ref, <ref>.lock) are deliberately not classified here — they can occur mid-way through a multi-ref push/fetch, where a retry would not be idempotent. Conflict, “nothing to commit”, a real non-zero exit, a timeout, a signal, or a missing binary are also not lock contention and must not be retried this way.
is_merge_conflict
Whether a failed merge/merge_commit stopped on a merge conflict. (jj surfaces conflicts as state rather than as errors, so this only fires on git output — see vcs_core::Error::is_merge_conflict.)
is_nothing_to_commit
Whether a failed commit/commit_paths reported nothing to commit (a clean tree), as opposed to a real error.
is_transient_fetch_error
Whether a failed fetch/fetch_branch/remote_branch_exists looks transient (DNS, a dropped connection, a fast network blip) and is worth retrying.
parse_diff
Parse a git-format unified diff into one FileDiff per file. Works on git diff and jj diff --git output alike. Public so a consumer can parse diff text it obtained by other means.
provider_fn
Adapt a synchronous closure into a CredentialProvider. The closure runs at request time and returns the credential (or None to defer to ambient auth). For async sources (a network vault), implement CredentialProvider directly.

Type Aliases§

BisectResult
Backward-readable alias for BisectStep. Prefer BisectStep in new code.
ProgressCallback
Object-safe callback accepted by the typed streaming operations.
Result
Crate result alias.