How to Make Conflict-Free Git Trees? (Part 1)

April 6, 2015

Today let’s talk about Git tree planting: how to make easy-to-use and elegant trees? In addition we’ll talk about a few useful techniques for working with Git.

Git is now one of the most popular VCS (Version Control System). By popularity (for repositories with an open source code), it caught up with SVN. However, in the world of Android app development, Git dominates.

Why is it necessary to think about strategies of branching? Git is a powerful tool, the fundamental actions of which are branching and merging. But this tool does not say anything about when these actions should be performed. Just to answer this question, it is necessary to apply some strategy.

Let’s start with the strategy inherited from SVN — a centralized strategy. You start with the main branch, the trunk. You copy the trunk from the server to the local repository, add the changes, commit, and send it all to the server.

So far, everything is convenient and clear, but it remains so only as long as you are working alone.

If there are several people who work with the repository, there are also several local versions. The first will send changes to the server according to the first scenario. But the next participants may not be so lucky–the branch with which they are working has changed, and therefore there may be conflicts. That is not so convenient. Among other things, it is difficult to implement CI (continuous integration) within this strategy, as there is a great risk of breaking the main branch.

We cannot say that this strategy is not usable – that is not true. If your project has just migrated from SVN to Git and the team has never worked with Git before, this is a good temporary option for a transition period. Whatever it was, this strategy does not allow us to use the main Git’s feature – branches.

In order to allow several people to work on the same project simultaneously and at the same time not disturb each other, the concept of feature branches has been proposed. This is the simple rule: no matter what changes you make to the project, your first action is the creation of a new branch. You work exclusively with this branch, commit it and when it becomes clear that changes need to be shown to the rest – merge it with the main branch.

The simplest of strategies – GitHub strategy is based on this rule. GitHub promotes this strategy and there is even a small tutorial on how to use it. In this strategy, we clearly follow the feature branch rule, but when it comes time to merge the branch with the main branch, we do not do it by ourselves, but create a pull request. That is to announce to all our colleagues, “I want to merge the branch. Criticize its code, please.” Colleagues criticize, you correct their comments and, as a result, someone merges the branch.

GitHub flow strategy

This is a great strategy, because it is very simple. However, it has a significant drawback for scenarios in which the release of the product takes a significant amount of time (for example, the publication of a mobile app to the AppStore). The fact is that the main branch is used for the CI and the following can happen:

  • you have gathered release
  • you continue developing (there are new commits in the main branch)
  • the release finally has reached the users
  • users have found a bug that need to be corrected quickly, but there are new commits in the main branch.

As you can see, it is reasonable to store the code from which the last commit was compiled so that it can be easily modified. This problem is solved by a different strategy – GitFlow. This is a very popular best practice. Initially, this strategy was proposed by Vincent Driessen in his publication.

We can say that GitFlow involves an agreement as to which branches should be in the repository constantly, which branches need to be created temporarily and what to call the actions of branching and merging of certain branches.

So, initially there are two branches in the repository: master and develop. In the Master, there is the code, which already has been released. Developers work with the develop and branch feature branches from it.

The first action is “start feature”. This means creating a feature branch. Merging this branch into the develop branch  is called the “finish feature”. Features never merge directly with the master.

When developers understand that it is time to prepare the release, then the action “start release” is performed and the release branch is branched out from the develop. New features do not get to this branch. It is used for making the assembly, which is used for testing. If there are any flaws found in the process of testing, they are corrected and added to the branch release. In  addition, the documentation or meta-information can also get to this branch (for example, a file with a version number or a description of the changes).

When it comes time for release, then the action “finish release” takes place. This action means:

  1. merging release and master branches
  2. creating a tag with the version number on the master
  3. merging release with develop (to keep it up to date)
  4. removing release branch

A good practice is to configure CI for automatic build on every master merge.

Now we come to the problem that arose when using GitHub flow – hot fixes. How do we edit bugs that were found in the already released version of the product? With this strategy, it is easy because there is the code in the master, which was used for collecting release. This is the one you need to edit. The “start hotfix” action takes place – a branching out from the master branch hotfix. In this branch, the error is corrected and the “finish hotfix” action takes place. That is:

  1. Hotfix branch merges with the master
  2. A tag with the version of the corrected release is created on the master
  3. Hotfix merges with the develop
  4. Hotfix is destroyed

Note that the process is very similar to the process of release, with the only difference being that the original branch branches out of the master, not the develop.

GitFlow is not only a strategy, but also a set of scripts that help to initialize the repository and perform all actions with branches according to the GitFlow rules (see here how to install scripts: setup). Moreover, the company Atlassian has built tools for working with GitFlow in their GUI tool for working with Git-repository SourceTree.

Though GitFlow is a really well proven strategy and many people compliment it, there are still some drawbacks. I see its main flaw as its complexity. However, overall picture may not be simple, but if you look on each step separately, you’ll easily get the main principles and will be able to use this strategy. In addition, there are cases where this model simply does not fit.

In the current project at Stanfy we use a model that is similar to GitHub flow, but slightly modified. Thus, the creation of test and release assemblies is automatic in our company.

When we are ready to make an internal release so we can give it to testers, we run a task on our Jenkins. This task is trying to collect the code from the master. If all goes well, then the tag with the current version’s number is created on the master, and the assembly itself is sent to a special folder on our CI server (which is available to testers). In addition, the branch with the name of the “delivery” is placed on the current commit (from which we have made assembly). Therefore, we would label  this commit: “A commit from which we made the latest test assembly.”

If in the process of testing any problems are found, we fix them and then make a test release again.

When it comes to the final release, we run another task on Jenkins. This task collects the code from the branch delivery, affixes tags and puts the resulting assembly in the folder on the CI server that is available to the customer (or the people who will distribute this assembly).

If we need to make a hotfix, we branch off from the tag, for which it needs to be done. When the  correction is done, all is the same as with the case of the usual release. However, as the base branch, we don’t use the master, but a fixed branch. In addition, the branch with the fix merges with the master after we make bugfix release.

Git is really flexible and, as it should be with a truly good tool, it allows you to use it very differently. We have tried to look at several solutions and found that all of them are viable – you need to experiment and choose the most convenient option. But remember that it can always be improved! 😉