Read the type and the scope from the branch name alone — no git log required.
Which model applies
Every repo runs one of two branch models. Check the repo’s default branch (git remote show origin or the GitHub settings page) — never guess it from whatever branch happens to be checked out locally:
- Default branch
develop→ the repo is on git-flow. Read git-flow branch types below. - Default branch
main→ the repo uses trunk flow. Read trunk-flow branch types below.
git-flow branch types (repos with develop)
git-flow repos follow the model implemented by git-flow-next. The git flow CLI is installed and configured globally (nix-managed gitflow.* git config).
Every merge into
main produces a release: release-please watches main, opens the release PR against it, and tags on merge. The release PR merge-commits like any other main PR — it is not a squash exception. See PR conventions for the full merge-strategy breakdown.
Promotion is a step you take, not an event that happens
Feature PRs squash intodevelop — and stop there. Nothing on develop reaches production until someone opens a develop → main promotion PR and lands it with a merge commit (never squash, never rebase). Each promotion merge is a release.
This is the step that gets skipped: features pile up on develop, everything looks “done” in the PR list, and main — the branch releases are cut from — quietly falls weeks behind. Treat promotion as part of finishing work, not a separate ceremony:
- When a change on
developis worth having in production, promote. Small, frequent promotions beat batched ones — each release stays reviewable and revertible. - Before calling a stretch of work complete, check
git log origin/main..origin/develop. Commits listed there are merged intodevelop, not shipped. - The promotion PR needs no new content — its body is the list of squashed feature commits it carries.
Working a change
- Create or switch to a fresh worktree based on
origin/develop. - Start the branch there:
git flow feature start <name>—git-flow-nextprepends thefeature/prefix itself, so<name>is the slug only.git flow feature start 123-fix-inventory-loaderproducesfeature/123-fix-inventory-loader; passing the prefix yourself doubles it. Include the GitHub issue number when one exists. Never invent an issue number; use a short descriptive slug for issue-less maintenance. - Commit atomically — one fix, one feature, or one coherent section of updates per commit. Follow Commit conventions and reference the issue (
#123), using a closing keyword (fixes #123) when the change closes it. - Open the PR against
develop. Squash-merge ordinary feature PRs. Use a merge commit intodeveloponly where preserving multiple atomic commits matters — stacked work, coordinated multi-commit changes, and release or hotfix back-merges. hotfix/*is the one exception to PR targeting: it branches frommain, its PR targetsmaindirectly with a merge commit (never squash), andmainis immediately back-merged intodevelopafterward.- Small direct pushes to
developare fine; open a PR for anything reviewable.
git-flow-next usage
git flow feature start <name>/git flow release start <version>/git flow hotfix start <name>create correctly-based branches.git flow finishdetects the branch type from any topic branch. Prefer finishing through the PR flow above; usefinishdirectly only where a PR is overkill (e.g. local-only cleanup) and the branch target allows direct pushes.- Config keys live in git config (
gitflow.branch.*), set globally via Nix — don’t override them per-repo without a recorded reason.
Trunk-flow branch types (Conventional Branch)
Repos without adevelop branch use trunk-based flow: no feature/*, release/*, or hotfix/* git-flow types, and PRs squash-merge straight into main (see PR conventions).
Branch names follow Conventional Branch: a short type prefix, a /, and a kebab-case description. The type matches the commit conventions prefix that the eventual squash commit will use, so the branch name predicts the changelog entry.
Shape
<type>is one of the allowed short forms below — never the long form.<short-kebab-case-name>is lowercase, hyphen-separated, descriptive but tight (≤ 5 words is usually enough).- No issue numbers, no usernames, no dates — those belong in commit bodies and PR descriptions, not branch names.
Allowed types — short forms only
These match the Conventional Commits prefixes one-to-one. Always the short form.feat not feature. fix not bugfix. No exceptions.
If two types fit, pick the one that produces the right version bump.
feat: → minor, fix: → patch, everything else → no version bump (per release-please defaults).
Why short forms, on trunk-flow repos
On a trunk-flow repo, the long forms (feature, bugfix) don’t appear in any Conventional Commits / Conventional Branch spec. Mixing both creates two parallel taxonomies that release tooling, branch protection rules, and CODEOWNERS regexes have to match independently. One canonical set keeps everything (CI, automation, AI agents, humans skimming git branch) on the same page.
The full Conventional Branch spec lives at conventional-branch.github.io; the commitlint config that enforces the matching commit prefixes is @commitlint/config-conventional. Its own release/ and hotfix/ types are distinct from git-flow-next’s identically-named branch categories above — trunk-flow repos use neither; git-flow repos use git-flow-next’s.
What this connects to
Commit conventions
The matching commit prefix vocabulary. Branch type and commit type are one decision, made once.
PR conventions
PR title inherits the conventional-commit prefix from the branch.
Git transport
SSH for public, HTTPS for private. Picks transport by visibility, not by branch.
CI/CD policy
How
feat: and fix: translate into release-please version bumps once the branch merges.