Git release workflow
SoloDB uses curated release branches. The develop branch is the shared integration branch, but it is not the source from which a release is cut. A release/<major>.<minor> branch contains only the features selected for that release. This allows completed work to be tested on develop without committing it to the next production release.
Branch model
Branch | Purpose | Created from | Merged into |
|---|---|---|---|
| The exact production history. Every production release is tagged here. | Not applicable |
|
| Broad integration and development-environment testing. It can contain features for different or unassigned releases. | Long-lived | Nowhere as a whole; never merge |
| The curated, deployable candidate for one release train, for example |
|
|
| One independently selectable feature, for example | Normally |
|
| A fix found while stabilising a release. | The affected release branch | The affected release branch; also apply it to |
| An urgent fix for the production version. |
|
|
The key rule is that develop answers “does this work with other ongoing development?”, while a release branch answers “will this ship in this version?”. A merge into develop is not a promise to release the feature.
Merge policy
Protect
main,develop, andrelease/**; do not push or force-push to them directly.Require a pull request, review, and successful automated checks for every merge.
Use merge commits for feature and release pull requests. Do not independently squash the same feature into
developand a release branch: preserving the feature commits lets Git recognise the work when histories meet again.Do not rebase a branch after it has been shared or merged anywhere.
Never merge
developwholesale into a release branch ormain.Prefer forward merges from
mainover cherry-picks. Cherry-picks are a recovery tool, not the normal promotion mechanism.Keep a feature branch available until it has either reached its assigned release or been formally cancelled.
Feature branches should stay independent. If feature B depends on feature A, record that dependency in both pull requests. A dependent feature cannot be scheduled earlier than its prerequisite. Prefer feature flags when incomplete work must coexist with released code.
Release lifecycle
1. Create and scope the release
The release manager creates the branch from the latest production main, never from develop:
Create a milestone or release ticket with:
the target version and expected date;
the feature pull requests selected for the release;
known dependencies, database changes, and rollout work;
the person responsible for the release decision.
Multiple release branches may be open at the same time. Each starts from main and contains only its own selected features. Release trains reach main in version order. When an earlier release reaches main, merge main forward into every later release branch before that later release is accepted.
2. Develop an independently selectable feature
Start the feature from current main so that it does not inherit unrelated work from develop:
Keep the feature current by merging main into it when necessary. Open a pull request from the feature branch to develop when the feature is ready for integration testing. Leave the feature branch available after that merge.
3. Assign the feature to a release
When the feature is scheduled, open a second pull request from the same feature branch to the chosen release branch. For example:
The pull request must identify the target version, dependencies, release notes, tests, and any deployment or schema steps. Merge it only after the release manager confirms the scope. A feature does not have to be merged into develop before it can enter a release, although integration testing there is normally useful.
This produces release selection such as:
Feature |
|
|
|
|---|---|---|---|
Equipment-linking feature | Yes | Yes | Inherited through |
New recipe workflow | Yes | No | Yes |
Experimental dashboard | Yes | No | No, still unassigned |
To reschedule a feature whose release pull request is still open, close that pull request and open one against the new release branch. If it has already been merged, revert its merge on the original release branch through a reviewed pull request. After the original release reaches main, forward-sync main into the later release and restore the feature there with a reviewed revert-of-revert or corrective pull request. Merging the original feature branch again is not enough because its commits are already in the shared history. Do not rewrite either release branch.
4. Stabilise the release candidate
At feature freeze, accept only fixes, documentation, release notes, and required deployment changes on the release branch. Create a bugfix/... branch from that release branch for every correction and merge it back through a pull request. Apply the same correction to develop when the affected code is present there.
Deploy and test the release branch as a complete candidate. Acceptance should include, as applicable:
PHP linting, static analysis, and PHPUnit tests;
the TypeScript build and relevant user-flow tests;
Doctrine schema validation and review of database changes;
host-specific checks for the intended
SOLODB_HOSTprofiles;generated template maps, release notes, and rollback instructions.
Do not validate a release from develop: it may contain features that are outside the release scope.
5. Publish the release
Open a pull request from
release/5.15tomainand perform the final review.Merge with a merge commit after all required checks and acceptance steps pass.
Create an annotated semantic-version tag on the resulting
maincommit and push it:git switch main git pull --ff-only origin main git tag -a v5.15.0 -m "SoloDB 5.15.0" git push origin v5.15.0Deploy the tag, publish the release notes, and verify the production deployment.
Open forward-sync pull requests from
maintodevelopand to every later active release branch.Delete
release/5.15after the deployment is verified and no release-specific follow-up remains.Delete feature branches after their release has shipped and all target pull requests are closed.
Tags are immutable. If production needs another change, publish a new patch version rather than moving an existing tag.
Production hotfixes and patch releases
Create an urgent production fix from main:
Open a pull request to main, run the normal checks and focused regression tests, and tag the merge commit as the next patch version, for example v5.15.1. Then merge main forward into develop and all active release branches. Never repair production by committing only to develop or by moving the previous tag.
Pull request responsibilities
Pull request | Required decision |
|---|---|
| Is the feature complete enough for shared integration testing? |
| Is the feature explicitly in this release's scope and independently deployable? |
| Does this fix the candidate without expanding its scope? |
| Is the complete candidate accepted for production? |
| Is the production risk urgent, understood, tested, and reversible? |
| Are all production changes represented in future work? |
The author owns implementation and test evidence. The reviewer owns code quality and risk review. The release manager owns release scope, acceptance, versioning, and the final merge to main.
Repository configuration required
For this process to be enforceable rather than conventional only:
configure branch rules for
main,develop, andrelease/**with required reviews and status checks;run the PHP and documentation checks for pull requests targeting all three branch classes, not only
develop;make release branches deployable to a release-candidate or staging environment;
reserve production deployments for version tags or accepted merges to
main;allow merge commits for promotion pull requests and disable automatic deletion of unshipped feature branches;
add “target release”, “dependencies”, “release notes”, and “rollout/rollback” fields to the pull request template.
Until CI and deployment triggers cover release/**, a release branch must not be treated as production-ready.