Skip to content

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:

git checkout develop
git pull origin develop
git checkout -b feature/crm-lead-scoring

2. Work on your branch

Make commits following the commit rules:

git add .
git commit -m "feat(crm): add lead scoring calculation"

Push regularly:

git push -u origin feature/crm-lead-scoring

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 on http://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 developmain (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:

feat(crm): add lead scoring calculation

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:

git checkout main
git pull origin main
git merge develop
git push origin main

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:

git checkout main
git tag -a v1.2.0 -m "v1.2.0"
git push origin v1.2.0

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 developmain Push → production
Create a versioned release Tag v* on main Tag → release workflow

Rules

  1. Never push directly to main or develop — always use pull requests.
  2. Feature branches are short-lived — merge or close within a few days.
  3. Delete branches after merging — keep the repo clean.
  4. Hotfixes get merged back to develop — so fixes aren't lost.
  5. Every commit follows conventional commits — git-cliff depends on it.