Release Branch Cut and Freeze Workflow
Cut a release branch from main, freeze feature merges into it, and channel last-minute changes through cherry-picks with review gates.
Objective
Create a stabilized release branch that only receives reviewed fixes, keeping main free for ongoing development.
Environment
- A main branch with green CI
- A release calendar or trigger event
- Cherry-pick and tag permissions on the repository
Steps
1. Verify main is releasable
Run the full CI pipeline on main and confirm tests, security scans, and build artifacts are green before cutting.
Expected state: A green pipeline run with a recorded URL exists for the exact commit being cut.
2. Cut the release branch
Create release/1.8 from the verified commit with a naming convention that encodes the version.
Expected state: The branch exists and its head commit matches the verified green run.
3. Freeze direct merges
Set branch protection on the release branch: no direct pushes, merges only via reviewed PRs labeled fix or cherry-pick.
Expected state: Direct push attempts are rejected by the platform.
4. Define the cherry-pick path
Document that late changes land on main first (or the fix branch), then cherry-pick into release with the original PR link in the description.
Expected state: Every release-branch commit traces to a reviewed PR, none are hand-crafted fixes.
5. Tag the candidate
Tag v1.8.0-rc1 on the release branch and produce build artifacts from that tag only.
Expected state: Artifacts carry the tag, and rebuilding the tag reproduces the same artifact hash.
Decision points
Cut from main or from a stabilization integration branch?
- Cut from main when trunk stays green
- Cut from an integration branch when features land broken
Start with main plus a freeze; add an integration branch only if main red-rate makes candidates unstable.
How late can fixes land?
- Until tagging
- Until RC2 only
Freeze fixes two working days before final tag to leave room for verification runs.
Expected outcome
A protected release branch whose every commit is a reviewed fix, and release candidates built from immutable tags rather than moving heads.
Verification
Branch protection settings show blocked direct pushes; the release branch log shows only merge commits referencing PRs; the rc1 tag hash matches the shipped artifact.
Freeze is a decision, not a hope
Cutting a release branch without protection just creates a second place where unreviewed changes can appear. The workflow makes the freeze mechanical: branch protection rejects everything except reviewed cherry-picks, and candidates build only from tags.
# cut from a verified green commit
git checkout -b release/1.8 <verified-sha>
git push origin release/1.8
# protect: no pushes, PR-only, required reviews
# every late fix:
git checkout main && git cherry-pick <fix-sha>
git checkout release/1.8 && git cherry-pick <fix-sha>
git tag v1.8.0-rc1 release/1.8 && git push origin v1.8.0-rc1
Fixes flow main-first
Cherry-picking straight into the release branch creates commits that exist nowhere else and will conflict with the next release. The rule that fixes land on main first and are then picked keeps the branches convergent and the fix permanently in the product.
Build from tags, not heads
A release branch head moves with every late fix; a tag does not. Building artifacts from the tag makes the candidate reproducible and lets anyone verify that the shipped bytes came from the reviewed state.