- Zig 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| src | ||
| tests | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| CLAUDE.md | ||
| context.md | ||
| DESIGN.md | ||
| LICENSE | ||
| README.md | ||
plumbuz
A Git plumbing library for Zig. Pure Zig, no subprocesses, no C dependencies.
Designed for embedding in applications — editors, TUIs, dev tools — where you need direct access to Git internals without shelling out to git. Comes with a small CLI for testing and exploration.
Features
Object store — read and write loose objects (blob, tree, commit, tag) with SHA-1 and SHA-256 support. Pack file reading with index caching.
Refs — resolve symbolic refs, read HEAD, list branches
Index — parse, mutate, and write .git/index (v2–v4)
Staging & commits — full git add + git commit workflow with automatic timezone detection, git reset (unstage) support
Diff — tree-to-tree comparison and line-level Myers diff
Log — commit walker with priority queue (reverse chronological, dedup)
Status — working tree status: staged changes, unstaged modifications, untracked files
Blame — line-level attribution via first-parent walk and diff mapping
.gitignore — hierarchical pattern matching (.git/info/exclude, root, per-directory)
Library usage
Add plumbuz as a Zig dependency. plumbuz follows Zig 0.16's explicit-I/O pattern: pass a std.Io (typically from std.process.Init.io) into Repository.open, after which the handle threads it through internally.
const std = @import("std");
const plumbuz = @import("plumbuz");
pub fn main(init: std.process.Init) !void {
const io = init.io;
const allocator = init.gpa;
// Open a repository
var repo = try plumbuz.Repository.open(allocator, io, ".");
defer repo.close();
// Stage and unstage files
try repo.addFile("src/main.zig");
try repo.unstageFile("src/main.zig");
// Get status
var status = try repo.getStatus();
defer status.deinit();
for (status.files) |f| {
std.debug.print("{s}: index={s} worktree={s}\n", .{
f.path,
@tagName(f.index_status),
@tagName(f.worktree_status),
});
}
// Walk commit history
var walker = try repo.logFrom("HEAD");
defer walker.deinit();
while (try walker.next()) |*entry| {
defer entry.deinit();
var hex_buf: [64]u8 = undefined;
const hex = entry.oid.toHex(&hex_buf);
std.debug.print("{s} {s}\n", .{ hex[0..7], entry.commit.message });
}
// Blame a file
var result = try repo.blameFile("src/main.zig");
defer result.deinit();
for (result.lines) |line| {
var hex_buf: [64]u8 = undefined;
const hex = line.oid.toHex(&hex_buf);
std.debug.print("{s} ({s} {d}) {s}\n", .{
hex[0..7], line.author.name, line.line_no, line.line,
});
}
// Read objects directly
const oid = try repo.resolveRef("HEAD");
var obj = try repo.readObject(oid);
defer obj.deinit();
}
CLI
$ zig build
$ ./zig-out/bin/plumbuz help
Usage: plumbuz <command> [args]
Commands:
add Stage a file
reset Unstage a file
commit Record changes to the repository
status Show working tree status
log Show commit history
branch List branches
diff Show changes between HEAD~1 and HEAD
blame Show line-by-line attribution
cat-file Display object contents
help Show this help
Examples
$ plumbuz status
On branch main
Changes not staged for commit:
modified: src/index.zig
Untracked files:
notes.txt
$ plumbuz add src/index.zig
$ plumbuz log -n3
commit a1b2c3d4...
Author: Alice <alice@example.com>
Date: Mon Mar 3 10:30:00 2026 +0100
Add staging workflow
$ plumbuz branch
* main
feature/parser
$ plumbuz blame src/main.zig
a1b2c3d (Alice 1) const std = @import("std");
f8e9d0c (Bob 2) const plumbuz = @import("plumbuz");
a1b2c3d (Alice 3) ...
$ plumbuz reset src/index.zig
Unstaged 'src/index.zig'
$ plumbuz cat-file a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0
commit (size 231)
tree 4b825dc642cb6eb9a060e54bf899d15644e68cb5
parent ...
Building
Requires Zig 0.16.
zig build # build CLI
zig build test # run test suite
Status
Early development. Read path is solid, write path covers staging and commits. Currently supports:
- SHA-1 (default) and SHA-256 object formats (auto-detected from
.git/config) - Loose object read/write and pack file reading (OFS_DELTA/REF_DELTA)
- Hierarchical
.gitignore(.git/info/exclude, root, per-directory) - Single-file
addandreset(unstage) (no directory/glob staging yet) - Commit creation with system timezone detection
- Line-level blame (first-parent walk)
License
MIT