Library / SDK
rust-lang/git2-rs avatar
rust-lang/git2-rs

git2: libgit2 bindings for Rust, and the choices they hide

libgit2 bindings for Rust

2,113 stars451 forksRustApache-2.0

At a glance

What is it?
git2 lets a Rust program read and write git repositories in process, through the libgit2 C library whose source ships inside the sys crate, so no system install is needed. Local work runs with no features enabled; remote work, link mode and object id format are all decisions a project makes by name.
Who is it for?
git2 fits a Rust service that reads or writes repositories inside its own process, and it does not fit a binary that wants remote work without deciding anything first. Before depending on it, confirm the toolchain clears rust-version 1.87 and pick the link mode on purpose.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 18 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 6, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A Rust API over the libgit2 C library, not the git binary

git2 is how a Rust program reads and writes a git repository without launching a git executable. The package manifest states the intent plainly: bindings that let a program both read and write git repositories while staying threadsafe and memory safe. The dependency chain is short enough to read in one screen, and the first link is pinned by path rather than pulled from a registry.

toml
[package]
name = "git2"
version = "0.21.0"

Version 0.21.0, edition 2021 and a rust-version floor of 1.87 place this squarely in current toolchains. Four direct dependencies sit under the package: url 2.5.4 marked optional, bitflags 2.1.0, libc 0.2 and log 0.4.8, alongside the sys crate. Because the C library arrives inside libgit2-sys, a machine with no libgit2 installed can still build the crate, which is the practical difference from driving the git binary through a subprocess and parsing its output.

Local repositories work with no features enabled, remote ones do not

Out of the box the crate covers local repositories and stops there. The feature table ends with an empty `default = []`, and every network path sits behind a feature a project has to name.

bash
cargo add git2

That is the whole installation for local work. Remote work changes the command:

bash
cargo add git2 --features https,ssh

Enabling ssh is not a convenience toggle. It pulls libssh2 and OpenSSL into the dependency graph, and on macOS the README hands you off to the `openssl` crate instructions for making OpenSSL work at all. Two further feature names are visible in the manifest: `unstable`, an empty list reserved for work in progress, and `unstable-sha256`, which switches on the experimental object id format in upstream libgit2. With an empty default, the shape of what your program can do is decided by what you leave out as much as by what you turn on.

Vendored by default, dynamic only when the system copy is close enough

The link mode follows two conditions, and one environment variable can overrule the build flag. Vendoring wins when `LIBGIT2_NO_VENDOR=1` is not set and either the `vendored-libgit2` feature is on or no acceptable libgit2 exists on the system.

toml
libgit2-sys = { path = "libgit2-sys", version = "0.18.7" }

The documentation is explicit that the environment variable overrides the Cargo feature, so an image that exports `LIBGIT2_NO_VENDOR=1` keeps that setting whatever the manifest asks for. What deserves a second read is the word appropriate in the versioning rule. The crate wants libgit2 1.9.6 or a newer patch, and a system carrying an older copy is not an appropriate version, so the vendored static build takes over on an ordinary build. Set `LIBGIT2_NO_VENDOR=1` on that same machine and the fallback disappears: there is no older libgit2 to fall back to either, and the build is left hunting for headers for a version nobody installed. One environment variable is the distance between a self-contained crate and a machine that needs a C toolchain plus the right development package.

The ssh feature reaches OpenSSL through a target-gated dependency

OpenSSL enters through a target rule rather than a feature of its own. The manifest files openssl-sys 0.9.45 and openssl-probe 0.1 under a target dependency scoped to unix targets other than macOS, and marks both optional.

toml
openssl-sys = { version = "0.9.45", optional = true }
openssl-probe = { version = "0.1", optional = true }

macOS is excluded from that list and gets a separate instruction instead: with ssh enabled, the crate depends on libssh2, libssh2 depends on OpenSSL, and macOS users are pointed at the `openssl` crate instructions. So one feature produces two different build stories. On Linux and other unix targets the hunt for a usable OpenSSL is delegated to `openssl-probe` at build time, which means the result depends on what the build machine already carries. Nobody enabling ssh on a CI image should wait for the first missing-header failure to find out which path their runner takes.

Building from a clone leaves the test matrix to CI

A first build from source is three commands.

sh
$ git clone https://github.com/rust-lang/git2-rs
$ cd git2-rs
$ cargo build

The clone brings the repository with the libgit2 source that the sys crate compiles. The build step compiles C before Rust, so it needs a working C compiler even on a machine with no system libgit2. Testing is where this project pushes work outward: the README notes that running tests and covering the edge cases on every commit turns tedious quickly, and points at GitHub's automated CI instead of a local loop. The manifest carries the toolchain for that work, with clap 4.4.13 and the derive feature, time 0.3.45, tempfile 3.1.0 and trybuild 1.0.117 under dev-dependencies, and a Cargo.lock at the top level holding the resolved graph.

unstable-sha256 cannot be switched off once a dependency enables it

The `unstable-sha256` feature is the sharpest edge in the manifest, and the comment block above it is longer than the feature itself. It reflects upstream libgit2's GIT_EXPERIMENTAL_SHA256, the manifest calls it an ABI-breaking change, and it warns that later builds carrying the feature may break without notice. Then comes the instruction aimed at other people's crates: library authors must not enable it by default in their dependencies, because Cargo features are additive and downstream users cannot deactivate one already switched on upstream.

toml
unstable-sha256 = ["libgit2-sys/unstable-sha256"]

That is the part that survives contact with a real dependency graph. A workspace depending on two crates, one of which turns the feature on, ends up with it on, and setting `default = []` in your own manifest does not take it back off.

The same comment block records a threading limit that sits awkwardly beside the package description. The manifest calls the library threadsafe, yet the same file states that libgit2's builtin SHA256 implementation is not thread-safe, and tells you to enable the `https` feature to route hashing through the thread-safe OpenSSL implementation instead. The tracking reference for that sits at https://github.com/rust-lang/git2-rs/issues/1255.

Fifteen example programs, and a tree with no benchmark harness

The examples directory is the closest thing to a manual here. Fifteen programs sit in it, one per operation: add.rs, blame.rs, cat-file.rs, clone.rs, diff.rs, fetch.rs, file-latest-commit.rs, init.rs, log.rs, ls-remote.rs, pull.rs, rev-list.rs, rev-parse.rs, status.rs and tag.rs. Reading clone.rs beside fetch.rs and pull.rs shows the network paths the feature flags open, and file-latest-commit.rs shows the history walk behind a status check.

The top-level tree is wider than a crate plus a README. Alongside src/ and libgit2-sys/ sit systest/, tests/, ci/, git2-curl/, examples/, .github/, .gitignore, .gitmodules, Cargo.lock, CONTRIBUTING.md, FUNDING.json and triagebot.toml. Nothing in that list is a benchmark directory, and no benchmark procedure or performance figure is written down anywhere in the project, so any speed claim has to come from your own measurements.

One detail does not line up on a close read. The README says the libgit2 source is included in the libgit2-sys crate, and the tree separately carries a `.gitmodules` entry at the top level. Both statements can hold at once, and neither file explains which copy the build reads, so patching libgit2 locally means checking both before assuming an edit took effect.

Adopting it means owning the libgit2 build, not only a Rust dependency

Two licence statements in this repository disagree on scope: the repository header names Apache-2.0, while the licence section and the manifest both offer that licence or MIT at your option. The last push was on 2026-10-05 and the project publishes no GitHub releases, so CHANGELOG.md is the only version record in the tree.

What to check first is narrow. Confirm your toolchain clears `rust-version` 1.87, then decide the link mode deliberately, because leaving the environment alone keeps the vendored static libgit2 while setting `LIBGIT2_NO_VENDOR=1` hands responsibility for a libgit2 1.9.6 or newer patch to the build machine. If anything in your graph needs remote work, name the https and ssh features in your own manifest rather than hoping a transitive dependency turns them on, since features switched on anywhere in the chain cannot be reversed from your side.

Editorial conclusion

git2 fits a Rust service that reads or writes repositories inside its own process, and it does not fit a binary that wants remote work without deciding anything first. Before depending on it, confirm the toolchain clears rust-version 1.87 and pick the link mode on purpose. If you set LIBGIT2_NO_VENDOR=1, make sure the machine already carries libgit2 1.9.6 or a newer patch, because that variable removes the vendored fallback. The same care applies to features: anything you or a dependency switch on, including https, ssh and unstable-sha256, cannot be switched back off from your own manifest.

Frequently asked questions

Which libgit2 version does git2 need, and do I have to install libgit2 myself?

The crate requires libgit2 1.9.6 or a newer patch version. The source for libgit2 is included in the libgit2-sys crate, so no pre-install is needed, and if an appropriate version is already present the crate attempts to link it dynamically instead.

How do I make git2 clone over HTTPS or SSH?

Add the features by name with `cargo add git2 --features https,ssh`. Neither network path is available in the default build, which leaves the feature set empty.

Can I force git2 to use the system libgit2 instead of the vendored build?

Setting `LIBGIT2_NO_VENDOR=1` does it, and the environment variable overrides the `vendored-libgit2` Cargo feature. The system copy then has to be libgit2 1.9.6 or a newer patch, because the vendored fallback is no longer there to cover an older one.

Should a library author enable the unstable-sha256 feature in a published crate?

The manifest says no: it is an ABI-breaking change, later builds may break without notice, and Cargo features are additive, so downstream users cannot deactivate it once enabled by a dependency. Enabling the `https` feature routes hashing through the thread-safe OpenSSL implementation, since libgit2's builtin SHA256 implementation is not thread-safe.

Which Rust versions does git2-rs support?

The manifest sets edition 2021 and a rust-version floor of 1.87, and the README says the crate works with stable Rust and typically with the most recent prior stable release.

What do the bundled examples show?

Fifteen programs, one per operation: add, blame, cat-file, clone, diff, fetch, file-latest-commit, init, log, ls-remote, pull, rev-list, rev-parse, status and tag. The clone, fetch, pull and ls-remote programs are the ones that need the network features.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. Project website
  4. README
  5. rust-lang/git2-rs on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rust-lang-git2-rs.svg)](https://hysenlabs.com/projects/rust-lang-git2-rs)

Was this useful?