git-spawn
An async Rust wrapper around the git CLI. Each git subcommand is a
builder-style struct; .execute().await spawns git as a subprocess and
returns typed output.
use ;
async
Install
[]
= "0.1"
= { = "1", = ["macros", "rt-multi-thread"] }
MSRV: 1.85 (Rust 2024 edition).
Capabilities
- Porcelain: init, clone, add, commit, status, log, diff, show, branch, checkout, switch, merge, rebase, pull, push, fetch, remote, tag, stash, reset, restore, rm, mv
- Plumbing: rev-parse, ls-files, ls-tree, cat-file, hash-object, update-ref, for-each-ref
- Advanced: worktree, submodule, bisect, cherry-pick, grep, config, reflog
- Typed parsers (behind the
parsefeature, on by default) forstatus --porcelain=v1 -z,logwith a fixed token format, anddiff --name-status -z - Higher-level workflow helpers (behind the
workflowfeature, off by default) —repo.info(),repo.branches(),repo.tags(),repo.history(), andrepo.workflow()for one-call repo state, typed branch / tag / commit listings, and common compositions likefeature_branch,commit_all,sync,squash_merge - Escape hatches on every command (
.arg,.args,.flag,.option) so flags the typed API hasn't surfaced are still reachable
Choosing a git library for Rust
This crate is one of three realistic options. Pick based on what you're building, not which is "best."
| Project | What it is | You need git installed |
Async-native | Honors local git config, hooks, credential helpers |
|---|---|---|---|---|
git-spawn |
Async subprocess wrapper around the git CLI |
yes | yes | yes |
git2 |
Rust bindings to libgit2 (C) | no | no | partial |
gix |
Pure-Rust (gitoxide) | no | some | partial |
When to reach for git-spawn
- You're automating workflows a human would script in bash: commits, pushes, rebases, cherry-picks, worktree setup, release tagging.
- You want behavior to match exactly what the user's
gitdoes on the host, including~/.gitconfig,core.*settings, pre-commit hooks, SSH/HTTPS credential helpers, andsafe.directory. - You're already in a
tokioprogram and want to run several git operations concurrently (fetching multiple remotes, building repos in parallel). - You need a feature that libgit2 / gix haven't implemented yet. Any
gitflag works via the escape hatches.
Trade-offs:
- A
gitbinary must be onPATHat runtime. - Each operation has process-spawn overhead (low hundreds of microseconds to a few milliseconds). Fine for workflow automation; not fine for tight loops over thousands of objects.
- Output parsing is on you (or the
parsefeature).
When to reach for git2
- You need in-process object database access (walking trees, reading blobs, creating commits) without spawning a subprocess per call.
- You're building a tool that should work without requiring users to have
gitinstalled (GUIs, IDE plugins, CI containers). - You're comfortable with a C dependency:
git2links libgit2, which means a C compiler / CMake (or the vendored build) at build time. - Your program is sync or you're OK running libgit2 calls on a blocking thread pool.
Trade-offs:
- No first-class async. You'll use
spawn_blockingif you're in tokio. - libgit2 doesn't invoke the user's
githooks or credential helpers by default; you implement credential callbacks yourself. - Some newer git features lag behind the CLI (partial clone variants, SHA-256, sparse checkout modes).
When to reach for gix
- You want a pure-Rust stack: no C toolchain, deterministic builds, easy cross-compilation, no libgit2 CVEs to track.
- You need high-throughput object access or want to build sophisticated
tooling on top of git's data model.
gixis split into many focused crates so you can take only what you need. - You're willing to accept a still-evolving API in some areas. The read paths are solid; some write/network paths are newer.
Trade-offs:
- Like
git2, doesn't rungithooks or credential helpers for you. - Not every git feature is implemented yet; check the gitoxide project board for status of what you need.
Quick decision guide
- "I'm calling
git push/git rebase/git cloneon behalf of a user." ->git-spawn. - "I'm walking commit history to generate a report, or reading blobs, and
I can't require
gitto be installed." ->git2if you need maturity and don't mind C;gixif you want pure Rust. - "I'm building a merge engine, a git server, or a CAS-backed fetcher."
->
gix. - "I want to let a user pick a commit and cherry-pick it onto another
branch, respecting their hooks."
->
git-spawn.
Usage
Repository handle
use ;
async
Repository is cheap and cloneable; the accessor methods (.add(),
.commit(), .log(), ...) return commands pre-scoped to the repo's
working directory.
Typed parsers
use ;
use StatusFormat;
use ;
async
The parse feature (on by default) also provides parse_log (paired with
LOG_FORMAT) and parse_diff_name_status. Enable the serde feature to get
Serialize / Deserialize on the parsed types.
Workflow helpers (opt-in)
Enable the workflow feature for one-call repo state, typed listings, and
common compositions:
[]
= { = "0.1", = ["workflow"] }
use Repository;
async
See the module docs for info, branches, tags, history, and workflow
for the full surface.
Escape hatches
Every command supports .arg, .args, .flag, and .option for flags that
don't yet have a typed builder method:
use ;
async
Timeouts, env, working dir
use Duration;
use ;
async
Feature flags
| Flag | Default | Purpose |
|---|---|---|
parse |
on | Typed parsers for status / log / diff output |
serde |
off | Serialize / Deserialize on parsed types |
workflow |
off | Higher-level helpers: info, branches, tags, history, workflow compositions (implies parse) |
Contributing
PRs welcome. Please run before submitting:
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.