Git Strategy
This project uses GitFlow adapted for per-platform releases. The KMP mono-repo ships six independently versioned artifacts (Android, iOS, Desktop, Server, Web, WASM), and mobile releases sit in app-store review for days while main keeps moving — a single-trunk model can’t absorb that reality, so we keep a stabilization line per platform.
This doc owns how work flows through branches. Two sibling docs own the adjacent concerns:
- Versioning — the
VERSIONfile, tag scheme, and how build scripts read versions. - Release checklist — the step-by-step execution when you’re actually shipping.
Branch model
| Branch | Cut from | Merged into | Merge style | Purpose |
|---|---|---|---|---|
main | — | — | — | Production mirror. Every commit is a shipped artifact and is tagged by a publish workflow. |
develop | main (once) | — | — | Integration line. Default base for feature branches. |
feat/*, fix/*, chore/*, docs/*, refactor/* | develop | develop | squash | Day-to-day work. Short-lived. |
release/{platform}/{version} | develop | main (then back-merge to develop) | merge commit | Per-platform stabilization. Only bug fixes and the /VERSION bump. |
hotfix/{platform}/{version} | tag {platform}/v{X.Y.Z} on main | main (then back-merge to develop) | merge commit | Fix a shipped artifact without waiting for the next cycle. |
main accepts merges only from release/* and hotfix/*. Feature branches never target main directly.
Naming conventions
Branches:
- Feature work:
feat/<slug>,fix/<slug>,chore/<slug>,docs/<slug>,refactor/<slug> - Release:
release/{platform}/{version}(e.g.release/server/1.2.0,release/android/1.1.0) - Hotfix:
hotfix/{platform}/{version}(e.g.hotfix/server/1.1.1)
Platforms: android, ios, desktop, server, web, wasm.
Tags: {platform}/v{X.Y.Z} — e.g. server/v1.0.0, android/v1.0.0. Created by publish workflows only; never tag by hand.
Pull requests
- Default base:
develop. mainaccepts onlyrelease/*andhotfix/*PRs.- Merge style:
- Squash for
feat/*,fix/*,chore/*,docs/*,refactor/*→develop. - Merge commit for
release/*andhotfix/*→main, and for the back-mergemain→develop. Preserving the release lineage matters for hotfix cherry-picks.
- Squash for
- CI must be green before merge. Solo self-merge is fine, but only after CI passes.
- Size: small PRs. Split shared-code refactors from feature landings; land the refactor first.
Commit messages — Conventional Commits
Format: type(scope): subject — imperative mood, ≤72 chars for the subject.
Types: feat, fix, chore, docs, refactor, test, build, ci, perf, style, revert.
Scopes — either a platform or a feature (or both, comma-separated):
- Platforms:
server,android,ios,desktop,web,wasm,shared. - Features:
finance,tasks,habits,journal,calendar,study,nutrition,timers,workout,work,books,notifications,dashboard,onboarding, and anything else living undershared/.
Breaking changes: ! before the colon, plus a BREAKING CHANGE: footer explaining migration.
Release commits: chore(release): {platform} v{X.Y.Z} — as release-checklist.md already uses.
Examples:
feat(finance): add net-worth widget to dashboard
fix(server): reject expired refresh tokens on WebSocket upgrade
refactor(shared,android): extract timezone helper into shared/util
feat(server)!: rename POST /meals payload field servingSize -> servingsPerMeal
chore(release): server v1.1.0Per-platform release flow
For each platform independently:
- Cut
release/{platform}/{version}fromdevelop. - Bump the platform’s keys in
/VERSION, move[Unreleased]→[X.Y.Z]in the platform’sCHANGELOG.md, and if iOS is affected runscripts/ios_generate_version_xcconfig.sh. Commit aschore(release): {platform} v{X.Y.Z}. - PR
release/*→main. Merge with a merge commit (not squash) once CI is green. - Trigger the platform’s publish workflow from Actions on
main. It tags{platform}/v{X.Y.Z}on the merge commit. - Back-merge
main→developand delete the release branch.
The verification steps (spot-check the artifact, confirm the tag, etc.) live in release-checklist.md.
Hotfix flow
Same shape as a release, but starting from a shipped tag rather than develop:
git checkout -b hotfix/{platform}/{X.Y.Z+1} {platform}/v{X.Y.Z}— cut from the tag onmain.- Fix the bug. Bump only that platform’s key(s) in
/VERSION. Update the platform’sCHANGELOG.md. Commit aschore(release): {platform} v{X.Y.Z+1}. - PR to
main(merge commit). Trigger the platform’s publish workflow. - Back-merge
main→develop. If the fix also applies todevelop’s next release, the back-merge carries it; otherwise resolve conflicts to preserve the develop-line behaviour.
Cross-platform coordination
Three rules that only make sense once written down:
- Server-first for API changes. Any change to REST or WebSocket contracts ships on the server before the clients that consume it, and the server keeps the old contract until the minimum supported client version has caught up. Mobile update lag (store review + user delay) is real — plan for weeks, not hours.
shared/changes are all-platforms changes. A commit touchingshared/commonMainmust build on every active platform before it merges todevelop. If a platform is mid-release/*, decide whether the shared change is a fix (cherry-pick to the release branch) or a feature (waits for the next cycle).- Concurrent release branches are fine. Two platforms can stabilize in parallel (e.g.
release/server/1.2.0andrelease/android/1.1.0). They only conflict on/VERSION— resolve by keeping each edit scoped to its own keys.
Branch protection & CI
Enforce on both main and develop:
- PR required for all changes; direct pushes blocked.
- CI green required to merge.
- Force-push blocked; branch deletion blocked.
ci.yml runs lint + tests on every PR to develop or main, and on every push to those branches. Publish workflows remain workflow_dispatch and always run against main.
Quick reference
| Branch | Cut from | Merged into | Merge style | Tagged by |
|---|---|---|---|---|
feat/* / fix/* / chore/* / docs/* / refactor/* | develop | develop | squash | — |
release/{platform}/{version} | develop | main → back-merge to develop | merge commit | Publish workflow, on main |
hotfix/{platform}/{version} | tag {platform}/v{X.Y.Z} on main | main → back-merge to develop | merge commit | Publish workflow, on main |
Notes
- iOS follows the same branch flow even though its publish step is still Xcode-manual (no GitHub Actions workflow yet). The
release/ios/{version}branch still exists to house the version bump andVersion.xcconfigregeneration; the “trigger the publish workflow” step becomes “archive from Xcode”. developdidn’t exist before this doc. Bootstrapping:git checkout main && git pull && git checkout -b develop && git push -u origin develop. Enable branch protection on both branches afterwards.