HIP-0517: Branch Naming — main is the Trunk, Everywhere
Preamble
A branch name is not a preference. It is an address that automation resolves:
CI triggers on it, GitOps Applications pin targetRevision to it, release gates
check it, mirrors sync it, and every README that says "clone and go" assumes it.
When the address varies per repo, every one of those systems needs a per-repo
exception, and the exception is invisible until the day it silently does nothing.
This HIP fixes the address. Across Hanzo, Lux, and Zoo the trunk is main.
The state this was written from
A survey of 1,190 non-archived repositories across hanzoai, hanzo-apps,
hanzo-templates, luxfi, lux-apps, zooai, zoo-apps, zenlm, luxcpp
and hanzo-js:
| default branch | repos |
|---|---|
main | 1066 |
master | 108 |
develop / dev | 8 |
| a leftover feature or CI branch | 5 |
| none (empty repo) | 2 |
The tail is the part that matters. Five repositories have a transient branch as
their default — luxfi/pqclean on fix-sphincs-duplicate-consistency,
zenlm/zen-private on fix-ci-1782365623, luxcpp/papers on
fix-ci-1782355258, luxfi/cggmp21.rs on m, luxfi/verkle on
account-leaf. A throwaway branch became the thing every clone, every CI run and
every dependency resolution reads first. Nobody chose that; it is what happens
when the trunk has no name the tooling can assume.
Specification
1. The trunk is main
Every repository in every Hanzo, Lux, and Zoo organization has a default branch
named main. Not master, not develop, not trunk, and never a feature or CI
branch.
This applies to repositories we own. Upstream forks keep whatever the upstream
uses on the branches that track upstream — but our own default branch is still
main, because that is the branch our tooling reads.
2. dev is the only other long-lived branch, and it is optional
A repository that needs an integration branch uses exactly one, named dev.
develop is not a synonym; pick one spelling or the tooling needs two.
Most repositories should not have one. Feature branches integrate into main
rapidly; a long-lived dev that drifts is a merge debt that compounds, and the
hanzoai/templates case below shows what the end state looks like.
3. Feature branches are <kind>/<subject>
feat/, fix/, chore/, docs/. No dates in branch names — a date in a branch
name is a comment that cannot be updated and is wrong by the second commit.
4. A feature branch may never become a default branch
If a default branch is ever observed pointing at a fix/*, feat/* or a CI-
generated name, that is an incident, not a preference: it means a force-push or a
branch deletion took the trunk with it.
5. Renaming is a rename, not a re-push
Use the rename operation, which moves open pull requests and installs a redirect:
gh api -X POST repos/<org>/<repo>/branches/master/rename -f new_name=main
Do NOT create main as a fresh branch beside master and switch the default.
That produces two branches with unrelated histories in one repository, and
every later merge fails with refusing to merge unrelated histories. This is not
hypothetical — see below.
The failure this prevents
hanzoai/templates carries both. master held 30 commits main lacked; main
held 7 commits master lacked; the two shared no common ancestor. master
is the submodule-based catalog with 68 .gitmodules entries. main is a
flattened rewrite with no .gitmodules at all and a flatten-apps.sh explaining
the intent.
Both are real work. Neither is stale. The repository cannot merge them, and no
tool can tell which one a reader should trust — git will happily serve either.
That is the cost of creating main alongside master instead of renaming, and
it is unrecoverable without a human deciding which history is canonical.