In GitLab Flow, what are environment branches and how do changes reach production?
answer
- Branches named after where code runs
- Ask which direction changes are allowed to travel
- The tip is what that environment runs
- Fix upstream, then bring it down
- Cherry-pick records its origin with a flag
basics
~20 sEnvironment branches are long-lived Git branches named for deploy targets, such as staging and production, whose tips represent what each environment runs. Changes always land on main first and are then merged downstream, never committed directly to an environment branch.
solid answer
~40 sGitLab Flow keeps `main` as the single integration branch and adds long-lived branches named after deploy targets — typically `staging` and `production`. A branch's tip is what that environment runs, so promotion is a Git merge rather than a separate release artifact. The rule that makes it work is directional: changes enter `main` first and then flow downstream, `main` → `staging` → `production`, and nothing is ever committed directly to an environment branch. That guarantees every change running downstream is already integrated upstream. Urgent fixes follow the same rule, "upstream first": commit the fix on `main`, then bring just that commit downstream with `git cherry-pick -x`. Skipping the upstream step leaves a fix that exists only in production and disappears at the next promotion.
code
bash · 9 lines# promote everything integrated on main
git switch staging
git merge --no-ff main
git switch production
git merge --no-ff staging
# what is merged but not yet promoted?
git rev-list --count production..maingo deeper
Recall that GitLab Flow keeps main plus long-lived branches named for environments, and that changes move downstream from main rather than starting on an environment branch.
Explain the invariant the downstream-only rule buys and how upstream-first cherry-picking preserves it during an incident, including why -x matters.
Be ready to judge whether environment branches are earning their keep in a given repository, and to describe how the invariant degrades once someone commits directly downstream.
Own the choice between the environment-branch variant and the per-version release-branch variant, based on whether users run your deployment or their own copy.
## Where GitLab Flow starts GitLab Flow keeps the single-integration-branch idea — `main` is where work is merged — and adds long-lived branches that model *where code is deployed*, together with a rule about which direction changes travel. It is best understood as a middle position: less ceremony than Git Flow's develop/release/hotfix set, more structure than a lone `main`. ## What an environment branch is An environment branch is an ordinary long-lived Git branch whose name matches a deploy target: `staging`, `production`, sometimes `pre-production`. The names are conventions; Git attaches no meaning to them. What gives them meaning is a single invariant the team enforces: **the tip of the branch is what that environment is running**. That makes "what is in production right now" a question you answer with Git rather than with a deployment dashboard: `git log production`, or `git log production..main` for everything merged but not yet promoted. ## The downstream-only rule Changes enter at the top and move down. A unit of work is merged into `main`. Promoting to staging is `git merge --no-ff main` while on `staging`; promoting to production is the same merge from `staging`. Nobody commits to `staging` or `production` directly. The payoff is an invariant worth stating out loud in an interview: after a promotion, the promoted commit from `main` is an ancestor of the environment branch's tip, so nothing can be running downstream that has not already been integrated upstream. The moment someone commits straight onto `production`, that invariant is gone and no amount of tooling restores it. The branches are also naturally ordered by how far behind they are. `git rev-list --count production..main` is a live count of merged-but-unpromoted work. ## Upstream first, for urgent fixes An incident tempts you to commit the fix on `production` because that is where the problem is. GitLab Flow forbids this and calls the alternative "upstream first": make the fix on `main` (directly, or on a branch merged into `main`), then bring that one commit downstream with `git cherry-pick -x <sha>` onto `production`. The reasoning is purely about history. A commit made only on `production` exists nowhere else, so the next promotion merge brings down a `main` that never had it — and if someone later touches the same lines upstream, the merge either conflicts confusingly or quietly reverts the emergency fix. Fixing upstream first means the change is present on every branch that will ever be promoted, and the eventual promotion merge sees matching content and resolves cleanly even though the cherry-picked commit has a different hash. The `-x` flag matters here: it appends a line to the commit message recording which commit was cherry-picked, so the duplicate is traceable rather than mysterious. ## The release-branch variant GitLab Flow also describes a second shape for software that is versioned and shipped rather than deployed: instead of environment branches, keep a long-lived branch per released minor version, and apply the same upstream-first rule — the fix lands on `main` and is cherry-picked onto each supported version branch. The two variants are alternatives, chosen by whether your users run your deployment or their own copy. ## What Git is actually doing Nothing exotic. Environment branches are branches, promotion is `git merge`, upstream-first patching is `git cherry-pick`. Everything in the model is a convention the team enforces socially or with server-side checks; Git has no concept of an environment. That is precisely why the discipline matters: the invariant holds only as long as everyone keeps commits off the environment branches. ## When environment branches earn their place They pay off when promotion is a gated, deliberate step — an environment that deliberately lags, a manual sign-off, a deploy window — because then "what is on staging" is genuinely different information from "what is on main", and encoding it as a branch makes it queryable. They cost more than they return when every merge to `main` goes straight to production. In that case `main`, `staging` and `production` all point at nearly the same commit, and the branches are two extra merges that tell you nothing you could not read from `main` and a tag. ## The common failure mode A team adopts environment branches, then hotfixes directly on `production` during an incident "just this once". Weeks later the fix is gone, no one can say when it disappeared, and `git log production` no longer means what the team thinks it means. The model's value is entirely in the invariant; the invariant is entirely in the discipline.
- Why does upstream-first patching use cherry-pick rather than merging the production branch back into main?Merging the environment branch upstream would drag its whole promotion history — merge commits and all — into `main`, inverting the direction the model depends on. A cherry-pick copies exactly the one fix. Applying it upstream first and then downstream keeps every environment branch strictly a promoted subset of `main`.
- How would you tell, from Git alone, how far staging lags behind main?`git rev-list --count staging..main` gives the number of commits merged but not yet promoted, and `git log --oneline staging..main` lists them. Because the environment branch tip is by convention what is deployed, that range is a literal answer to "what is waiting to go out".
- When are environment branches not worth having?When every merge to `main` is deployed immediately. Then `main`, `staging` and `production` sit within a commit or two of each other, and the promotion merges encode no information you could not read from `main` plus a tag. Environment branches earn their keep only where promotion is genuinely a gated, lagging step.
saying these in an interview costs you the question
- Says you commit hotfixes straight onto the production branch
- Thinks environment branches are a Git feature with special semantics
- Claims changes may be merged upstream from production to main
- Confuses environment branches with Git Flow's release branches
- Believes the branch name alone triggers a deployment