main de06cbd

Fast read-only Git repository reader for Go

README.md

Read-only mirror. Development happens at git.bnema.dev; issues and pull requests are closed here.


Features

  • Refs, loose and packed objects, trees, blobs and commits
  • History in git log order, with git log -- path simplification
  • Commit-graph and changed-path Bloom filters for fast history
  • Safe for concurrent use; no allocation per commit after warm-up

Status: early development, no stable API.

Usage

import git "bnema.dev/ermite-git"

repo, err := git.Open("/srv/repos/project.git") // bare, or a ".git" directory
if err != nil {
	return err // errors.Is(err, git.ErrUnsupported) → use the git binary
}
defer repo.Close()

id, _ := repo.Resolve("main")
commitID, _ := repo.PeelToCommit(id)

var c git.Commit // reuse it
_ = repo.ReadCommit(commitID, &c)
_ = repo.ReadTree(c.Tree, func(e git.Entry) bool {
	fmt.Printf("%s %s\n", e.Mode, e.Name) // e.Name is valid during the callback only
	return true
})

for c, err := range repo.Walk(ctx, commitID, git.WalkOptions{Path: "pkg/gui", Limit: 50}) {
	if err != nil {
		return err
	}
	fmt.Println(c.ID) // c is reused: valid until the next iteration
}

// last commit of each entry of a directory, in one walk
ids, err := repo.LastCommits(ctx, commitID, "pkg", []string{"gui", "commands"})

Call Refresh after the repository changes (push, repack) to see new refs and packs.

Open returns ErrUnsupported for SHA-256, reftable, other extensions.*, shallow, grafts, replace refs, alternates, partial clones, and incremental or stale multi-pack-index.

Known limits: docs/limits.md.

Test

Tests compare with the git binary, 2.45 or newer (skipped without it).

go test -race ./...
ERMITE_GIT_BENCH_REPO=/path/to/repo/.git go test -run TestRealRepository -v   # every object vs git cat-file
go test -run '^$' -bench . -benchmem
go test -run '^$' -fuzz FuzzParseCommit -fuzztime 30s

License

MIT. See LICENSE.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 2

↑↓ moveenter openesc close