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)

  • GitApi — the object-safe trait every operation lives on. Depend on &dyn GitApi (or generically on impl GitApi) so a test can swap the real client for a double. Methods take the working directory as the first argument and return typed results (StatusEntry, Branch, Commit, FileDiff, BlameLine, …) or a structured Error.
  • Git — the real client. Git::new uses the job-backed runner; Git::with_runner injects a fake one for tests. It is generic over the ProcessRunner seam, defaulting to the production runner. with_credentials attaches a CredentialProvider to authenticate HTTPS remote ops (fetch/push/clone/ls-remote) with a token kept out of argv — opt-in, off by default (ambient helpers / SSH agent).
  • GitAt — a cwd-bound view (Git::at) whose methods drop the leading dir, so git.at(dir).status() reads as git.status(dir) — handy when one client drives one checkout.
  • Builder specs for the multi-option commands — CommitPaths, MergeCommit / MergeNoCommit, GitPush, CloneSpec, WorktreeAdd, AnnotatedTag, MergeCheck — each #[non_exhaustive], built with a constructor + chained setters, named after the flags they emit.
  • conflict — a typed conflict-marker model: parse marker soup into structured regions, re-render byte-exact, and resolve to a chosen side.
  • Git::hardened — a profile for untrusted repositories (hooks off, GIT_* scrubbed, system config skipped); see the guide::security guide.

§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};
let dir = Path::new(".");
git.fetch(dir).await?;
git.commit_paths(dir, CommitPaths::new(["src/a.rs"], "wip")).await?;
git.push(dir, GitPush::branch("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.

§Safety

Every caller value placed in a bare positional argv slot is refused before spawning if it is empty or starts with - (git would parse it as a flag); flag-value slots (-b <name>) are consumed verbatim and don’t need it. For eager validation at an input boundary, the RefName / RevSpec newtypes validate up front. Paths always go through -- / pathspec.

§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 — chiefly a Drop guard. They shell out through std::process directly (no async, no job-containment), so reserve them for short-lived cleanup.
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.
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.
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”.
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.
ProcessResult
The captured result of running a process to completion.
RefName
A pre-validated git reference name (branch/tag/remote), for callers that accept names from untrusted input (UIs, bots, agents) and want to fail early with a clear error. The dir-taking methods stay &str — they apply the same flag-injection guard internally — so this type is optional up-front validation, not a required wrapper.
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 pre-validated revision/range expression (HEAD~2, main..feature). 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 -), matching the internal guard the dir-taking methods apply anyway. Optional up-front validation for untrusted input.
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.
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).
Worktree
A worktree from git worktree list --porcelain.
WorktreeAdd
Options for GitApi::worktree_add (git worktree add).

Enums§

ChangeKind
How a file changed in a unified diff.
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).
Error
Errors produced when launching or running a child process.

Constants§

BINARY
Name of the underlying CLI binary this crate drives.
EMPTY_TREE
Git’s well-known empty-tree object id — a stable stand-in for HEAD when diffing the working tree of an unborn (no-commits-yet) repository. Public so a caller can diff/stat a pre-first-commit working tree against it directly.

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§

Result
Crate result alias.