Hi and welcome!

GIT branching 10-08-2015

gitdev

I often see discussions about how to do branching in GIT properly. Rebase or not to rebase. Merge or not to merge. Use strict git-flow or use git-flow but with some exceptions and changes. And so on.

I think in all these debates people forget about one most important thing - we, software engineers, should produce great software. Of course, you can blindly adopt git-flow or any other methodology and fanatically follow it’s rules. But I’m not sure that this will make your software great.

To develop great software you should understand - WHY you do certain things and HOW they affects you. So, if you are using git-flow you should understand WHY you are using exactly this methodology (because it is very popular - incorrect answer) and HOW it impacts your development process and your product.

For example. I worked in a big team. We had a lot of commits per deploy. The system was big, important and it generated money, so it was extremely important to deploy only CLEAN and VERIFIED code. Of course we have had automatic tests, manual tests, code reviews and leads all the time, in "daemon" mode, reviewed commits in master. Also if something very bad happened in PROD it was vital to find and fix problem quickly. So we must were able to clearly see which commits where added to particular deploy.

When we switched from SVN to GIT, we initially did not have any rules HOW to do branching. But very quickly someone did mistake during merge and committed BAD code to master branch and this code hit PROD very badly. Leads spent few hours trying to understand in GIT commits history when things went wrong. Eventually problem was found and fixed, but it was not easy.

After this incident we invented code reviews and one rule for branching and commits:

"NO MATTER THAT, BUT COMMITS HISTORY IN MASTER BRANCH MUST BE CLEAN AND LINEAR".

Starting from that point we understood HOW we should do branching to produce great software and WHY.

I really liked the idea with linear commits history in master branch and from then I tried to adapt it in all my next projects and jobs. But, unfortunately, not always this simple idea got positive response from other developers and managers. I think this happened because projects were relatively small - few developers and few dozens commits per deploy. Yes, sometimes it was tricky to understand from commits history when code was changed and who changed it first and merged with bug, but seems like this problem just was not big enough.

In conclusion to make my point more clear simple example:

Branch per feature with simple merge

mkdir branches && cd branches && git init

echo "111" > 111.txt && git add * && git commit -m "111"

git checkout -b 222 && echo "222" >> 111.txt && git add * && git commit -m "222"
git checkout master
git checkout -b 333 && echo "333" >> 111.txt && git add * && git commit -m "333"
git checkout master

echo "444" >> 111.txt && git add * && git commit -m "444"
echo "555" >> 111.txt && git add * && git commit -m "555"

git log --graph --oneline
* 039460b 555
* 4700e87 444
* 89d4df7 111

git checkout 222 && echo "222-2" >> 111.txt && git add * && git commit -m "222-2"
git merge master
// fix conflicts
git add * && git commit -m "222-2"
git checkout master && git merge 222

git log --graph --oneline
*   9e8ce3a 222-2
|\
| * 039460b 555
| * 4700e87 444
* | f08bc09 222-2
* | 041d76d 222
|/
* 89d4df7 111

git checkout 333 && echo "333-2" >> 111.txt && git add * && git commit -m "333-2"
echo "333-3" >> 111.txt && git add * && git commit -m "333-3"
git merge master
// fix conflicts
git add * && git commit -m "333-3"
git checkout master && git merge 333

git log --graph --oneline
*   187de6d 333-3
|\
| *   9e8ce3a 222-2
| |\
| | * 039460b 555
| | * 4700e87 444
| * | f08bc09 222-2
| * | 041d76d 222
| |/
* | be95c49 333-3
* | e62e0d1 333-2
* | 15b92cc 333
|/
* 89d4df7 111

You see - just few feature branches merged to master branch. This is the simplest possible case, but already commits history is messy.

Branch per feature with squash merge

mkdir branches && cd branches && git init

echo "111" > 111.txt && git add * && git commit -m "111"

git checkout -b 222 && echo "222" >> 111.txt && git add * && git commit -m "222"
git checkout master
git checkout -b 333 && echo "333" >> 111.txt && git add * && git commit -m "333"
git checkout master

echo "444" >> 111.txt && git add * && git commit -m "444"
echo "555" >> 111.txt && git add * && git commit -m "555"

git checkout 222 && echo "222-2" >> 111.txt && git add * && git commit -m "222-2"
git merge master
// fix conflicts
git add * && git commit -m "222-2"
git checkout master
git merge --squash 222
git add * && git commit -m "222-2"

git log --graph --oneline
* 77db5e6 222-2
* 45aff46 555
* 0dbe89b 444
* d8f1793 111

git checkout 333 && echo "333-2" >> 111.txt && git add * && git commit -m "333-2"
echo "333-3" >> 111.txt && git add * && git commit -m "333-3"
git merge master
// fix conflicts
git add * && git commit -m "333-3"
git checkout master && git merge --squash 333
git add * && git commit -m "333-2"

git log --graph --oneline
* 8bced4a 333-2
* 77db5e6 222-2
* 45aff46 555
* 0dbe89b 444
* d8f1793 111

The result is the same. But now we have linear and clean history. If you will tag commits before deploy, it will be trivial to review history of changes and track problems.

Of course, you will say - with squash we loss some information about commits during feature development. Yes. But if you really need that history, you can always do multiple commits after squash merge.

This is only one way how you can achieve linear commits history in master branch. You can try interactive rebase, deleting unnecessary branches after merge, etc…. Important is that now you understand which result you want to have and WHY. Now HOW question is really simple to answer.