Skip to main content
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:

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 into develop — and stop there. Nothing on develop reaches production until someone opens a developmain 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 develop is 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 into develop, not shipped.
  • The promotion PR needs no new content — its body is the list of squashed feature commits it carries.

Working a change

  1. Create or switch to a fresh worktree based on origin/develop.
  2. Start the branch there: git flow feature start <name>git-flow-next prepends the feature/ prefix itself, so <name> is the slug only. git flow feature start 123-fix-inventory-loader produces feature/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.
  3. 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.
  4. Open the PR against develop. Squash-merge ordinary feature PRs. Use a merge commit into develop only where preserving multiple atomic commits matters — stacked work, coordinated multi-commit changes, and release or hotfix back-merges.
  5. hotfix/* is the one exception to PR targeting: it branches from main, its PR targets main directly with a merge commit (never squash), and main is immediately back-merged into develop afterward.
  6. Small direct pushes to develop are 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 finish detects the branch type from any topic branch. Prefer finishing through the PR flow above; use finish directly 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 a develop 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.
Examples:

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.