May 17th, 2021

Using GitLab Flow with

I've been using the GitLab Flow paradigm for managing branches and environments of projects. At a minimum, this involves setting up a main (historically master, see below) branch as the default for the repository, and a separate production branch. If CI/CD processes require it, a third pre-production branch could be inserted in between. These branches should be protected -- preventing deletion and any direct pushes (i.e., all commits must be done via a merge request).

Preparing a new project to use GitLab Flow

First use the "move" command in git to copy the entire master branch to a new main branch, and push to the upstream repository.

git branch -m master main
git push -u origin main

Then make main the default branch in GitLab. To do so, go to "Settings" > "Repository" for your project. At the top is a section marked "Default Branch" -- expand it to select "main" instead of "master". Then click "Save changes".

Next, remove the master branch. Go to "Repository" > "Branches" for your project. Under the "Active Branches" find "master" and click the trashcan icon to the right to delete the branch (say "Ok" to the warning about not being able to undo the delete).

Create a new production branch that will automatically be deployed to the live site (i.e., trigger a build) when changes are made to it.

git checkout -b production

And, lastly, protect both main and production branches, allowing "Maintainers" to merge to either but disallowing all users from pushing directly.

Configuring branches and environments in

First, configure your project repository's integration with, making sure to include the following features:

  • Fetch branches from the GitLab repository to your project.
  • Remove branches from your project that have disappeared from GitLab.
  • Re-sync environment data on every pull request build.

Once the integration is working, you will see two new inactive environments off of the "Master" environment in one that mirrors the main git branch and another mirroring production. The remaining steps require that you are running a local copy of the project (see Lando). Once you have a local webserver running and have downloaded and installed the CLI, you can proceed (steps extracted from the documentation).

First, remove the parent association from the "production" environment and make it top-level.

platform environment:info -p <Project ID> -e production parent -

Re-link "main" to use "production" as its parent.

platform environment:info -p <Project ID> -e main parent production

Reconfigure the default branch by deactivating the "master" environment, setting the project's default_branch property, and then deleting the "master" environment entirely.

platform environment:delete master --no-delete-branch -p <Project ID>
platform project:info default_branch production -p <Project ID>
platform environment:delete master --delete-branch -p <Project ID>

Following the GitLab Flow paradigm

As the main and production branches are protected, all development should occur on additional feature branches from main. Once the development on a particular feature branch is complete, its changes should first be rebased onto the main branch to pick up any other changes that have been introduced since the feature branch was created (or last rebased).

Next, create a merge request to merge the feature branch into main. Ensure that "Squash commits when merge request is accepted" is checked off (preferably set as the default/required setting in the project's repository settings). Once code review is complete and the reviewer(s) "accept" the merge request, you can choose to complete the merge (note that this may have to be done in the GitLab UI if pushing directly to main is not allowed -- see above).

At this point, GitLab should trigger a new build on the "main" environment in Review/test the changes just incorporated on this newly built environment and, when ready, submit another merge request -- this time to merge main into production. Once that merge request is approved and the merge is complete the new changes will be deployed to the "production" environment and will be visible live.

Semi-linear structure

The recommendation is to use a "semi-linear" branching structure, which can be selected in the project's repository settings (go to "Settings" > "Repository"). This means that feature branch should be rebased onto main just prior to submitting a merge request -- and when merging, the feature branch's commits should be "squashed" into a single merge commit when incorporated back into main. Subsequently, any deployments to the "production" environment will be made by a single merge commit from main to production (as detailed above).

This approach will ensure that the git history remains clean and understandable, while capturing the commit history of a particular feature's development as set by the author. For more information, see "Git Interactive Rebase, Squash, Amend and Other Ways of Rewriting History" by Tute Costa.

Annotated example


(Note that these annotations do not correspond to the visual order in the image.)

  • 40d1854: Project initialized with main and production branches
  • de337be: Code update pushed directly to both branches (should be avoided!)
  • b8e510c: Feature branch created, commits made, rebased onto main and then merged into main -- triggering a deployment to the "main" environment
  • 2d32233: main rebased onto production and then merged -- triggering a deployment to the "production" environment
  • a32f775: At some point prior to the deployment, a feature branch called site-aliases is created and a commit is made.
  • 4734625: Now that main (from which the site-aliases branch had been created) is ahead of site-aliases, that feature branch is rebased onto main -- which applies its commits to the head of main, and leaves site-aliases as only a local branch (to be deleted).
  • 0bd846e: main is merged into production -- triggering a deployment to the "production" environment again.