From my blog, ryougifujino.com.

The original address

Git allows a wide range of branching policies and workflows. As a result, many organizations use workflows that are complex, not clearly defined, and not well integrated with issue tracking systems. So, we present the GitLab Flow as a well-defined set of best practices. It combines feature-driven development with feature branches with problem tracking.

Organizations that move to Git from another version control system often find it difficult to develop an efficient workflow. This article describes GitLab Flow, which integrates a Git workflow with a problem tracking system. It provides a way to use Git transparently and efficiently.

When moving to Git, you must get used to sharing the fact that a commit takes three steps with your colleagues. Most version control systems have only one step: commit the working copy to a shared server. To use Git, you need to add files from your working copy to the staging area and then commit them to your local repository. The third step is to push to a shared remote repository. After getting used to these three steps, the next challenge is the branching model.

Because many organizations new to Git have no conventions on how to use it, their repositories can quickly become chaotic. The biggest problem is that there are many long-running branches, all of which contain partial changes. It can be difficult to figure out which branch has the latest code, or which branch should be deployed into production. Often, the solution to this problem is to adopt a standardized pattern, such as Git Flow and GitHub Flow. We believe that there is still room for improvement. In this document, we describe a set of practices that we call GitLab Flow.

For a video on how this all works in GitLab, see GitLab Flow.

Git Flow and its problems

Git Flow was one of the first proposals to use Git branches, and it has received a lot of attention. It recommends using a master branch and a separate Develop branch, as well as auxiliary branches for features, releases, and hotfixes. Development is done on the Develop branch, then moved to the Publish branch, and finally merged into the Master branch.

Git Flow is a well-defined standard, but its complexity introduces two problems. One is that developers must use the Develop branch instead of the Master branch. The master branch is used to hold code that is released to production. By convention, you call your default branch master, and most of your branches come from it and merge back into it. And because most tools automatically use master as the default branch, having to switch to another branch is annoying.

The second problem with Git flow is the complexity caused by hotfix and release branches. These branches may be a good idea for some organizations, but they are too much for most. Today, most organizations implement continuous delivery, which means your default branch can be deployed. Continuous delivery eliminates the need for hotfix and release branches, including all the rituals that their introduction entails. An example of this ritual is the regression merge of release branches. While specialized tools do exist to address these issues, they require documentation and add complexity. Developers often make mistakes, such as only merging changes to the Master branch but not to the Develop branch. The reason for these errors is that Git Flow is too complex for most use cases. For example, many projects only need to be released, but do not need to be hot-fixed.

Github Flow — a simpler alternative

In response to Git Flow, GitHub has created a simpler alternative. GitHub Flow only has a feature branch and a master branch. This workflow is clean and intuitive, and many organizations have adopted it with great success. Atlassian recommends a similar strategy, although they rebase functional branches. Combining everything into the master branch and deploying it frequently means you keep the amount of unpublished code to a minimum. This approach is consistent with the principles of streamlining and continuous delivery best practices. However, this workflow still leaves many unanswered questions about deployment, environment, release, and issue integration. In GitLab Flow, we provide additional guidance for these puzzles.

The production branch in GitLab Flow

GitHub Flow assumes that you can deploy to production any time you join the feature branch. However, this can be a problem in some cases, such as SaaS applications. This is not possible in many cases, such as:

  • You can’t control the timing of the release. For example, an iOS App can only be released if it is approved by the App Store.
  • You have deployment Windows – for example, operations teams are fully operational between 10am and 4pm on weekdays, but you may also merge code at other times.

In these cases, you can create a production branch to host the deployed code. You can deploy a new version by merging the master branch into the production branch. If you want to know what the code looks like in production, you can check out the production branch. The time of deployment is roughly known by the merge commit in the version control system. If you automate the deployment of your production branches, this time is fairly accurate. If you need a more precise time, you can have your deployment script create a label for each deployment. This process avoids the overhead of publishing, tagging, and merging that occurs in Git Flow.

Environment branch in GitLab Flow

It’s probably a good idea to have an environment that automatically updates to the master branch. However, in this case, the name of the environment might be different from the name of the branch. Suppose you have a pre-release environment, a pre-production environment, and a production environment. In this case, deploy the Master branch to the pre-release environment. To deploy to a pre-production environment, create a merge Request from the master branch to the pre-production branch. Come online by merging the pre-production branch into the production branch. This workflow, in which commits only flow downstream, ensures that everything is tested in all environments. If you need to cherry-pick a commit with a hot fix, usually develop on the feature branch and then merge it into the Master with a merge request. In this case, do not delete the feature branch yet. If the master passes the automated tests, you can merge the branch with the other branch. If this is not possible because more manual testing is required, you can send the merge request from the functional branch to the downstream branch.

Release branch in GitLab Flow

You only need to use the release branch when you need to release software to the outside world. In this case, each branch contains a minor version, such as a 2-3-stable or a 2-4-stable. Create stable branches starting at master and as late as possible. By doing so, you can minimize the time spent having to apply bug fixes to multiple branches. After a release branch is published, only critical bug fixes are added to that branch. If possible, first merge the bug fixes into the master and then cherry-pick them to the release branch. If you merge into the release branch in the first place, you may forget to cherry-pick them on the master, and then you will encounter the same error on subsequent releases. Merging into the Master branch and then cherry-picking into the release branch is called “upstream first” and is practiced at Google and Red Hat as well. Each time you add a bug fix to the release branch, increase the patch version (to match the semantic version) by setting a new TAB. Some projects also have a stable branch that points to the same commit as the recently released branch. There is usually no production branch (or master branch of Git flow) in this process.

Merge/pull requests in GitLab Flow

Merge or pull requests are created in the Git manager. They require a designated person to merge the two branches. Tools like GitHub and Bitbucket choose the name “Pull Request” because the first manual action is the pull branch. Tools such as GitLab chose the name “Merge Request” because the final action is to merge functional branches. These are referred to in this article as merge requests.

If you’ve worked on a functional branch for more than a few hours and then want to share the intermediate with the rest of the team. To do this, create a merge request and do not assign it to anyone. However, it is possible to mention someone else in a description or comment, for example, “/cc @mark @susan”. This indicates that while the merge request is not yet ready to be merged, feedback is welcome. Your team members can comment on the merge request in general, or use line comments to comment on specific lines. Merge requests are used as a code review tool rather than a separate code review tool. If a review finds a defect, anyone can submit and push a fix. Typically, the person doing this is the creator of the merge request. When a new commit is pushed to the branch, the difference in the merge request is automatically updated.

When you’re ready for your functional branches to be merged, assign the merge request to the person who knows the code base you’re modifying best. Also, mention other people from whom you’d like to hear feedback. After the designated person is satisfied with the results, they can merge the branch. If the appointee feels bad, they can ask for more changes, or close the merge request without the merge.

In GitLab, long-standing branches, such as the Master branch, are usually protected, so most developers cannot modify them. Therefore, if you want to merge into a protected branch, please assign your merge request to someone who has maintainer privileges.

After merging a functional branch, it should be removed from the source control software. In GitLab, you can do this at merge time. Deleting the completed branch ensures that only the work in progress is shown in the branch list. This also ensures that if someone reopens the issue, they can use the same branch name without causing problems.

When you reopen an issue, you should create a new merge request

Issue Tracking in GitLab Flow

GitLab Flow is a way to make the relationship between code and the Issue Tracker more transparent.

Any major changes to the code should begin with an issue that describes the goal. Providing a reason for every code change helps inform the rest of the team and keeps the granularity of a functional branch as small as possible. In GitLab, every change to the code base starts with an issue in the problem tracking system. If there is no issue yet and the change requires more than an hour of work, create one. In many organizations, issues are raised as part of the development process because they are used in sprint planning. The title of the issue should describe the expected state of the system. For example, the issue title “as an administrator, I want to delete users without reporting an error” is better than “Administrators cannot delete users.”

When you are ready to start coding, create a branch for the issue from the master branch. This branch is where any work related to this change will go.

The name of a branch may depend on organizational standards.

When you’re done or want to discuss the code, open a merge request. Merge requests are an online place to discuss changes and review code.

If you open a merge request but do not assign it to anyone, it is a draft merge request. These drafts are used to discuss the proposed implementation, but they are not yet ready for inclusion in the Master branch. Start the merge request with [Draft], Draft:, or (Draft) in the title to prevent it from being merged before it is ready.

When you think the code is ready, assign the combined request to a reviewer. When reviewers decide that the code is ready for inclusion in the Master branch, they can merge the changes. When they press the merge button, GitLab merges the code and creates a merge commit, making this event visible later. The merge request always creates a merge commit, even though the branch could have produced no merge commit at merge time. This merge strategy is called “no fast-forward” in Git. After the merge, the feature branch will be deleted because it is no longer needed. In GitLab, this deletion is an option when merging.

Suppose a branch is merged, but there is a problem and the issue is reopened. In this case, it is not a problem to reuse the same branch name because the first branch was removed at the time of the merge. At most, each issue has one branch at any one time. It is possible for one branch of functionality to solve multiple issues.

Link and close the issue from the merge request

Link them by referring to the issue in the description of the submitted information or merge request, for example, “Fixes #16” or “Duck typing is preferred. See # 12 “. GitLab then creates a link to the mentioned issue and creates a link round in the issue and requests a comment.

To automatically close linked issues, refer to them with the keyword “fixes” or “watches”, for example, “fixes #14” or “watches #67”. When the code is merged into the default branch, GitLab closes these issues.

If you have an issue that spans multiple repositories, create an issue for each repository and link all the issues to a parent issue.

Use rebase to compress the commit

In Git, you can use interactive rebase (rebase-i) to compress multiple commits into one, or to rearrange them. This feature can help you replace several small submissions with a single submission, or help you prioritize multiple submissions.

However, if you have other active contributors in the same branch, you should avoid rebasing commits that you have pushed to a remote server. Because Rebase creates new commits for all of your changes, this can be confusing because the same change can have multiple identifiers. This will cause a merge error for anyone working on the same branch because their history doesn’t match yours. This is really troublesome for authors or other contributors. Also, if someone has already reviewed your code, rebase makes it hard to tell what has changed since the last review.

You should never rebase someone else’s submission unless you agree otherwise. This would not only rewrite history, but also lose author information. Rebase makes it impossible for other authors to be attributed and share parts of git blame.

If a merge involves many commits, it may seem harder to undo. To solve this problem, you might consider using GitLab’s “squash-and-merge” feature to compress all changes into one commit before merging. Fortunately, you can undo a merge and all of its commits. The way to do this is to revert (restore) the merged commit. Retaining this ability to revert merges is a good reason to always use the “no fast-forward” (–no-ff) strategy when manually merging.

If you revert a merge submission and then change your mind, then revert that revert submission can undo the merge. Otherwise Git will not allow you to merge the code again.

Reduce merge commits in functional branches

A large number of merger proposals have confused your warehouse history. Therefore, you should try to avoid merge commits on function branches. In general, the way people avoid merging commits is to use rebase to reorder their commits after committing to the master branch. Using rebase eliminates the need for merge commits when the master branch is merged into the function branch, and creates a clean, linear history. However, as discussed in the section on Rebase, you should avoid commits in functional branches that rebase shares with others.

Rebase may produce more work because you may need to resolve the same conflicts each time you rebase. Sometimes you can rerere a recorded solution, but merge is better because you only need to resolve a conflict once. Atlassian has a more thorough explanation of the tradeoff between Merge and Rebase on their blog.

A good way to prevent many merge commits is not to merge the master branch into the function branch too often. There are three reasons to join the master: to take advantage of new code, resolve merge conflicts, and update long-running branches.

If you need to use some code that was introduced in master after you created the feature branch, it’s usually just a cherry-pick commit.

If your function branch has merge conflicts, creating a merge commit is the standard way to solve this problem.

Sometimes you can use.gitattributes to reduce merge conflicts. For example, you can set your Changelog file to use the union merge driver so that multiple new entities do not conflict with each other.

The final reason to create a merge commit is to keep the long-running feature branch up to date with the latest status of the project. The solution to this type of merge commit is to make the function branch short-lived. Most functional branches should work less than one day. If your feature branch often takes more than a day, try breaking your feature into smaller work units.

If you do need to keep a feature branch alive for more than a day, there are strategies to keep it up to date. One option is to use continuous integration (CI), which merges the master in at the beginning of the day. Another option is to plug in only from a defined point in time, such as a tagged release. You can also use feature toggle to hide incomplete features so you can still return to master every day.

Don’t confuse automated branch testing with continuous integration. Martin Fowler makes this distinction in an article on functional branching. “[People] say they are doing CI because they will run the build on every commit on every branch (perhaps using a CI server). It’s continuous build, which is a good thing, but there’s no integration, so it’s not CI.”

In short, you should try to avoid merging commits, but not eliminate them. Your code base should be clean, but your history needs to reflect what really happened. Developing software is done in small, messy steps, and it’s perfectly ok to let your history reflect that. You can use the tool to view the submitted network diagram to understand the messy history of creating your code. If you rebase the code, the history is incorrect and the tools can’t fix it because they can’t handle changing commit identifiers.

Submit and push frequently

Another way to make your development easier is to commit often. Whenever you have a set of tests and code available, you should submit it. Breaking up the work into separate commits provides context for developers looking at your code later. Smaller submissions can give a clear picture of how a feature is evolving. They can help you roll back to a specific point in time, or restore just one code change instead of several unrelated changes.

Frequent submissions also help you share your work, which is important because then everyone knows what you’re doing. You should always push your feature branch, even if it’s not ready for review. By sharing work in your function branch or merge requests, you can prevent team members from reworking. Sharing your work before it’s done can also allow for discussion and feedback on the changes. This feedback can help improve the code before review.

How to write a good commit message

The commit message should reflect your intentions, not just the content of the commit. You can see the changes in the commit, so the commit message should explain why you made the changes. A good example of a commit message is: “Merge templates to reduce duplicate code in user views.” The words “change,” “improve,” “fix,” and “refactor” do not add much information to the commit message. For example, “Improved XML generation” might be better written as “Correctly escape special characters in XML generation.” For more information on the format for submitting messages, check out this excellent blog post by Tim Pope.

To add more context to the commit message, consider adding information about the source of the change. For example, the GitLab issue URL, or the Jira issue number, includes more information for those users who need to change the detailed context.

Such as:

Properly escape special characters in XML generation.

Issue: gitlab.com/gitlab-org/gitlab/-/issues/1
Copy the code

Pre-merge test

In the old workflow, the continuous integration (CI) server typically only ran tests on the Master branch. Developers must ensure that their code does not break the master branch. When using GitLab Flow, developers create their branches from this master branch, so it is vital that it is never broken. Therefore, each merge request must be tested before being accepted. CI software such as Travis CI and GitLab CI/CD simplifies the process by showing the build results directly in the merge request.

There is a downside to testing merge requests: the CI server only tests the functional branch itself, not the result of the merge. Ideally, the server can also test the master branch after each change. However, retesting each time you commit to Master is computationally expensive and means you have to wait for test results more often. Because the functional branch is supposed to be short-lived, the risk of only testing it is acceptable. If a new commit in the Master causes a merge conflict with the functional branch, you can pull the master back into the functional branch and have the CI server reruns the tests. As mentioned earlier, if your feature branch often lasts more than a few days, then you should make your issue smaller.

Using functional branches

When creating a function branch, it is always based on the latest master branch. If before you start, you know that your work depends on another branch, it can be based on that branch, too. If you need to join another branch after starting, please explain why in the merge commit. If you haven’t already pushed your submissions to a shared location, you can also rebasemaster or another feature branch to incorporate the changes. If your code works and can be merged cleanly without merging upstream, don’t merge upstream again. Merge only when needed to avoid creating merge commits in the functional branch that will eventually leave a trace in the master history.

May 8, 2021. Some of the content may evolve over time.