GitGitLab Flow

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

Text
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

Text
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

Bash
# 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

Text
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.2

Applying a fix to release branches

Bash
# 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

Bash
# 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

YAML
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
GitLab Flow naming conventions
GitLab's own documentation uses branch names like `pre-production` and `production` for environment branches, and `2-3-stable` (with hyphens, no dots) for release branches. The hyphen convention avoids issues with some tools that handle dots specially in branch names.
Protect environment branches with approval rules
Set up branch protection on `pre-production` and `production` so that merging into them requires at least one approval, passing CI, and no failed deployments. This turns each promotion into a deliberate, reviewed event rather than an accidental push.