GitLab Flow
GitLab Flow is a pragmatic middle ground between the simplicity of GitHub Flow and the rigour of Git Flow. It was developed by GitLab to solve a specific gap: GitHub Flow is excellent for continuous deployment, but it does not naturally support teams that deploy to multiple environments (staging, pre-production, production) or maintain multiple versioned releases. GitLab Flow adds environment branches and release branches on top of the GitHub Flow model without adding Git Flow's full complexity.
The upstream-first principle
GitLab Flow's core rule is upstream first: code always flows in one direction — from feature branches into main, and from main into environment branches (never the other way). Fixes are always made on main first (or a feature branch that merges to main), then cherry-picked or merged downstream into environment branches. This prevents code from existing only in production without also being in main.
The upstream-first rule
Flow direction (always upstream → downstream): feature/* ──▶ main ──▶ pre-production ──▶ production NEVER: production ──▶ main (forbidden) pre-production ──▶ feature/* (forbidden) Hotfixes: hotfix/* ──▶ main ──▶ pre-production ──▶ production (fix goes to main first, then downstream)
Environment branches
The most distinctive feature of GitLab Flow is using Git branches to represent deployment environments. Each environment branch automatically reflects what is currently deployed to that environment.
Environment promotion diagram
main: A ── B ── C ── D ── E ── F ──▶
↓ ↓
pre-production: ─────────── C ──────────── F ──▶
↓
production: ─────────────────────────── F ──▶
Timeline:
- Developers merge features to main continuously.
- When QA approves, main is merged/promoted to pre-production.
- After staging tests pass, pre-production is merged to production.
- Each merge represents a deliberate promotion decision.Promoting code through environments
# Feature lands on main via PR (same as GitHub Flow) git switch main git pull origin main # When team decides to deploy to pre-production: git switch pre-production git merge main # or: git merge --no-ff main git push origin pre-production # (CI/CD detects push to pre-production and deploys automatically) # After testing in pre-production, promote to production: git switch production git merge pre-production git push origin production # (CI/CD deploys to production automatically)
Release branches for versioned software
For teams that maintain multiple supported versions (e.g., a library with active v1.x and v2.x release lines), GitLab Flow adds stable release branches. These are separate from environment branches and follow the same upstream-first principle.
Release branches in GitLab Flow
main: A ── B ── C ── D ── E ── F ──▶ (active development)
↓ ↓
release/2-3-stable: ─ 2.3.0 ─ 2.3.1 ──▶ (v2.3.x maintenance)
release/2-2-stable: ─ 2.2.0 ─ 2.2.1 ── 2.2.2 ──▶ (v2.2.x maintenance)
Patch fixes:
1. Fix on main first (upstream first)
2. Cherry-pick to relevant release branches
3. Tag each release: v2.3.1, v2.2.2Applying a fix to release branches
# 1. Fix on main first git switch main git switch -c fix/session-timeout git commit -m "fix: extend session timeout to 30 minutes" git push -u origin fix/session-timeout # (merge PR to main) # 2. Find the fix commit SHA git log --oneline main -3 # abc1234 fix: extend session timeout to 30 minutes ← this one # def5678 feat: add user preferences page # ghi9012 chore: update dependencies # 3. Cherry-pick onto release branches git switch release/2-3-stable git cherry-pick abc1234 git push origin release/2-3-stable git switch release/2-2-stable git cherry-pick abc1234 git push origin release/2-2-stable # 4. Tag the patch releases git switch release/2-3-stable git tag -a v2.3.1 -m "Patch release 2.3.1: session timeout fix" git push origin v2.3.1
GitLab Flow with merge requests
In GitLab Flow, all code changes happen through Merge Requests (GitLab's term for Pull Requests). Environment promotions can also be done through MRs, giving you a review and approval gate before each promotion.
Create a promotion MR from main to pre-production
# Using GitLab CLI (glab) glab mr create --source-branch main --target-branch pre-production --title "Deploy main to pre-production (2024-03-15)" --description "Includes features: dark mode, user avatars, search v2" # After review and approval, merge the MR in the GitLab UI. # GitLab CI/CD then automatically deploys pre-production.
GitLab Flow vs GitHub Flow vs Git Flow
Dimension | GitHub Flow | GitLab Flow | Git Flow |
|---|---|---|---|
Long-lived branches | main | main + env branches | main + develop |
Environment tracking | No (branch = env implicit) | Yes (explicit env branches) | No |
Multi-version support | No | Yes (release branches) | Yes (release/* + hotfix/*) |
Deployment trigger | Branch deploy before merge | Push to env branch | Merge to main |
Hotfix process | PR to main | PR to main, then downstream | hotfix/* → main + develop |
Complexity | Low | Medium | High |
Best for | Continuous delivery, SaaS | Multiple environments or versions | Scheduled versioned releases |
When to choose GitLab Flow
Multiple deployment environments — when you need staging, UAT, and production to be clearly tracked in Git.
Compliance and audit requirements — environment branches create a clear, verifiable record of what was deployed and when.
Partially continuous delivery — you deploy frequently but not on every commit (e.g., after QA sign-off).
Versioned products with active maintenance — when you need to maintain v2.x while shipping v3.x features.
Teams already on GitLab — the workflow integrates naturally with GitLab's CI/CD environment configuration.
Teams that find GitHub Flow too loose but Git Flow too heavy — GitLab Flow adds structure without the full Git Flow ceremony.
Setting up environment protection in GitLab CI
.gitlab-ci.yml: environment branch deployments
stages:
- test
- deploy
test:
stage: test
script:
- npm ci
- npm test
deploy-staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging
url: https://staging.example.com
only:
- pre-production
deploy-production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
url: https://example.com
only:
- production
when: manual # Require manual approval for production deploys