GitLab Flow: The Middle Ground Between GitFlow and GitHub Flow
Environment branches, release branches, or a production branch: how to pick the GitLab Flow variant that matches the way you deploy.
Here's a problem I keep running into. GitHub Flow is lovely, but the moment someone says "we need a staging environment" or "we can't push to production until Thursday," it starts to wobble. GitFlow handles those cases, but it brings so much ceremony that nobody's happy.
GitLab Flow sits in between. It keeps the simplicity of GitHub Flow, adds just enough structure for real-world release constraints, and ties tightly into issue tracking and CI/CD. GitLab introduced it as a response to exactly the pain points above.
Worth knowing up front: GitLab Flow isn't one rigid workflow. It's a family of three patterns that all share the same base: feature branches, merge requests, and main as the integration branch. You pick the variant that matches your deployment reality.
The shared foundation
- Work happens on short-lived feature branches.
- Changes land via merge requests (MRs) into
main. mainis the source of truth, and everything flows downstream from it.- Upstream first: a fix goes into
mainfirst, then gets cherry-picked or merged downstream. Never the reverse. This single rule prevents the "fixed in prod but not in main" mess.
Variant 1: Environment branches
Use this when you deploy to multiple environments and want branches to represent what's running where.
Code only moves one direction: main → staging → production. A commit reaches production only after it's proven itself in the earlier stages.
gitGraph
commit id: "initial"
branch staging
branch production
checkout main
branch feature/invoice
checkout feature/invoice
commit id: "add invoice"
commit id: "tests"
checkout main
merge feature/invoice
checkout staging
merge main
checkout production
merge staging tag: "deployed"
Typical commands:
# Promote main to staging
git checkout staging
git merge --ff-only main
git push origin staging
# After verification, promote to production
git checkout production
git merge --ff-only staging
git push origin production
Using --ff-only guarantees that the environment branch is exactly what you tested, with no surprise merge commits.
Good for: web apps with dev/staging/prod pipelines, or teams that need an explicit promotion step and an audit trail of what went where.
Variant 2: Release branches
Use this when you must support multiple released versions, such as mobile apps, packaged software, or self-hosted products.
Release branches are cut from main when a version is ready. Bug fixes land in main first, then get cherry-picked into the affected release branches:
# fix lands on main via MR first, grab its SHA
git checkout release/1.0
git cherry-pick -x <sha-of-fix-on-main>
git tag -a v1.0.1 -m "Patch release 1.0.1"
git push origin release/1.0 --tags
The -x flag appends "(cherry picked from commit …)" to the message, which is extremely helpful when you're auditing later.
Good for: products where customers run different versions and you backport security and critical fixes.
Variant 3: Production branch (with or without pre-production)
This is the lightest option. You add one extra branch, production, that you merge into whenever you want to release. It's GitHub Flow plus a deliberate deploy gate.
You merge into production on your own schedule: nightly, weekly, after a sign-off, whatever. Tagging commits on production gives you release markers.
Good for: teams that want continuous integration into main but need control over when things go live.
Choosing a variant
What GitLab brings to the party
GitLab Flow was designed alongside GitLab's tooling, so a few things come almost for free:
- Issue-driven branches. Create a branch from an issue and its name gets linked automatically. Merging the MR can close the issue with
Closes #123. - Merge request pipelines. Run CI on the MR source branch, or on the merged result, so you're testing what will actually land.
- Protected branches & environments. Restrict who can push to
productionand who can approve deployments. - Review apps. Spin up a temporary environment per MR for reviewers to click through.
A minimal .gitlab-ci.yml for the environment-branch variant:
stages: [test, deploy]
test:
stage: test
script:
- npm ci
- npm test
deploy_staging:
stage: deploy
script: ./deploy.sh staging
environment: staging
rules:
- if: $CI_COMMIT_BRANCH == "staging"
deploy_production:
stage: deploy
script: ./deploy.sh production
environment: production
when: manual
rules:
- if: $CI_COMMIT_BRANCH == "production"
Note when: manual on production, which gives you a human approval button without any extra branching.
Pros
- Flexible. You choose the amount of structure you need.
- Simpler than GitFlow while still handling environments and versions.
- Upstream-first discipline keeps
mainauthoritative and avoids drift. - Strong CI/CD integration if you're on GitLab.
- Clear promotion path with an auditable record of what's in each environment.
Cons
- "Which variant are we using?" needs an explicit team decision and documentation. Without it, you get a hybrid nobody understands.
- Cherry-picking at scale is tedious and can cause duplicate commits and subtle conflicts if you're not careful.
- Environment branches can drift if someone commits directly to them. Protect them.
- Slower than trunk-based since changes wait to be promoted.
Best practices
- Write down which variant you're using in your
CONTRIBUTING.md, including the promotion steps. - Protect every long-lived branch (
main,staging,production,release/*). - Always fix upstream first. Make it a hard rule in code review.
- Use
--ff-onlymerges for environment promotion so you deploy exactly what was tested. - Use
git cherry-pick -xfor backports so the origin is traceable. - Keep MRs small and short-lived, same as any other flow.
- Squash on merge into
mainto make cherry-picking a single commit instead of a series. - Delete merged feature branches, but obviously keep the environment and release ones.
Wrapping up
GitLab Flow is the "grown-up GitHub Flow." If your team has outgrown a single main branch but doesn't need the full GitFlow machinery, it's probably where you'll end up. Pick the variant that matches how you really deploy, write it down, and automate the promotion steps.
If you enjoyed this, consider supporting me with a coffee: buymeacoffee.com/vatul16 ☕
Thanks for reading!
About the author
- Hi, I'm Atul! I am a software engineer with a strong focus on DevOps, Cloud Infrastructure, and automation. I spend my time building scalable CI/CD pipelines, mastering tools like Docker, Terraform, and AWS, and breaking down complex cloud concepts into accessible, hands-on tutorials. I believe the best way to learn is to build, break, and document the process.
Comments
Join the discussionNo comments yet. Be the first to comment.
Git Branching Strategies: A Practical Guide for Teams
A five-part, hands-on series on Git branching strategies. We go through GitFlow, GitHub Flow, GitLab Flow and Trunk-Based Development, with diagrams, real commands, pipeline examples, pros, cons and best practices. The final post helps you decide which strategy fits your team, release style and level of automation.



