five git implementations, one repository, the same six operations.
Every runner is held to the same work contract on a full clone of vercel/next.js, so the numbers compare implementations rather than workloads. bun:ffi + libgit2 finishes 4.2× faster than the git CLI, and 356× separates the fastest runner from the slowest.
5 runners · 6 operations · 5 timed samples each · last run
- 01Commit
- 02History
- 03Branch
- 04Merge
Overall ranking
Geometric mean of the six operations against the git CLI, on a logarithmic axis: lower is faster, a bar left of parity beats git, and the whisker spans the runner’s best and worst operation.
Which library should you use?
Recommendations are derived from the published results and update when the benchmark changes.
Best overall
bun:ffi + libgit2Lowest geometric mean across all six operations, normalized against the git CLI.
Best in-process option
bun:ffi + libgit2Avoids subprocess startup when the benchmark work is embedded in your application.
Most reliable
gitoxideFewest parity mismatches and failed operations, with the smallest average sample spread.
Operations
How far apart the implementations land on each operation: the bar is the slowest median divided by the fastest, and the whisker is that ratio at its most and least favourable across the samples.
- Log (100)Walk the 100 most recent commits from HEAD.12,785× ±24%0.26ms
- Read 25 blobsRead 25 file blobs at HEAD by path.7,802× ±9%10ms
- Changed filesName-only diff between HEAD~1 and HEAD.4,226× ±5%2.33ms
- Tracked filesEnumerate every path tracked in the index.210× ±49%1.30ms
- Current branchResolve HEAD to a branch name.149× ±62%0.04ms
- StatusWorktree + index status (porcelain).21× ±5%364ms
Speed against consistency
- Score:
- the geometric mean of the six operations, each as a multiple of the git CLI.
- Spread:
- the worst operation divided by the best. A point far to the right hides at least one operation you would not want in a request path.
Full results
Median time per operation; sort by any runner to find what it handles worst.
| Operation | |||||
|---|---|---|---|---|---|
Current branch | 1.47ms1.0×1.37ms–1.63ms | 0.04ms0.03×0.03ms–0.06ms | 5.90ms4.0×5.73ms–7.11ms | 3.64ms2.5×3.20ms–6.02ms | 1.49ms1.0×1.31ms–2.58ms |
Status | 407ms1.0×405ms–421ms | 687ms1.7×685ms–688ms | 364ms0.89×350ms–377ms | 7.59s19×7.44s–7.67s | 1.85s4.5×1.83s–1.85s |
Log (100) | 5.07ms1.0×4.10ms–5.94ms | 0.26ms0.05×0.22ms–0.35ms | 12ms2.3×12ms–12ms | 3.32s655×3.28s–3.36s | 11ms2.1×10ms–11ms |
Tracked files | 17ms1.0×17ms–19ms | 1.30ms0.08×0.90ms–2.66ms | 273ms16×269ms–274ms | 36ms2.1×34ms–50ms | 16ms0.92×15ms–16ms |
Changed files | 2.33ms1.0×2.24ms–2.41ms | 14ms5.8×13ms–14ms | 30ms13×29ms–37ms | 9.83s4,226×9.69s–9.88s | 3.11s1,338×3.11s–3.12s |
Read 25 blobs | 59ms1.0×57ms–64ms | 10ms0.18×9.56ms–11ms | 155ms2.6×151ms–165ms | 81.45s1,377×79.53s–82.43s | 64ms1.1×64ms–65ms |
Methodology
- Every runner executes in the same Docker image against one clone of vercel/next.js: one warmup iteration is discarded, 5 timed samples follow, and the median is published.
- Runners are held to the same work contract — status collects untracked but not ignored files, blob reads materialize full contents — and any disagreement with the git CLI baseline is logged as a parity mismatch rather than counted as a win.
- The score is the geometric mean of each operation's ratio to the git CLI, so no single slow operation decides the aggregate.
- Numbers come from a GitHub Actions runner, so the comparison between runners is the signal and the milliseconds are context.
- CPU
- AMD EPYC 7763 64-Core Processor
- Runtime
- Bun 1.4.0
- Source
- github-actions
Run against 8057603. Raw results, config schema and the source.




