docs(git): describe phase A usage and checks
- commit
- 235a2674e472335369f506dd7eb172859d30f3ea
- author
- brice · · 2026-10-10 20:35 UTC
- parent
- eb5a076
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +30 -2
M README.md +30 -2
| @@ -2,18 +2,46 @@ | |||
| 2 | 2 | ||
| 3 | 3 | A fast, read-only Git repository reader for Go: refs, objects, packfiles, trees and history, using commit-graph and changed-path Bloom filters. Standard library only. | |
| 4 | 4 | ||
| 5 | - | Status: early development, no stable API. | |
| 5 | + | Status: early development, no stable API. Phase A (this tree) reads refs, loose and packed objects, trees, blobs and commits; history walks come next. | |
| 6 | 6 | ||
| 7 | 7 | ```go | |
| 8 | 8 | import git "bnema.dev/ermite-git" | |
| 9 | + | ||
| 10 | + | repo, err := git.Open("/srv/repos/project.git") // bare, or a ".git" directory | |
| 11 | + | if err != nil { | |
| 12 | + | return err // errors.Is(err, git.ErrUnsupported) → use the git binary | |
| 13 | + | } | |
| 14 | + | defer repo.Close() | |
| 15 | + | ||
| 16 | + | id, _ := repo.Resolve("main") | |
| 17 | + | commitID, _ := repo.PeelToCommit(id) | |
| 18 | + | ||
| 19 | + | var c git.Commit // reuse it: no allocation per commit after warm-up | |
| 20 | + | _ = repo.ReadCommit(commitID, &c) | |
| 21 | + | _ = repo.ReadTree(c.Tree, func(e git.Entry) bool { | |
| 22 | + | fmt.Printf("%s %s\n", e.Mode, e.Name) // e.Name is valid during the callback only | |
| 23 | + | return true | |
| 24 | + | }) | |
| 9 | 25 | ``` | |
| 10 | 26 | ||
| 27 | + | A `Repo` is safe for concurrent use. Call `Refresh` after the repository changed (a push, a repack) to see new refs and packs; reads in flight keep working while packs are replaced. | |
| 28 | + | ||
| 29 | + | Repositories the package does not read are rejected by `Open` with `ErrUnsupported`: SHA-256, reftable, other `extensions.*`, shallow, grafts, replace refs, alternates, partial clones, incremental or stale multi-pack-index. | |
| 30 | + | ||
| 11 | 31 | ## Test | |
| 12 | 32 | ||
| 13 | 33 | ```sh | |
| 14 | - | go test ./... | |
| 34 | + | go test -race ./... | |
| 35 | + | # also compare every object of a real repository with `git cat-file`: | |
| 36 | + | ERMITE_GIT_BENCH_REPO=/path/to/repo/.git go test -run TestRealRepository -v | |
| 37 | + | # benchmarks (the same variable runs them on that repository): | |
| 38 | + | go test -run '^$' -bench . -benchmem | |
| 39 | + | # fuzz targets: | |
| 40 | + | go test -run '^$' -fuzz FuzzParseCommit -fuzztime 30s | |
| 15 | 41 | ``` | |
| 16 | 42 | ||
| 43 | + | The tests build their fixtures with the `git` binary (2.45 or later) and compare with it. | |
| 44 | + | ||
| 17 | 45 | ## License | |
| 18 | 46 | ||
| 19 | 47 | MIT. See [LICENSE](LICENSE). | |
| @@ -2,18 +2,46 @@ | |||||
| 2 | 2 | ||||
| 3 | A fast, read-only Git repository reader for Go: refs, objects, packfiles, trees and history, using commit-graph and changed-path Bloom filters. Standard library only. | 3 | A fast, read-only Git repository reader for Go: refs, objects, packfiles, trees and history, using commit-graph and changed-path Bloom filters. Standard library only. | ||
| 4 | 4 | ||||
| 5 | - | Status: early development, no stable API. | 5 | + | Status: early development, no stable API. Phase A (this tree) reads refs, loose and packed objects, trees, blobs and commits; history walks come next. |
| 6 | 6 | ||||
| 7 | ```go | 7 | ```go | ||
| 8 | import git "bnema.dev/ermite-git" | 8 | import git "bnema.dev/ermite-git" | ||
| 9 | + | ||||
| 10 | + | repo, err := git.Open("/srv/repos/project.git") // bare, or a ".git" directory | |||
| 11 | + | if err != nil { | |||
| 12 | + | return err // errors.Is(err, git.ErrUnsupported) → use the git binary | |||
| 13 | + | } | |||
| 14 | + | defer repo.Close() | |||
| 15 | + | ||||
| 16 | + | id, _ := repo.Resolve("main") | |||
| 17 | + | commitID, _ := repo.PeelToCommit(id) | |||
| 18 | + | ||||
| 19 | + | var c git.Commit // reuse it: no allocation per commit after warm-up | |||
| 20 | + | _ = repo.ReadCommit(commitID, &c) | |||
| 21 | + | _ = repo.ReadTree(c.Tree, func(e git.Entry) bool { | |||
| 22 | + | fmt.Printf("%s %s\n", e.Mode, e.Name) // e.Name is valid during the callback only | |||
| 23 | + | return true | |||
| 24 | + | }) | |||
| 9 | ``` | 25 | ``` | ||
| 10 | 26 | ||||
| 27 | + | A `Repo` is safe for concurrent use. Call `Refresh` after the repository changed (a push, a repack) to see new refs and packs; reads in flight keep working while packs are replaced. | |||
| 28 | + | ||||
| 29 | + | Repositories the package does not read are rejected by `Open` with `ErrUnsupported`: SHA-256, reftable, other `extensions.*`, shallow, grafts, replace refs, alternates, partial clones, incremental or stale multi-pack-index. | |||
| 30 | + | ||||
| 11 | ## Test | 31 | ## Test | ||
| 12 | 32 | ||||
| 13 | ```sh | 33 | ```sh | ||
| 14 | - | go test ./... | 34 | + | go test -race ./... |
| 35 | + | # also compare every object of a real repository with `git cat-file`: | |||
| 36 | + | ERMITE_GIT_BENCH_REPO=/path/to/repo/.git go test -run TestRealRepository -v | |||
| 37 | + | # benchmarks (the same variable runs them on that repository): | |||
| 38 | + | go test -run '^$' -bench . -benchmem | |||
| 39 | + | # fuzz targets: | |||
| 40 | + | go test -run '^$' -fuzz FuzzParseCommit -fuzztime 30s | |||
| 15 | ``` | 41 | ``` | ||
| 16 | 42 | ||||
| 43 | + | The tests build their fixtures with the `git` binary (2.45 or later) and compare with it. | |||
| 44 | + | ||||
| 17 | ## License | 45 | ## License | ||
| 18 | 46 | ||||
| 19 | MIT. See [LICENSE](LICENSE). | 47 | MIT. See [LICENSE](LICENSE). | ||