(Outdated) A Merge-based Feature-branch Git workflow

5 minute read

Update

Some time after using this workflow I’ve realised that using git merge --squash has some problems I wasn’t aware of, so I don’t recommend this workflow anymore. I’ve written a new post that describes a much better workflow.

First problem with merge --squash: if you have conflicts, the resulting commit won’t contain a combination of the commit messages that are being squashed. So you won’t know which commits have been merged. Read more in StackOverflow.

Second, if you merge --squash Master to Topic with conflicts, later when you merge Topic to Master you will have to resolve the conflicts again. Because there isn’t a real merge, Git doesn’t know you have already done that. Read more: Read more in StackOverflow.

In conclusion, git merge --squash is a good option to merge Topic to Master, but not Master to Topic, because the disadvantages it brings outweigh the advantages I discussed in the original post below.

Original post

In this post I explain the Git workflow I’ve found most useful, easy, clean and safe to work with. I call it Merge-based, because I only use git merge, and Feature-branch, because I create a new branch for each feature.

Here I assume that your project has a trunk branch that I will call Master, which should be as clean as possible and working at any moment. I also assume that you are using Gitlab, but I imagine it would work the same with Github or similar.

I won’t discuss whether you should have a Develop and a Master branch, or only Master, or a different branch for each release. In the end, this will depend on the kind of software you are developing and how you release it.

Instead, here I show the most basic part of any workflow, which is not usually discussed in detail: when to create a new branch, how to update it when Master gets ahead and how to merge it with Master.

In a nutshell

  1. Create a Topic branch from Master for each new feature, improvement, bug fix, etc. Start developing.

  2. Once development is finished, do

    git merge --squash master
    

    to ensure any new commits in Master are applied to the Topic branch. You can also do this many times during development, but it’s important to do it once finished.

    This is necessary as the Master branch should be as stable as possible, so integration should be tested first in Topic.

  3. Create a merge request in Gitlab, tick the “Squash commits” option and assign a reviewer. The review should happen shortly, in case other branches are merged to Master. If that happened, Master should be merged with –squash again to the Topic branch. It’s very important that merges to Master are as clean as possible.

  4. Accept the merge request. Make sure the “Squash commits” option is ticked and the “Delete branch” unticked. We want to keep Topic’s history at least for some time.

Example

Here we can see a graphical example. Workflow

  • The blue circles are Merge commits in Master
  • The green circles are commits in a Topic (Feature) branch
  • The orange circles are squash commits created by Gitlab
  • The orange rectangles are Merge Requests in Gitlab
  • Each vertical line represents a unit of time.

Explanation: one developer starts branch Feature A. At the same time, another starts Feature B. When A is finished, the developer creates a Merge Request in Gitlab which is reviewed and accepted by another developer. Gitlab squashes the commits A1 and A2 in a single commit (Squashed A) and creates a new merge commit in Master (Merge A into Master).

The other developer is still working on B. When he is finished, he sees Master has advanced, so he does a merge –squash from Master to Feature B. Feature B gets updated with a commit that combines the new Master commits (Merge –squash Master into B). Finally, he creates a Merge Request, which is reviewed and accepted.

As you can see in the picture, it’s a very simple workflow.

Reasoning

This is my preferred Git workflow for the following reasons.

First, the whole project history is kept while having a concise Master history at the same time.

Gitlab merges Topic branches to Master as two commits. The first combines every commit in the Feature branch. The second is the merge commit, which doesn’t bring any change but is useful to make the merge explicit in the history, and to show who accepted the merge request.

This way, the Master history is clean because there’s only two commits for each feature. The first commit contains the changes and indicates the author of the feature, while the second commit indicates who merged the feature. This makes clear who was involved in each change.

If we ever want to examine the history in detail, we can do so by checking out the relevant Feature branch, whose full history is kept in the remote (at least for a reasonable time, but it could be wiped some time later).

In addition, it’s possible to know when a Topic branch started with git merge-base Topic Master. Because the merge –squash is not a real merge, Master and Topic only have one common ancestor, which is the commit from which Topic branched off.

The second reason is that the workflow is very safe. Conflict resolutions with merge are easier than with rebase. Rebase is never used, so it’s never necessary to git push --force-with-lease (do not use --force). Squashing during the merge to Master is managed by Gitlab, so you don’t have to worry about it.

Possible mistakes

The workflow is very safe, but there’s two main mistakes one could do.

  1. Forget to add –squash when merging Master to Topic. This will cause a real merge from Master to Topic. It’s not a big deal, but it will pollute history. The only safe workaround is deleting Topic once merged into Master.

  2. Forget to tick “Squash commits” in the merge request. This will add every Topic commit to Master. This will pollute history and cannot be undone. It’s unlikely that this will happen because the option should be checked by both the merge requester and the reviewer.

In any case, none of these mistakes are destructive, so one shouldn’t worry if they happen.

More details

I haven’t checked what Gitlab does internally to merge Topic to Master, but you can manually achieve the same results like this:

    git checkout feature
    # Create temporary branch for squashing
    git checkout -b squash
    # Squash all feature commits
    git reset --soft $(git merge-base squash master)
    git add .
    git commit

As the squash is done in a new temporary branch, the feature branch’s full history is kept. Alternatively, the squash could be done with:

    git rebase --interactive $(git merge-base squash master)

Actually, Gitlab also rebases the squashed commit to master before merging –no-ff. The second commit can be achieved manually with:

    git checkout master
    # Merge with 'no fast-forward'. It ensures a commit is created
    git merge --no-ff squash
    git branch -D squash

Categories:

Updated:

Leave a comment