In a trunk-based project a release is a tag on main. Changelog links are built from the repository URL, and ../server/booknest.git would make them point nowhere, so Sam names one before cutting BookNest 1.2.0:
npm pkg set repository.url=https://git.example.com/sam/booknest.git
git commit -qam "build: name the repository for changelog links"
npx commit-and-tag-version --sign
cat CHANGELOG.md
git push -q --follow-tags
git log --cherry-mark --right-only --format='%m %h %s' main...release/1.1... ✔ bumping version in package.json from 1.1.0 to 1.2.0 ... ✔ committing package-lock.json and package.json and CHANGELOG.md ... ✔ tagging release v1.2.0 ... ## [1.2.0](https://git.example.com/sam/booknest/compare/v1.1.0...v1.2.0) (2026-09-25) ### Features * **api:** filter books by minimum rating ([92f2964](https://git.example.com/sam/booknest/commi t/92f2964b1329dc7b0c1855a6bc66cb6435ab9cc0)) ... > 264b373 Release 1.1.1 = ebadef9 Clamp ?limit= to 1-100 instead of failing with a 500
One feat since v1.1.0 made it a minor release. The build, chore and docs commits stayed out of the changelog, and so did every pre-convention message. --sign signed the release commit and the annotated tag v1.2.0, and --follow-tags pushed the tag with main.
A release branch earns its place when a released version needs a fix but main already holds changes that must not ship with it. BookNest has one: release/1.1, cut from v1.1.0 in Stash, Worktrees and Bisect for the 1.1.1 fix. A hotfix follows the rule "upstream first": fix main, then git cherry-pick -x the commit onto the release branch (Cherry-Picking) and tag a patch release there. Fixing only the release branch invites a regression, the bug returning in the next release from main. The last command checks the rule: --cherry-mark compares patches, not hashes, so = marks a release commit whose change is already on main and > one that is not. Delete a release branch when its version loses support; its tags keep every release reachable.