Simple Git plumbing library for Zig.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Mikael Säker 9f4d655089 docs: record audit session
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:03:19 +02:00
src fix: audit findings — refs, index, status, write path, blame 2026-07-19 01:03:02 +02:00
tests fix: audit findings — refs, index, status, write path, blame 2026-07-19 01:03:02 +02:00
.gitignore diff: indent heuristic + extensive git-parity tests 2026-06-28 09:46:10 +02:00
build.zig tests: integration suite against system git (zig build test-integration) 2026-07-12 17:39:08 +02:00
build.zig.zon Port to Zig 0.16 2026-04-30 07:25:17 +02:00
CLAUDE.md Initial project: git plumbing library with object, commit, tree, and refs modules 2026-03-03 12:35:21 +01:00
context.md docs: record audit session 2026-07-19 01:03:19 +02:00
DESIGN.md docs: record fuzz harnesses and wrinkle-hunt results 2026-07-18 21:28:03 +02:00
LICENSE Initial project: git plumbing library with object, commit, tree, and refs modules 2026-03-03 12:35:21 +01:00
README.md Port to Zig 0.16 2026-04-30 07:25:17 +02:00

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 (v2v4)

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 add and reset (unstage) (no directory/glob staging yet)
  • Commit creation with system timezone detection
  • Line-level blame (first-parent walk)

License

MIT