Branching & Workflow¶
This project uses a Gitflow branching model. All development flows through develop (staging) before reaching main (production).
Branch Overview¶
gitGraph
commit id: "v0.1.0"
branch develop
checkout develop
commit id: "initial develop"
branch feature/auth
commit id: "add login"
commit id: "add refresh"
checkout develop
merge feature/auth
branch feature/crm-leads
commit id: "lead model"
checkout develop
merge feature/crm-leads
checkout main
merge develop tag: "v0.2.0"
Branches¶
| Branch | Purpose | Deploys To | Protected |
|---|---|---|---|
main |
Production-ready code | Production (server.dannyhaslund.dk) | Yes |
develop |
Integration branch for next release | Staging (test-server.dannyhaslund.dk) | Yes |
feature/<name> |
New features | PR environment (ephemeral) | No |
fix/<name> |
Bug fixes | PR environment (ephemeral) | No |
hotfix/<name> |
Urgent production fixes | PR environment (ephemeral) | No |
release/<version> |
Release preparation | — | No |
Day-to-Day Development¶
1. Start a feature or fix¶
Always branch from develop:
2. Work on your branch¶
Make commits following the commit rules:
Push regularly:
3. Open a Pull Request¶
- Source: your feature branch (e.g.,
feature/crm-lead-scoring) - Target:
develop
When you open the PR:
- CI runs lint + test automatically
- If the PR touches
server/, an ephemeral environment spins up onhttp://192.168.1.6:<port>and posts the URL as a comment - Get code review from a team member
If Gitea shows "This branch is out-of-date": Click Update branch by merge to bring your feature branch up to date with develop.
4. Merge to develop¶
After approval and passing CI, merge the PR into develop.
Use the correct merge strategy — this matters for keeping history clean:
| Merge strategy | When to use |
|---|---|
| Squash commit | Feature/fix branches → develop (most common — collapses all branch commits into one clean commit) |
| Merge commit | develop → main (preserves the full history of what was in the release) |
| Rebase + fast-forward | Only for trivial single-commit branches |
Why squash for features?
A feature branch often has intermediate commits like "fix typo", "address review feedback", "WIP". Squashing collapses these into a single commit with a clean conventional commit message, so develop's history reads as a clean list of completed features and fixes — which is exactly what git-cliff needs to generate good changelogs.
When squash-merging, make sure the commit message follows conventional commits:
This automatically deploys to the staging server (test-server.dannyhaslund.dk).
Verify your changes work on staging before proceeding.
5. Delete your feature branch¶
After merging, delete the feature branch (Gitea can do this automatically on merge).
Releasing to Production¶
When develop is stable and ready for release:
Option A: Direct merge (simple)¶
For straightforward releases:
This triggers an automatic deploy to production.
Option B: Release branch (for larger releases)¶
When you need a stabilization period:
# Create a release branch from develop
git checkout develop
git pull origin develop
git checkout -b release/v1.2.0
# Only bug fixes go here — no new features
git commit -m "fix(server): correct pagination offset"
# When ready, merge into BOTH main and develop
git checkout main
git merge release/v1.2.0
git push origin main
git checkout develop
git merge release/v1.2.0
git push origin develop
# Delete the release branch
git branch -d release/v1.2.0
git push origin --delete release/v1.2.0
Tag the release¶
After merging to main:
This triggers the release.yml workflow which generates a changelog with git-cliff and creates a Gitea release.
Hotfixes¶
For urgent production bugs that can't wait for the normal flow:
# Branch from main (not develop)
git checkout main
git pull origin main
git checkout -b hotfix/fix-auth-crash
# Fix the issue
git commit -m "fix(auth): prevent crash on expired token rotation"
# Open PR targeting main
# After merge, also merge main back into develop:
git checkout develop
git merge main
git push origin develop
Flowchart¶
flowchart TD
START([Start work]) --> BRANCH[Create branch from develop]
BRANCH --> WORK[Write code + commit]
WORK --> PUSH[Push to remote]
PUSH --> PR[Open PR → develop]
PR --> CI{CI passes?}
CI -->|No| WORK
CI -->|Yes| REVIEW{Code review approved?}
REVIEW -->|No| WORK
REVIEW -->|Yes| MERGE_DEV[Merge to develop]
MERGE_DEV --> STAGING[Auto-deploy to staging]
STAGING --> QA{Staging looks good?}
QA -->|No| WORK
QA -->|Yes| READY{Ready for release?}
READY -->|Not yet| START
READY -->|Yes| MERGE_MAIN[Merge develop → main]
MERGE_MAIN --> PROD[Auto-deploy to production]
PROD --> TAG[Tag version]
TAG --> RELEASE[git-cliff generates release notes]
Quick Reference¶
| I want to... | Branch from | PR target | Deploy trigger |
|---|---|---|---|
| Build a new feature | develop |
develop |
Merge → staging |
| Fix a non-urgent bug | develop |
develop |
Merge → staging |
| Fix an urgent production bug | main |
main |
Merge → production |
| Release to production | — | Merge develop → main |
Push → production |
| Create a versioned release | — | Tag v* on main |
Tag → release workflow |
Rules¶
- Never push directly to
mainordevelop— always use pull requests. - Feature branches are short-lived — merge or close within a few days.
- Delete branches after merging — keep the repo clean.
- Hotfixes get merged back to
develop— so fixes aren't lost. - Every commit follows conventional commits — git-cliff depends on it.