CLI tool
libgit2/rugged avatar
libgit2/rugged

rugged: Ruby bindings to libgit2 for people who need the object model

ruby bindings to libgit2

2,311 stars293 forksCMIT

At a glance

What is it?
A self-contained gem that compiles libgit2 and exposes Git as a Ruby API, aimed at tooling that has to read and write repository internals without shelling out.
Who is it for?
rugged earns its place when your Ruby code has to reason about Git as a data structure rather than as a program it runs. Commit graphs, tree walks, index manipulation and object lookup are all reachable without a subprocess, which matters for static site generators, backup tools and repository migrations that would otherwise shell out thousands of times.
Can I use it commercially?
Yes. MIT 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 80 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

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

Editorial analysis

A gem that compiles a C library on the way in

Rugged is not a pure Ruby wrapper around a subprocess. It bundles the libgit2 sources and builds them during gem installation, which is why installing it needs a toolchain rather than just a Ruby:

bash
$ gem install rugged

The prerequisites are CMake and pkg-config. On Debian-derived Linux the README lists them explicitly:

bash
$ sudo apt install libgit2-dev cmake pkg-config

On macOS, Homebrew supplies the pair:

bash
$ brew install cmake pkg-config

The README also names the exact failure this prevents, telling you to follow the Homebrew step if installation dies with `ERROR: CMake is required to build Rugged.` That is a useful detail, because a missing build dependency otherwise shows up as an opaque native extension error.

The version constraint is the part to notice. The README states that the major and minor versions of libgit2 and rugged must match if you build against the system library, so this is not a gem you can point at an arbitrary distro package and expect to work. Vendoring by default sidesteps that, at the cost of compiling every time.

Turning on SSH, because it is not on by default

Out of the box you get HTTPS and no SSH transport, which surprises people who clone over SSH by reflex. The README points at libgit2's optional dependencies and then offers two routes to SSH, and the choice between them is a real design decision rather than a preference.

Using libssh2, the recommended option, either through an environment variable or an install flag:

bash
CMAKE_FLAGS='-DUSE_SSH=ON' gem install rugged

Or by handing a build option to the gem installer:

bash
gem install rugged -- --with-ssh

The alternative is to execute the system OpenSSH binary rather than link libssh2, selected with `-DUSE_SSH=exec` or `--with-ssh-exec`. The README describes this one as handy when you want SSH behavior configured through `.ssh/config`, which is the real trade: libssh2 is self-contained and predictable, while delegating to OpenSSH inherits whatever your config and agent setup already do.

Bundler users get the same choice through configuration rather than environment variables:

bash
bundle config build.rugged --with-ssh

There is also a `:submodules` option for bundling libgit2 from the repository's own git source, which matters when you need a specific commit rather than a released version.

Opening a repository, discovering one, or creating a bare one

Loading rugged is a single require, after which the Repository class is the entry point to everything else:

ruby
require 'rugged'

Open an existing repository by path, and the constructor reports the git directory it resolved to:

ruby
repo = Rugged::Repository.new('path/to/my/repository')
# => #<Rugged::Repository:2228536260 {path: "path/to/my/repository/.git/"}>

Create one with `init_at`, adding `:bare` for a repository without a working tree:

ruby
Rugged::Repository.init_at('.', :bare)

The most useful method for tooling is `discover`, which walks upward from a subdirectory and returns the repository root. This is what lets a linter or generator work on the current directory regardless of where it was invoked:

ruby
Rugged::Repository.discover("/Users/me/projects/repo/lib/subdir/")
# => "/Users/me/projects/repo/.git/"

The README then walks through the accessors you would reach for first: `bare?`, `empty?`, `head_unborn?` and `head_detached?` for state, `path` and `workdir` for locations, and `head` for the current reference. Note that `discover` returns a path string while `new` returns a Repository, which is a small asymmetry worth remembering.

Reading objects and writing commits without a subprocess

The Object hierarchy is where rugged earns its keep. Every object exposes an oid, a type of `:commit`, `:tree`, `:blob` or `:tag`, and `read_raw` for the undecoded payload. Reading a commit gives you the message, the tree and the object id without parsing anything yourself:

ruby
object = repo.read('a0ae5566e3c8a3bddffab21022056f0b5e03ef07')
# => #<Rugged::OdbObject:0x109a64780>
object.len
# => 237
object.data
# => "tree 76f23f186076fc291742816721ea8c3e95567241\nparent 8e3c5c52b8f29da0adc7e8be8a037cbeaea6de6b\nauthor Vicent Mart\303\255 <[email protected]> 1333859005 +0200\ncommitter Vicent Mart\303\255 <[email protected]> 1333859005 +0200\n\nAdd `Repository#blob_at`\n"
object.type
# => :commit

Writing is where the library saves real work. The low-level form writes arbitrary content with an explicit type, and the higher-level path builds an index from the current HEAD tree, stages an entry, and creates a commit with a full options hash:

ruby
oid = repo.write("This is a blob.", :blob)
index = repo.index
index.read_tree(repo.head.target.tree)
index.add(:path => "README.md", :oid => oid, :mode => 0100644)

The commit call takes author, committer, message, parents and an `update_ref`, and the README notes that parents should be empty when the repository is empty. That last detail is the kind of edge case that normally turns into a runtime crash in a hand-rolled implementation.

The README also documents the staging area, tree walking, and tags as separate areas, and describes the object types as blobs, commits, tags and trees.

Bundling libgit2 is convenient, with a caveat worth reading

The default is to build and use the bundled libgit2, which means the gem is self-contained and your build does not depend on what your distribution happens to ship. The README notes that Rugged supports only specific versions of libgit2, which is the catch on both paths.

Building against the system library is available in two forms, as an install option or a bundler setting:

bash
gem install rugged -- --use-system-libraries

The README also suggests a bundler route that pairs the flag with an install, which is the combination you want in a lockfile-driven deployment so the flag is recorded rather than remembered.

On maintenance, the picture is worth stating plainly rather than inferring. The repository is not archived and the last push recorded is 2026-07-19, so work is happening. The release tags, though, are old: the newest is v1.1.0 from October 2020, before v0.24.0 in 2016 and v0.23.3 in 2015. Those two older releases have descriptive bodies about updating the bundled libgit2, while v1.1.0 has an empty one, so the tagged releases are not where recent history lives. With 121 open issues and 293 forks, this is a widely used dependency whose ongoing changes arrive through the default branch rather than through GitHub releases. If you pin a version in production, pin it deliberately and check the branch, not the release list.

Editorial conclusion

rugged earns its place when your Ruby code has to reason about Git as a data structure rather than as a program it runs. Commit graphs, tree walks, index manipulation and object lookup are all reachable without a subprocess, which matters for static site generators, backup tools and repository migrations that would otherwise shell out thousands of times. The tradeoff is that you are binding to a C library through FFI, so install failures are compiler problems, version matching matters, and SSH support is off unless you ask for it at install time. Read the install section before anything else, decide whether you want the vendored or system libgit2, and treat the version requirement as a real constraint rather than a footnote.

Frequently asked questions

What is libgit2 and what does rugged add to it?

libgit2 is a pure C implementation of Git's core methods, designed to be fast and portable. rugged is a Ruby gem that compiles libgit2 and exposes it as a Ruby API, so you can read and write objects, walk trees and manipulate the index without shelling out to the git binary.

How do I install rugged with SSH support?

SSH is off by default. Either pass the build flag with gem install rugged -- --with-ssh, which links libssh2, or use CMAKE_FLAGS='-DUSE_SSH=ON' gem install rugged. To delegate to the system OpenSSH binary instead, use --with-ssh-exec, which lets you configure behavior through your .ssh/config.

Can rugged use my system libgit2 instead of the bundled one?

Yes, with gem install rugged -- --use-system-libraries, or the equivalent bundle config build.rugged --use-system-libraries. The constraint is that rugged supports only specific versions of libgit2, so the major and minor versions of the system library and rugged must match.

How does rugged compare with running git from Ruby?

Shelling out spawns a process per command and forces you to parse text output, which is slow and fragile when you are walking a large commit graph. Rugged works on the object model in process, returning typed objects such as commits, trees and blobs with their raw data available. The cost is a native build dependency and tighter version coupling to libgit2.

Official sources

  1. Issues
  2. libgit2/rugged on GitHub
  3. License: MIT
  4. README
  5. Releases
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/libgit2-rugged.svg)](https://hysenlabs.com/projects/libgit2-rugged)