Reading and writing git repositories in Zig, without running git.
zig-git CONTRIBUTING.md
6.4 kB · 140 lines
Markdown
at main

Contributing to zig-git #

This file covers what you need to know to make a change and get it accepted. README.md says what zig-git is and where it is going.

Where to send changes #

Report issues and send pull requests to the Tangled mirror: https://tangled.org/jcollie.dev/zig-git.

Setting up #

Everything is pinned in the Nix flake, Zig 0.17.0 and git included:

$ git clone https://git.jcollie.dev/jeff/zig-git.git
$ cd zig-git
$ nix develop

Without Nix you need Zig 0.17.0 and a git on PATH. The tests that read a repository make it with git; without one they are skipped.

Before you send a change #

CI runs these, and a change has to pass all of them:

$ reuse lint                            # every file has a license
$ zig fmt --check --exclude zig-pkg .   # Zig is formatted
$ zig build test --summary all          # every test, under SHA-1 and SHA-256
$ zig build check                       # compiles what nothing else builds
$ nix build                             # the package; runs the tests in the sandbox

nix build sees only files git knows about. git add a new file before running it, or the build fails as if the file were missing.

Where things are #

Path What
src/root.zig The public API: what the git module exports
src/object.zig Object formats, object IDs, kinds, headers, hashing
src/Identity.zig Author, committer and tagger lines
src/commit.zig, src/tag.zig, src/tree.zig Views of commits, tags and trees
src/quote.zig Paths quoted as git quotes them
src/ObjectDatabase.zig The object database interface
src/loose.zig Loose objects
src/zlib.zig zlib streams, inflated and checked as git checks them
src/PackIndex.zig Pack indexes (.idx), versions 1 and 2
src/pack.zig Packs: entries, delta chains, reading objects
src/delta.zig Applying a delta to its base
src/MultiPackIndex.zig The multi-pack-index
src/alternates.zig Reading objects/info/alternates and GIT_ALTERNATE_OBJECT_DIRECTORIES
src/DiskDatabase.zig The object database on disk: every objects directory, pack and loose object
src/Config.zig Configuration: its sources, includes, scopes and lookups
src/config/syntax.zig The configuration file syntax, and keys spelled as git spells them
src/config/value.zig Booleans, integers and paths as git reads them
src/config/parameters.zig GIT_CONFIG_PARAMETERS and GIT_CONFIG_COUNT
src/wildmatch.zig git's glob patterns
src/Diagnostics.zig What a failed read was about
src/testing/fixture.zig Repositories made with git for the tests
src/testing/packs.zig Packs and pack indexes written byte by byte for the tests
src/testing/fuzz.zig Fuzz test inputs from the corpus or the fuzzer
src/fuzz/ The fuzz corpus: files git wrote, and patterns
tools/zig_git.zig zig-git, git's commands answered by the library, for comparing with git
tools/config_command.zig zig-git config
tools/regex.zig POSIX extended regular expressions, for zig-git config --get-regexp
tools/docs_server.zig zig build docs-serve
GLOSSARY.md The words for the things in a repository
docs/adr/ Decisions, and why

Making changes #

Never from git's source #

zig-git is MIT licensed and git is GPL-2.0-only, so nothing here may be translated from git's source, nor from libgit2, xdiff or any other implementation whose license this project could not carry (docs/adr/0004-written-from-documentation-never-gits-source.md). Don't read them while working on zig-git. What git does comes from its documentation — the manual pages, gitformat-*, the protocol documents — and from running it. When the documentation is silent, ask git: make a repository that exercises the case and see what it answers.

Words #

Use the words in GLOSSARY.md, in code and in comments: an object ID, never a SHA or an OID; the index is the staging file, and a pack's .idx is a pack index. A new concept gets an entry there when it gets a name.

git is the oracle #

Correct means what git does. A test that reads a repository makes it with git through src/testing/fixture.zig and checks the library's answer against what git says about the same repository, rather than against a value written into the test. The fixture runs git with no system or global configuration and a fixed clock, so object IDs are the same on every machine. The git the tests compare with is the one the flake pins.

zig build test runs every test twice, the second time with ZIG_GIT_TEST_OBJECT_FORMAT=sha256, so each repository test reads a SHA-1 repository and a SHA-256 one. Nothing may assume an object ID is 20 bytes.

Repositories git refuses to make — a corrupt object, a malformed tree — are written byte by byte in the test that needs them.

zig-git (tools/zig_git.zig) is the same idea from the command line: some of git's commands answered by the library and printing what git prints, so diff <(zig-git ls-tree -r HEAD) <(git ls-tree -r HEAD) checks the library against git on any repository. Its tests run each command both ways and compare the output and exit status.

Layers and I/O #

Reading a format (an object header, a commit, a tree) is done on bytes the caller already has, with no I/O, so it can be tested and fuzzed alone. Only the storage layer touches files, through a std.Io it is given. Nothing reads the environment, the clock or the current directory on its own.

Errors #

Anything a repository can get wrong is an error, never a panic, and a function that can fail on the repository's account takes an optional *Diagnostics and fills it in. Something this library does not support returns an error saying so rather than an answer that is nearly right; see docs/adr/0002-unsupported-revision-options-are-errors.md.

Documentation #

Every public declaration has a doc comment, since the published API documentation is made from them. Comments say why rather than what. The README describes the project as it is now, and its references cite the documents the code was written from.