Skip to content
gitmeta

go · one scan · o(1) lookups

Per-file git metadata, fast.

One batch scan of a working tree, then constant-time per-path lookups for last-commit time, author, churn, and tracked/ignored status — a 10k-file repo costs one git pass, not ten thousand.

MIT licensed · no third-party dependencies · needs the system git binary

gitmeta.go
cache, _ := gitmeta.New(ctx, "/path/to/repo")
if cache == nil { return } // not a git working tree
if info, ok := cache.Lookup(path); ok {
    fmt.Println(info.LastCommitTime, info.LastCommitAuthor, info.CommitCount)
}
cache.IsTracked(path) // bool

what you get

Git history, at lookup speed.

Pay the git cost once, up front — then every per-path question is a map read. Built for tooling that touches thousands of files.

One scan, then O(1)

git ls-files + a single git log pass up front; per-path lookups are constant time — no git log -1 -- <path> per file.

Rich per-file info

Last-commit time / author / subject, first-seen date, and commit count as a CommitCount churn proxy — everything a "hot file" question needs.

Tracked / ignored

IsTracked and IsIgnored status checks straight off the scan, matching git's own check-ignore semantics.

Pool for long-running processes

One Cache per repo, re-validated on HEAD change — ideal for servers, watchers, and tooling that answer many queries over an unchanging tree.

Zero dependencies

Pure Go, no third-party imports. It just needs the system git binary on your PATH.

Graceful degradation

Returns a nil Cache when it isn't a git tree or git is absent (HasGitBinary() probe), so callers just treat it as "no git data".

usage

Scan once, look up anywhere.

A one-shot Cache for a single walk; a Pool when a long-running process serves many lookups over the same repo.

// One-shot: scan a tree, then look up paths
cache, err := gitmeta.New(ctx, root)
if err != nil { /* git error */ }
if cache == nil { /* not a git tree */ }

if info, ok := cache.Lookup(path); ok {
    fmt.Println(info.LastCommitTime,
        info.LastCommitAuthor, info.CommitCount)
}
// Long-running: one Pool, reused across requests
pool := gitmeta.NewPool()

// built once per repo; refreshed when HEAD moves
cache, err := pool.Get(ctx, root)
if cache != nil {
    cache.IsTracked(path)
    cache.IsIgnored(path)
}
  • New
  • Lookup
  • IsTracked
  • IsIgnored
  • NewPool
  • Pool.Get
  • HasGitBinary

install

Add it to your module.

One import, no transitive dependencies — just the git binary at runtime.

go get go get github.com/richardwooding/gitmeta
import import "github.com/richardwooding/gitmeta"

Extracted from file-search-on, where it powers the git_* search attributes. Full API on pkg.go.dev.