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 onimpl 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 structuredError.Git— the real client.Git::newuses the job-backed runner;Git::with_runnerinjects a fake one for tests. It is generic over theProcessRunnerseam, defaulting to the production runner.with_credentialsattaches aCredentialProviderto authenticate HTTPS remote ops (fetch/push/clone/ls-remote) with a token kept out ofargv— opt-in, off by default (ambient helpers / SSH agent).GitAt— a cwd-bound view (Git::at) whose methods drop the leadingdir, sogit.at(dir).status()reads asgit.status(dir)— handy when one client drives one checkout.- Builder specs for the multi-option commands —
CommitPaths,MergeCommit/MergeNoCommit,GitPush,CloneSpec,WorktreeAdd,AnnotatedTag— 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 theguide::securityguide.
§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_ Mock GitApi - __
mock_ Mock GitApi_ GitApi - blocking
- Synchronous, best-effort helpers for contexts that cannot
.await— chiefly aDropguard. They shell out throughstd::processdirectly (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§
- Annotated
Tag - Options for
GitApi::tag_create_annotated(git tag -a). - Blame
Line - One line of
git blame --line-porcelainoutput: who last touched the line and where it came from. - Branch
- A local branch from
git branch. - Branch
Status - 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. - Cancellation
Token - A token which can be used to signal a cancellation request to one or more tasks.
- Clone
Spec - Options for
GitApi::clone_repo(git clone). - Commit
- A commit, parsed from a
\x1f-delimitedgit logline. - Commit
Paths - Options for
GitApi::commit_paths(git commit --only). - Credential
- A resolved credential: a
Secretplus 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). - Credential
Request - 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
hostis frequentlyNone).#[non_exhaustive]: more context may be added. - Diff
Stat - 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_TOKENif present”. - File
Diff - One file’s entry in a parsed git-format unified diff (
git difforjj diff --git). - Git
- The real Git client. Generic over the
ProcessRunnerso tests can inject a fake process executor;Git::newuses the real job-backed runner. - GitAt
- A
Gitclient with a working directory bound, so calls drop the leadingdirargument —git.at(dir).status()isgit.status(dir). Construct one withGit::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
gitbinary supports, probed viaGitApi::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).Ordcompares numerically, so a caller can gate a feature on a minimum version;Hashlets it key a map (e.g. a per-version capability cache). - Hunk
- A single
@@ … @@hunk within aFileDiff. - Merge
Commit - Options for
GitApi::merge_commit(git mergethat commits the result). - Merge
NoCommit - Options for
GitApi::merge_no_commit(git merge --no-commit). - Mock
GitApi - The Git operations this crate exposes — the interface consumers code against and mock in tests.
- Process
Result - 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. - Retry
Policy - A bounded retry strategy: how many attempts, the (exponential) backoff between
them, and whether to add full jitter. Used by
ManagedClientto retryis_lock_contentionfailures. TheDefaultisnone(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. - Static
Credential - A provider that always yields the same
Credentialfor every request — the common “use this one token” case. - Status
Entry - One entry from
git status --porcelain=v1 -z(XY <path>, NUL-delimited). - Worktree
- A worktree from
git worktree list --porcelain. - Worktree
Add - Options for
GitApi::worktree_add(git worktree add).
Enums§
- Change
Kind - How a file changed in a unified diff.
- Credential
Service - Which backend/tool is asking for a credential — lets a provider return
different secrets per service.
#[non_exhaustive]: new backends may be added. - Diff
Line - 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\ris stripped along with the\n, so reconstruct exact bytes fromFileDiff::raw, not from these lines. - Diff
Spec - What a
GitApi::diff/GitApi::diff_textcall compares. - 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
HEADwhen 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§
- Credential
Provider - Supplies a
Credentialfor aCredentialRequest, just-in-time. ReturningOk(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.
Functions§
- is_
lock_ contention - Whether
erris a whole-repository lock-contention failure — another process held git’sindex.lockor 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-refpush/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_commitstopped on a merge conflict. (jj surfaces conflicts as state rather than as errors, so this only fires on git output — seevcs_core::Error::is_merge_conflict.) - is_
nothing_ to_ commit - Whether a failed
commit/commit_pathsreported nothing to commit (a clean tree), as opposed to a real error. - is_
transient_ fetch_ error - Whether a failed
fetch/fetch_remote_branch/remote_branch_existslooks transient (DNS, timeout, dropped connection) and is worth retrying. - parse_
diff - Parse a git-format unified diff into one
FileDiffper file. Works ongit diffandjj diff --gitoutput 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 (orNoneto defer to ambient auth). For async sources (a network vault), implementCredentialProviderdirectly.
Type Aliases§
- Result
- Crate result alias.