My team recently switched from SVN to Git and it has been more difficult to transition than I had expected. I realized that I had some misconceptions about the way Git operates. For instance, in a previous blog post I was fooled into over-complicating our development workflow because I was still thinking about branching the way I did with SVN.
In SVN you branch off of other branches. The new branch is essentially a mirror of the parent branch until you make changes to it. In Git however, you don't branch off of branches per se, you branch off of commits. Once I learned this I immediately realized that I had added an unnecessary step to our development workflow. In my previous workflow design I made the master branch mirror the code that was released to the live website. With the knowledge that you branch off of specific commits there is no longer a reason to keep a branch that mirrors live code. Instead the commit that was released to live should be tagged so it is easy to find on the timeline. Then if you have a hotfix you just branch off of that commit for your hotfix branch.
I've also realized that developer branches are pointless. With distributed version control each developer has their own local version of the master branch. Developers should be allowed to commit to master as often as they like, but they should only push their branch up to origin/master when a feature is complete. If a developer needs to work on several features at once then local feature branches should be created. This allows the developer to transition back and forth between features without leaving unfinished features in the master branch. If Feature-A is completed before Feature-B then it can be merged into master and pushed up to origin for the other developers to pull down and integrate with their own code.
At first glance my new diagram may look a little more complicated than my old one. Truthfully, I believe it is much simpler. This diagram actually shows a more complete development process. The first bar on the master branch is a commit that was released. This commit now reflects live code throughout the next development sprint. If a hotfix needs to be implemented in the middle of development it's as simple as branching off of the release commit (ideally this commit would be tagged to identify it as a release commit).
This diagram is simplified to show a possible scenario on a single developer's computer; we'll call our developer Joe. You'll notice that first on the master branch timeline there are several commits. On the second commit Joe decided that he needed to work on Feature-A while still working on some smaller tickets that come through. Joe needs to be able to commit those smaller tickets and push those up to origin without having to push unfinished code for Feature-A. Joe creates a feature branch for Feature-A where he can work happily. Every day Joe updates his master branch with changes from origin/master and he merges any new changes up into his Feature-A branch from master. This way he can make sure no new code being checked in by other developers will conflict with the code for Feature-A.
In the middle of working on Feature-A Joe is told he has to start work on Feature-B. Joe does not know which feature he will complete first so he creates another branch from the most recent commit on master. Now Joe can work on whatever feature he chooses and when he finishes a feature he can merge it down to his master branch. Since every time he works in a feature branch he periodically makes sure to merge new changes from master into the feature branch, completed features will be merged up and integrated. This way Joe knows one of his other completed features does not conflict with the one he is working on.
I think the diagram I put together represents this new workflow quite well, but it may not be easy to view it from a practical perspective. For instance, it looks like branching for multiple hotfixes is rather messy if you just go by the diagram. However, if you look at your actual Git history you will see a very linear timeline and it will be easy to see where you branch from when creating new hotfix branches.
You can see the branching lines on the left. Those are nice when you need them, but usually you'll barely have to pay attention to them. The important information is all in a linear list. Technically Hotfix-1 was branched from the last tagged release commit. Once Hotfix-1 was complete it was tagged, released, merged back into master, and then deleted. Hotfix-2 was then branched from the commit tagged "Hotfix-1". Notice that even though we deleted the Hotfix-1 branch its commit history still remains. This is nice because we can see the line telling us a branch was created and then we can see a commit on that branch tagged as "Hotfix-1", but we don't have to clutter our branch list with useless branches that are no longer used. It all sounds more complicated than it is, but really if you look at the history view above you'll notice that, even though stuff was happening on different branches, everything appears in a convenient list starting at the bottom and working up. To do a Hotfix-3 you would simply branch off of the commit tagged "Hotfix-2" and go from there. So technically the hotfix branches are being created and destroyed as needed and the branch lines show that, but as far as you're concerned you just see your tags in the list and you branch from them. It's that simple.
Tags in Git make it extremely easy to mark important points in the histroy timeline. In the future you can find those tags and branch off of them; you can even checkout that commit by itself without creating a new branch if you simply wanted to see something from that point in time. I hope seeing the actual history list helps tie the workflow together for you. It should be easy to see now how easy branching and merging is in Git. You can create 10 branches for 10 features and be just fine. As long as you are mindful of merging up changes from lower branches into your newer branches regularly then you will have a very smooth development experience.
Hopefully it's also easy to see that if one feature depends on another feature that is currently in development. Say Feature-C depends on Feature-B but Feature-B isn't done yet, then you could simply branch off a commit in Feature-B to create Feature-C, periodically merging changes in Feature-B into Feature-C just like you would from master to Feature-B. Here is an extended diagram to show how this might work.
Any hotfixes that happen during development would be merged into master after they are released. From there the hotfix would make it into all other branches when the developers do their regular merge from master. As you can see this process completely eliminates the need for a development branch. The master branch is used for regular development because of how easy it is to branch off of a tagged commit. In SVN I would completely prefer the old workflow, but Git's awesomeness makes it so much easier.
I hope this helped out anyone looking at working with Git. If you're coming from SVN it really is a big paradigm shift so be ready to re-learn some processes you thought you already understood. Trust me though, it's worth it.