GIT STRATEGY, A PRACTICAL SAMPLE – PART II

GIT STRATEGY, A PRACTICAL SAMPLE – PART II

Git Tutorial Part II

ARCHITECTURE RESPONSIBILITY

In part I , we discussed an architecture separation which supports modular development. However, we need to worry about other points such as who maintains the common architecture in which branch it should live.

Someone must be in charge of the architecture, it can be you, me or Bryan, but one single person must be responsible for it. It may be hard to believe  but I have seen large projects at big corporations where this responsibility was spread among many people and the motive behind this separation was rather obscure. This decision results in poor software quality and redundant work. It is worth noting that choosing the person in charge should be based on experience, advanced technical skills, and long-term project awareness.

The architecture is independent of the modules that compose it and can be the responsibility of an entirely different team so we can create a new branch for it. The process of creating a branch is fully explained in the creation of our bikes branch in Part I so I will not be repeating it here.

BRANCH PER TASK

Few days ago Steve sent us the requirements for  v2.0. We now have a lot of work in progress in both modules.

 

Murphy’s law dictates that at this moment a significant bug in production is detected. A way to manage this situation, because branching in git is really fast and cheap, is to create a branch-per-task strategy for bug fixing.

This can be accomplished by going to master branch, create fix_0001 branch and go to fix_0001 branch:

git checkout master
git branch fix_0001
git checkout fix_0001

Now let’s edit the incorrect file (i.e. bikes.html):

<html lang=”en-US”>
  <head><tittle>Bike part store</tittle></head>
  <body>Some work</body>
</html>

And commit it:

git commit -a -m “Fixed bug 0001, changed tittle for bikes”

After testing it and making sure the bug is fixed, merge the hot-fix branch (fix_001) into master branch (note that –no-ff is used to avoid a fast-forward merge and make the examples more clear).

git checkout master
git merge --no-ff fix_0001

Following is the state after these commands are executed:

git branch per task example

In this case, we solved only one bug in fix_0001, but it should be noted that it is not necessary to follow a pure branch-per-task strategy, we can instead group several bugs, for example per release, and make a common branch for all of them.

We can also have a specific branch alive for bug-fixing where bugs are resolved and from time to time, as the project demands it, we can update the fixes into master branch and generate a release. In our case, it is not a good idea to work directly on it because partially resolved bugs can interfere with “ready-to-release” bugs and block merges, it’s better to manage this situation with an integration branch.

Okay now that we have taken care of the bug, let’s take care of Steve’s v2.0 needs.

INTEGRATION BRANCH

Steve calls us again, the significant bug is fixed and we have helped him put together an independent team to test future releases.

 

Bryan and I have almost finished version 2.0, we need to merge our work and generate a release for the test team. We are going to create an integration branch:

git checkout master
git branch integration_v2

We can now merge the v2 work from our modules into the integration branch:

git checkout integration_v2
git merge --no-ff bikes
git merge –no-ff scooters

The first command checks out integration_v2 branch, the second command merges bikes branch into integration_v2 and the third one merges scooters branch into integration_v2.

This is the state at the moment:

git integration branch example

The integration branch intergration_v2 now contains the code necessary to generate a release build for the test team.

As cautious software developers we ask ourselves what kind of conflicts can be encountered when using this strategy.

Well, there are two types of possible conflicts that can occur:

1.- Multiple modification in master and module.

This type of issue arises when the same file is changed in a common ancestor to master path and a module path and in one module itself. An example of this case would be a file  modified in commit fee8ca (bug 0001) and also in the commit 55b5a (work done in the bikes branch

2.- Multiple modification in modules

This type of issue arises when the same file is changed in a common ancestor of the module paths.

An example of this case would be a file that was modified in 55b5a (work done on the bikes branch) and also in 1fa8ee (work done on the scooters branch).

These conflicts must be resolved when merging the modules work into the integration branch. Type 2 can occur only when attempting the second merge.

MERGE BEFORE INTEGRATION STRATEGY

To avoid conflicts during merge of the integration branch we can implement a variant of our strategy in which our module branches are updated with the code in master branch, and later we do the merge into the integration branch. The obvious benefit is that this variation produces module branches updated with the master content. This, my friends, is an excellent practice.

First, update bikes branch with master code(orange merge in the graph):

git checkout bikes
git merge master

In this merge, only type 1 conflicts can occur.

Secondly, update the scooters branch from the master (orange merge in the graph):

git checkout scooters
git merge master

In this merge, only type 1 conflicts can occur.

And lastly, as we did before, merge the modules code into the integration branch:

git checkout integration_v2
git merge bikes
git merge scooters

In the bikes merge, no conflicts can occur, and in the scooters merge only type 2 conflicts can occur.

This is the state after this variation:

git merge before update an integration branch

Doing it this way has the benefit of each team resolving their type 1 conflicts in their own branch so when merging the into the integration branch, only type 2 conflicts can occur.

If the architecture is well designed, type 2 conflicts should be unusual since the modules will not usually affect the same code.

RELEASE A NEW VERSION

The last step, just after we get approval of the version from the testing team, is to merge the integration branch code into master to generate the production release.

git checkout master
git merge –no-ff integration_v2

That results in this state:

git release from an integration branch

At this point, we can tag (see git tag) the master as v2 version and generate a release from the master branch.

The project will continue, as Steve will continue contacting us and Bryan, so master, bike, and scooter branches will remain alive and this process will be repeated over and over again during successive versions.

Don’t forget that at this point, to update the bikes branch and the scooters branch, some bug fixing was done in the integration branch. This can be done from the master branch after the v2 release was tagged.

This is just one example of how we can manage a complex project with a repository strategy, but of course it is not the only one, in some cases probably not even the best. Each developer will need to figure out which strategy works best for each particular case. At the end, it is up to you, the developer, to understand the tools at your disposal, in this case GIT, and apply that knowledge to the project at hand so the tool works for you and your project in the best possible way.

I hope this example helps you to improve your knowledge of basic repository techniques!

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: