GIT STRATEGY, A PRACTICAL SAMPLE – 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>Bikepartstore</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:
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:
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:
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:
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!