This post is related to an earlier post and is part of a series on posts about branching support in Team Foundation Version Control and the particular development problems that it solves. Before we can get into that however we really need to nail down what we mean by branching and also what the related terms of merging, reverse integration, forward integration and the trunk are all about.
Like so many technologies we use today, the terminology surrounding it is based on a real-life metaphor which is useful for describing the function of a system (to a point). In the case of version control systems, the most commonly accepted metaphor is that of a tree.
Creating a version control repository is like planting the seed from which the source tree will grow and the trunk is what grows out of the seed bit by bit. In a version control system the trunk grows by having changes applied one after another until development is complete.
In some version control systems changes are recorded on a per-file basis, however Team Foundation Version Control supports the creation of changesets which represent a series of changes that are atomic.
When working with the trunk you can choose to check-out and edit files at the head of the trunk or view files from an earlier point in time. While you can change those files from an earlier point in time and check them back in, those check-ins will actually be committed to the head of the trunk. The point is that the trunk is sequential and it only ever grows.
Many development teams don’t do anything other than commit changes to the trunk of a version control repository but quite often versioning requirements exceed the capabilities of a simple sequential list of changes and this is where branching comes in to play.
While the trunk of the tree may seem important, the truth of the matter is that it is really nothing more than a special case branch (you have to start somewhere). In a version control system a branch is a copy of another branch (or trunk) at a point in time. The version control system then allows you to declare which branch you are working on and commit changes on the branch that developers working on the other branch can’t see.
The way in which branching is supported in each version control system is different which is why it is important to firstly understand the tool you are currently using, but secondly get an appreciation for the way other tools work. For example, in CVS branches are largely invisible to developers once they are on them. When the intial check-out is performed you specify which branch you want to work on.
In tools like Subversion and TFVC however branches are physically seperate folders in the source tree and developers can easily flip between them. While I’m not hung up on which approach is best, the TFVC/Subversion approach makes locking down branches with different security levels possible which can be useful as we will discover in future posts.
The act of branching creates a tree structure that makes it possible for developers to work on different versions of the same set of source files concurrently which can help productivity by isolating developers from the disruptive changes of other team members.
Having said that, the ultimate goal of software development is to ship a product that satisfies customer requirements so while feature development may occur on different branches those code changes will eventually need to be merged onto one branch (or trunk) that the final version of the product can be built from.
The process of taking source file changes from one branch and applying them to another is called “merging”. It is important to note however that you never actually merge two branches to make one, all merge does is attempt to merge contents of individual files in a roll-up of the changesets in both branches since the branch occured.
The picture above shows six merging scenarios on a tree structure with the trunk and two branches made at different points in time. The first four scenarios highlighted in green are permitted by TFVC whereas the the last two are not supported.
Merge scenarios one and two from the diagram above support a practice known as reverse integration. While reverse integration can be done manually without merging you will almost always want to use the merge functionality in TFVC because it will ensure that all the changes you want are reverse integrated. From here on in I will use the term merging and integrating interchangably.
So what is reverse integration? Reverse integration is the process of taking the development efforts that occured away from the main branch or trunk and integrating them. After integration occurs there is typically a period of stabilisation that is required to make sure that the finished product works as expected.
As code-bases and development teams that support them grow it may be necessary to introduce more intermediate branches to reverse integrate into. This allows each sub-organisation to produce builds on a stable foundation in order to perfect their own features before pushing them down and making them visible to the rest of the product teams.
Forward integration is the opposite of reverse integration and is the process of taking changes from the trunk (or parent branch) and merging them into a branch. This important process highlighted in scenarios three and four in the diagram above allows development teams to decide when they accept changes into their branch.
The logical time to perform a forward integration is as the development team approaches a milestone release that will require reverse integration. By forward integrating as code is close to being completed teams are given the opportunity to ensure their code will work with other changes that were made in the parent branch – this will make the process of reverse integration easier which is important since alot more people are affected when reverse integration goes badly.
Telling the Tale
Finally, before I start going into more detail in future posts we need to look at a complete end to end scenario involving branching and merging. To help me tell the tale I’ve introduced a short hand diagramming format. Most configuration managers who deal (well) with branching will have some kind of strategy for documenting what is going on inside the repository – this is mine.
The diagram above represents what you will typically find in a version control repository that supports branching. In this example we have a trunk which was branched in two directions, one branch to support the development of Feature A and another to support the development of Feature B.
Both Feature A and Feature B had seperate development teams and the Feature A team had a sub-team that worked on a key component of Feature A, lets call it Feature A-1. Once Feature A-1 was nearing completion the team forward integrated code from the main Feature A branch and then reverse integrated their changes. By forward integrating before reverse integrating the team minimised the impact to developers working on the Feature A branch because they had already stabilised the majority of the changes between the two branches.
With Feature A-1 integrated the Feature A team was able to complete all of their work and are ready to reverse integrate their code back onto the trunk. Once again however, they forward integrate first to ensure that most of the kinks are worked out before impacting all the teams that rely on the trunk being stable.
Meanwhile the Feature B team has been busy working on their code and are ready to push their code onto the trunk. Before they do they perform a forward integration which sucks in all the changes that the Feature A team put on the trunk. They spend some time stabilising their code then reverse integrate just in time for the first release of the product – and they all lived happily ever after!