# Android Cast — Git flow (green `master`) **Version:** 1.0 · **Last updated:** 2026-05-21 **PDF:** regenerate with `python3 docs/_pdf_build/build_git_flow_pdf.py` → `docs/GIT_FLOW.pdf` This document defines how we develop, integrate, release, and hotfix. **Cursor agents and humans follow the same rules.** --- ## 1. Goals | Goal | How | |------|-----| | **Always shippable `master`** | Only merges that passed CI/tests; tagged releases point here | | **Safe integration** | Day-to-day work lands on `next` first | | **Simple mental model** | Two long-lived branches + short-lived topic branches | | **Recoverable hotfixes** | Production fixes branch from what users actually run | --- ## 2. Branches | Branch | Role | Direct commits? | |--------|------|-----------------| | **`master`** | Production line; **green** (build + tests pass); release tags (`v0.1.2`) | **No** — merge only | | **`next`** | Integration / release candidate; absorbs features until a release is approved | **No** — merge only | | **`feature/*`**, **`fix/*`**, **`topic/*`** | Short-lived work; fork **`next`** | Yes (normal dev) | | **`hotfix/*`** | Urgent fix for **released** code | Yes; merge target **`master`** first | **Naming:** `feature/cast-resolution-presets`, `fix/crash-upload-500`, `hotfix/v0.1.1-pin-crash`. If `next` does not exist yet on the remote: ```bash git checkout master git pull git checkout -b next git push -u origin next ``` --- ## 3. Branch diagram (steady state) ```mermaid flowchart LR subgraph long["Long-lived"] M[master\nshippable + tagged] N[next\nintegration] end F[feature/*\nfix/*] H[hotfix/*] F -->|PR / merge after CI| N N -->|release merge after CI| M H -->|merge after CI| M M -->|sync after release or hotfix| N ``` --- ## 4. Daily development 1. **Start from `next`:** `git fetch && git checkout next && git pull` 2. **Create topic branch:** `git checkout -b feature/my-change` 3. **Commit** in small logical chunks on the topic branch. 4. **Before merge:** run project checks (Android: `./gradlew :app:testDebugUnitTest` or CI equivalent; backend: relevant tests/lint). 5. **Merge to `next`:** prefer merge commit or squash per team habit; **do not** merge unfinished work. 6. **Delete** topic branch after merge (local + remote). ```mermaid sequenceDiagram participant Dev as Developer / agent participant F as feature/* participant N as next participant CI as CI / local tests Dev->>F: branch from next Dev->>F: commits Dev->>CI: test CI-->>Dev: green Dev->>N: merge feature → next Dev->>F: delete branch ``` --- ## 5. Release: `next` → `master` **When:** `next` is feature-complete for the version, soak/QA agreed, CI green. 1. Ensure **`next`** is up to date and **all tests pass**. 2. **Merge `next` → `master`** (merge commit recommended for traceability). 3. On **`master`:** final smoke / CI. 4. **Tag** on `master`: `git tag -a v0.1.2 -m "Release v0.1.2"` then `git push origin master --tags`. 5. **Sync `next` with `master`** so both lines share the release commit and any last-minute release fixes: ```bash git checkout next git merge master # fast-forward or merge commit git push origin next ``` 6. Continue new work: branch **`feature/*` from `next`** again. ```mermaid sequenceDiagram participant N as next participant M as master participant T as tag v0.1.x Note over N: CI green, QA ok N->>M: merge next → master M->>M: CI / smoke M->>T: annotated tag M->>N: merge master → next (sync) ``` **Versioning:** tags on `master` only (`vMAJOR.MINOR.PATCH`). Bump per [SemVer](https://semver.org/) conventions you adopt for the app. --- ## 6. Hotfixes — chosen strategy ### Default: **hotfix from `master` (option 1)** ✓ Use when the bug affects **what is already released** (code on `master` / latest tag). 1. `git checkout master && git pull` 2. `git checkout -b hotfix/v0.1.2-crash-on-stop` 3. Fix + tests 4. Merge **`hotfix/*` → `master`**, CI green 5. **Tag** patch on `master`: e.g. `v0.1.3` 6. **`git checkout next && git merge master`** — bring hotfix into integration (resolve conflicts if `next` diverged) 7. Push `master`, `next`, tags; delete `hotfix/*` **Why not hotfix only on `next` (option 2)?** `next` often contains unreleased features. Fixing there delays shipping the patch and risks dragging unrelated commits into production when you later merge `next` → `master`. ### Exception: fix only on `next` (option 2) Use when the bug **does not exist on `master`** (introduced on `next` only). 1. `git checkout -b fix/...` from **`next`** 2. Merge to **`next`** only 3. No tag until the normal **release** merge `next` → `master` ```mermaid flowchart TD Q{BUG IN RELEASED\nmaster / tag?} Q -->|Yes| H[hotfix/* from master] H --> M[merge → master] M --> T[tag patch on master] T --> S[merge master → next] Q -->|No, next only| F[fix/* from next] F --> N[merge → next] ``` --- ## 7. Protection rules (CI + policy) | Rule | `master` | `next` | topic branches | |------|----------|--------|----------------| | Required CI (build + tests) before merge | ✓ | ✓ | ✓ (before PR/merge) | | No direct pushes of WIP | ✓ | ✓ | — | | Only maintainers merge to `master` | ✓ | optional | — | | Tag only on `master` | ✓ | — | — | See **[§7.1 How to protect branches](#71-how-to-protect-branches-not-local-hooks-alone)** below. ### 7.1 How to protect branches (not local hooks alone) **Short answer:** use **server-side** rules on `f0xx.org` (or whatever hosts `origin`). **Local git hooks** on your laptop are optional helpers only — they do not stop you or someone else from pushing bad history from another machine. | Layer | Where it runs | Stops bad pushes? | Good for | |-------|----------------|-------------------|----------| | **Hosting UI / branch protection** | Git server (GitHub, GitLab, Gitea, …) | **Yes** | Most teams; use this if available | | **Server git hooks** (`pre-receive` / `update`) | Bare repo on server | **Yes** | Self-hosted `git://` / SSH without a web UI | | **Local hooks** (`.git/hooks/pre-push`) | Your PC only | **No** (easy to skip) | Personal reminders, format/tests before push | | **CI** (build on push/PR) | CI runner | Indirect | Prove tests passed; pair with “required check” on server | #### What to enable on `master` and `next` 1. **No force-push** (no `git push --force` rewriting history). 2. **No direct push** (optional but strong): changes only via merge from `feature/*` or from PR/MR — you merge locally or on the web, but the server rejects `git push origin master` with a normal commit on top without review (exact option name varies). 3. **Require CI green** before merge (if you have Actions/GitLab CI/Gitea Actions). 4. **Restrict who can push** to you (and CI deploy key if needed). `feature/*` branches stay **unprotected** — push freely. #### If your host has a web UI (Gitea / GitLab / GitHub) Look for **Branch protection** / **Protected branches**: - Add rules for `master` and `next`. - Enable: prevent force-push, require pull request (optional), require status checks. No git internals required — the server rejects illegal pushes. #### If you only have bare git on a server (`git://` / SSH) Ask whoever administers `git://f0xx.org/android_cast` to install **server-side hooks** in the **bare repository** (not in your clone): - `hooks/pre-receive` or `hooks/update` — script receives branch name + old/new SHA; **exit 1** to reject (e.g. block direct push to `refs/heads/master` unless user is deploy; block all force-push to `master`/`next`). Example policy in words: “reject push if ref is `master` or `next` and user is not in allowlist” or “reject non-fast-forward updates to `master`/`next`”. Local clone hooks live in `.git/hooks/` and are **not copied** when others clone — so they are **not** team protection. #### Local hooks (optional, for you only) Useful on your machine, not security: ```bash # .git/hooks/pre-push (chmod +x) — example: warn before pushing to master protected_regex='^(refs/heads/)?(master|next)$' while read local_ref local_sha remote_ref remote_sha; do if echo "$remote_ref" | grep -qE "$protected_regex"; then echo "Blocked: push to master/next — use feature branch + merge (see docs/GIT_FLOW.md)" exit 1 fi done ``` Skip with `git push --no-verify` — another reason server rules matter. #### CI without branch UI Run tests on every push; you still **choose** not to merge until green. Branch protection + “required check” automates that on modern hosts. --- ## 8. Sync cheat sheet | Event | Action | |-------|--------| | After **release** (`next` → `master` + tag) | `git checkout next && git merge master && git push` | | After **hotfix** to `master` + tag | Same: **merge `master` → `next`** | | Feature finished | merge **topic → `next`** only | | Started wrong base | rebase topic onto latest `next` (coordinate if already shared) | --- ## 9. Agent / Cursor checklist Agents working in this repo **must**: 1. **Never** commit or push directly to `master` or `next` unless the user explicitly asks for that merge/release step. 2. **Branch from `next`** for features and non-production fixes (`feature/*`, `fix/*`). 3. **Run tests** (or state clearly if not run) before recommending merge to `next`. 4. **Releases:** only describe merging `next` → `master` and tagging after user confirms QA. 5. **Hotfixes on released code:** branch from `master`, merge back to `master`, tag, then **merge `master` into `next`**. 6. **Set active branch** metadata when using a named topic branch (project convention). 7. **Do not** force-push `master` / `next`. --- ## 10. Comparison to classic Git Flow | Git Flow | This repo | |----------|-----------| | `main` | `master` (green, tagged) | | `develop` | `next` | | `feature/*` | same, base = `next` | | `release/*` | optional short branch; default = test on `next` then merge to `master` | | `hotfix/*` | from **`master`**, merge to **`master`**, sync to **`next`** | We intentionally avoid a permanently divergent `develop` without a green production branch: **`master` is always releasable.** --- ## 11. First-time setup (maintainer) ```bash # Ensure next exists and tracks origin git checkout master && git pull git checkout -b next 2>/dev/null || git checkout next git push -u origin next # Optional: protect branches on hosting (GitHub example) # gh api repos/{owner}/{repo}/branches/master/protection ... ``` --- ## 12. Private primary + GitHub publish (dual remote) **Canonical repo:** your server — today `origin` → `git://f0xx.org/android_cast`. **Daily work:** always `git push` / `git pull` against **`origin`** first. **GitHub:** secondary remote for visibility, issues, and (optional) GitHub Actions — not the source of truth unless you decide otherwise. ### 12.1 Add GitHub without replacing origin ```bash # Create empty repo on GitHub (no README if you already have history) git remote add github git@github.com:YOUR_USER/android-cast.git # origin stays: git://f0xx.org/android_cast git remote -v ``` Keep **`origin`** = private server. Name the public remote **`github`** (or `publish`) so commands stay obvious. ### 12.2 What to push where | Ref | Private `origin` | GitHub `github` | |-----|------------------|-----------------| | `feature/*`, WIP | ✓ always | optional (omit for cleaner public) | | `next` | ✓ always | your choice: public integration or keep private | | `master` + tags | ✓ always | ✓ publish releases you want visible | | Secrets, `local.properties`, logs | never in either | never | **Typical OSS-style:** push everything to **`origin`**; push only **`master`** and **tags** to **`github`** until you are ready to open `next`. ```bash # After merge to master on private server git push origin master --tags git push github master --tags ``` Push `next` to GitHub only when you want public integration: ```bash git push origin next git push github next ``` ### 12.3 Daily habit ```bash git fetch origin git checkout next && git pull origin next # ... work on feature/foo ... git push -u origin feature/foo # merge to next on origin (PR or local merge + push) git push origin next # When ready to publish a release line to GitHub git push github master --tags ``` Set upstream on topic branches to **origin**, not github: ```bash git push -u origin feature/my-change ``` ### 12.4 Where branch protection lives | Server | Protect | |--------|---------| | **f0xx.org (`origin`)** | `master`, `next` — server hooks or admin UI (primary enforcement) | | **GitHub (`github`)** | `master` (and `next` if public) — Settings → Branch protection | GitHub rules only affect pushes **to GitHub**. They do not protect your private server unless you also configure **`origin`**. ### 12.5 One-way mirror (optional automation) On the private server (cron or post-receive hook), after a successful push to `origin`: ```bash git push --prune github +refs/heads/master:refs/heads/master +refs/tags/*:refs/tags/* ``` Or use GitHub’s “mirror repository” importers only for initial import; ongoing sync is usually `git push github …` from CI or hook. Avoid `git push github --mirror` unless GitHub is a full duplicate of **private** history — it copies all branches and deletes remote branches you deleted locally. ### 12.6 Suggested order of operations (your situation) 1. On **f0xx.org:** create **`next`** from `master`, push `origin next`. 2. On **f0xx.org:** enable server protection for `master` / `next` (admin). 3. Create **GitHub** repo; `git remote add github …`. 4. First publish: `git push github master` and tags you want public. 5. Enable **GitHub branch protection** on `master` (block force-push). 6. Continue feature flow on **`origin`** only; publish to GitHub when you merge/tag on `master`. --- ## Related - [ROADMAP.md](ROADMAP.md) — product tracks - [.cursor/rules/git-flow.mdc](../.cursor/rules/git-flow.mdc) — agent enforcement - `docs/_pdf_build/build_git_flow_pdf.py` — PDF generator