A key benefit of modern version control systems is that they make it easy for teams of work on parallel branches of a codebase, and later to merge such branches into a mainline code. While a simple concept, branches and their relationship to the mainline code can be become complicated, writes Martin Fowler in a recent blog post, FeatureBranch. He discusses this issue from the point of view of how developers integrate changes from different branches into the mainline codebase.
The basic idea of a feature branch is that when you start work on a feature (or story if you prefer that term) you take a branch of the repository to work on that feature. In a DVCS, you'll do this in your personal repository, but the same kind of thing works in a centralized VCS too...
The advantage of feature branching is that each developer can work on their own feature and be isolated from changes going on elsewhere. They can pull in changes from the mainline at their own pace, ensuring they don't break the flow of their feature. Furthermore it allows the team to choose its features for release.
While feature branching is an essential development practice, it can lead to problems when performing merges in order to facilitate a release. In this blog post, Fowler makes a distinction between working on independent feature branches with relatively infrequent merges to the mainline code, on the one hand, and frequent—what he calls "promiscuous"—merges into the trunk, on the other. Both approaches have merits as well as risks:
CI is effective at removing the problem of big merges, but it's also a vital communication mechanism... a key point of CI is everyone integrates to the mainline every day. Integrating across feature branches, which I shall call promiscuous integration (PI), doesn't involve or even need a mainline...
With CI, you lose the ability to use the VCS to do cherry picking [for a release]. Every developer is touching mainline, so all features grow in the mainline. With CI, the mainline must always be healthy, so in theory (and often in practice) you can safely release after any commit. Having a half built feature or a feature you'd rather not release yet won't damage the other functionality of the software, but may require some masking if you don't want it to be visible in the user-interface. This can be as simple as not including a menu item in the UI to trigger the feature...
PI [promiscuous integration] arose out of open-source work, and it could be that the less intensive tempo of open-source could be a factor here. In a full time job, you work several hours a day on a project. This makes it easier for features to be worked in priority. With an open source project people often put in a hour here, and the next hour a few days later. A feature may take one developer quite a while to complete while other developers with more time are able to get features into a releasable state earlier. In this situation cherry picking can be more important.
What are your preferred ways of maintaining feature branches?
I saw a reference to this posting in the Buzz category, and I asked a question:
Do DVCS systems solve the "feature branch problem?"
By the "problem," I mean this: when I make a feature branch, I'm branching from a particular point. If others make changes to the mainline, I have two choices, to ignore their changes for a time, or to incorporate their changes immediately. (Ie, what Fowler said.)
Incorporating their changes troubles weaker VCS systems (at work we use CVS, heheh); instead of a simple cvs up -j branchname, I have to track the revisions of each file that have been merged already and do zillions of -j post-this-merge -j pre-next-merge updates.
Not incorporating their changes means my changes may be out of date.
I know git has fantastic merging capabilities; do they include solving this problem? Hg/bzr? What are the logistical steps to executing such merges, both from the mainline and back to it?
> I saw a reference to this posting in the Buzz category, > and I asked a question: > > Do DVCS systems solve the "feature branch problem?" > > By the "problem," I mean this: when I make a feature > branch, I'm branching from a particular point. If others > make changes to the mainline, I have two choices, to > ignore their changes for a time, or to incorporate their > changes immediately. (Ie, what Fowler said.) > > Incorporating their changes troubles weaker VCS systems > (at work we use CVS, heheh); instead of a simple cvs up -j > branchname, I have to track the revisions of each file > that have been merged already and do zillions of -j > post-this-merge -j pre-next-merge updates. > > Not incorporating their changes means my changes may be > out of date. > > I know git has fantastic merging capabilities; do they > include solving this problem? Hg/bzr? What are the > logistical steps to executing such merges, both from the > mainline and back to it?
Yes. All (sane) DVCS systems solve this, they have to.
In monotone (and later systems, like git and mercurial) revisions are (change list / snapshot, [hash(parent_rev), ...]), so the history is a Directed Acyclic Graph. This means that when you merge two revisions the system knows exactly which ancestor revisions are shared and which are unique to each side, so you don't have to try to tell it what you've already merged.
The steps are
<1 hack on branch Foo> "mtn commit --message "Did something." --branch Foo <2 repeat 1 a few times...> <3 merge mainline to branch Foo> "mtn propagate mainline Foo" <4 repeat 1-3 ...> <5 merge Foo to mainline> "mtn propagate Foo mainline"