An Introduction to ClearCase Branching Strategies

An Introduction to ClearCase Branching Strategies

Version 1.1
By Doug Fierro
fierro@rational.com
supplement to presentation #CCM11


Summary

backround

Branching- that one word in the software development world can evoke a variety of responses. Some people love branching- they say they couldn't live without out it. Others are not so fond of it- a necessary evil at best. Their faces may show visible pain upon discussing the subject.

Most likely the people who are very favorable towards branching have used tools which greatly facilitate the branch/merge process of development, like ClearCase. The rest are probably still using legacy version control systems, or commercial tools based on technology that does not handle branching and merging well in a team environment.

The ClearCase branch/merge model is one of the most elegant features of the product. Any site not taking advantage of the ClearCase branching features to some extent is short-changing themselves and their development community from realizing the full potential of ClearCase and moving towards true parallel development.

Before ClearCase, branching was at best a manual and tedious process. Most shops stayed away from branching because they did not have the necessary tools in order to completely implement parallel development. It was hard enough to know the right place to branch from in one file, let alone several files being modified at once. The branch name provided to you by legacy version control tools was a numeric sequence identifier, and did not have any built in merge capabilities to speak of. Even if the merges were trivial, identifying all the files that are candidates for a merge was a task within itself. Forget multiple branch merges or subtractive merges or sibling merges - most sites would be happy to have any type of automated merge supported.

Most developers who have encountered traditional version control tools like SCCS or RCS have maybe tried a branch and merge on their own, just to say they have done it, and then never visited the topic again. To describe the process as error-prone is an understatement. There is much manual copying, editing, and diffing that takes place using legacy version control systems if you want to merge.

With ClearCase however, much of the tedious and manual work associated with branching and merging goes away. ClearCase uses an intuitive branching model that is natural for the developer to use. When a user wants to modify a file, the branch is dynamically created at the appropriate version on checkout, and ClearCase uses a named branch to describe the task, all in a transparent workspace. Built-in merge tools can graphically walk a user through a point-and-click solution, making branching and merging "fun". And ClearCase treats directories as first-class objects, which means they can be checked out and checked in, branched and merged just like a source file.

The goal of this paper is to introduce ClearCase users to different types of branching methodologies and terminology, and to provide examples so that a site may come up with their own solution for branching that fits their requirements best. Known best practices as well as common pitfalls will be discussed so the reader is aware of the pros and cons of different branching strategies.

Since most new users to ClearCase have not rigorously implemented branching/merging in their previous tool environment, or may have avoided parallel development entirely, some of the advanced features of ClearCase branching might be difficult for beginners. That is why it is important to cover the basics first.

Why Branch?

This question comes up quite a bit with new customers; before they jump into branching, they want to know the pros and cons. It is a fair question to ask so let's look at both sides.

Cons

The downside to branching is that you have to merge at some point. Again, merging can be a painful word to a lot of people before they were introduced to ClearCase. With ClearCase however, this downside is reduced or eliminated. Granted, any merge tool will require human interaction when conflicts are encountered, but with ClearCase's point-and-click interactive graphical merge tool, this is no longer a great burden. And the majority of branched changes will merge automatically anyway with no conflicts, which will greatly reduce the amount of time needed to integrate changes. ClearCase also provides merge options to allow you to identify any potential merge candidate within a repository, reducing the chance of missing changes that should be merged.

But some say if everyone works off the same branch, you eliminate the need to merge at all. This is a very short-sighted view, and in effect you are crippling parallel development, and not taking full advantage of the basic features of ClearCase. There are many pros to using an effective branching model for development.

Pros

The pros of branching with ClearCase:

Let's look in some more detail at some of the advantages of branching with ClearCase.

branching as a form of communication in ClearCase

With ClearCase, you can now name branches. You can also name additional metadata such as tags or labels that applied to a version of a particular file or element.

Let's look at a traditional version control tree:

It's hard to tell what work has been done on this particular file just by glancing at its version tree.

Compare this to a version tree with named branches, labels, and merge arrows:

Even without previous knowledge of ClearCase, the graphical version tree for this file is very intuitive- you can see for example that two tasks- in this case two bugfixes - were branched from a common point, release 2 (REL2). They were later merged back into the trunk of the tree, or main branch. The most recent version of the file, or LATEST, was part of the BETA1 release as well as the subsequent maintenance release, REL2.1. This basic branching strategy is how many ClearCase shops perform maintenance work on an existing code base.

As you can see, branching in ClearCase, besides allowing parallel and isolated development, also acts as a form of communication between members of a development team. Good communication within a team is always important on a project, especially as the team grows over time. ClearCase branches are an easy and intuitive way to relate historical data of one or more data files to the rest of the team. Without branches, changes over time appear one-dimensional and require further interaction on behalf of the user in order to extract relevant data.

The ClearCase version tree GUI is simple and intuitive, and in fact provides a wealth of functionality to support "one-stop shopping" for most users who are interested in a particular file under SCM control. From the ClearCase vtree, you can diff versions, view versions, filter metadata, search, query history and ownership, merge, zoom in/out, change color defaults, and even print out a hardcopy to review later.

parallel branching reduces time to release

One of the biggest benefits to branching is to allow parallel development, which in turn lets engineers work more in unison without clobbering each other's changes. Developers can share code quicker with ClearCase, and are not locked out from modifying a file they need when using branching.

There are many problems with everyone trying to checkout/checkin on the same branch at the same time. The major drawback to this method is that changes to shared files become serialized. The next change cannot happen until the previous change is committed, or checked in. So instead of allowing say two people to work in parallel and then merge their code later, the second developer must wait until the previous developer is complete with their work. Often this leads to contention of the "lock file". What tends to happen in situations like this is that users will instead retrieve the previous checked in version for viewing only, and then modify this copy instead of waiting for the current checkout to be checked in. There are many dangers to working in this kind of environment, ranging from working outside control of the tool (and hence misleading metrics as to the true status of the project) to clobbering changes that were introduced by the most recent checked in file version in the worst case. Any site who observes bugfixes suddenly undo themselves (re-introducing bad code that was previously fixed) is guilty of this behavior.

Forcing everyone to work on the same branch to make their changes also introduces the infamous "code freeze", where developers have to wait for a build or release to complete until they can resume their work. With ClearCase, development never stops- branches, labels, and time-based workspace rules allow a build or release engineer to effectively stop time in their own view, while allowing new development to occur in other views.

By allowing many developers to work in parallel via branching, you eliminate forced serial checkins, and hence speed up overall development over the same amount of time, which results in shorter release cycles. The diagram below illustrates this using just two concurrent tasks as an example. The more concurrent tasks a shop undertakes, the greater savings they will see over time if they move to a more parallel development model:

ClearCase also supports multiple branch merges, so integration merges are not serialized either, further reducing the length of the release cycle. In the previous diagram, our simple example reduced the time from start to finish by one-third. Imagine several tasks happening in parallel with a larger team, and you can quickly see how you can greatly reduce the time needed to release code by taking advantage of branching. One can easily extrapolate with this simple example of a six-week maintenance release cycle reduced by one-third to four weeks. Those are real savings that can be observed and measured, and in fact many shops that adopt ClearCase can see their release efforts reduced from weeks to days by taking advantage of the branching/merging features of the product.

graphical point-and-click merge tools

ClearCase supports both a graphical and command-line merge. The easy-to-use graphical merge greatly reduces the amount of manual effort needed to merge one or more files with branched changes, and also improves quality at the same time by eliminating tedious copying and cut-and-paste editing. Below is a sample graphical merge session:

Providing a first-class merge tool for the developer to use will allow projects to take advantage of branching without having to deal with the manual and error-prone headaches of merging later.

So at this point, hopefully the advantages of branching with ClearCase clearly and overwhelmingly are preferred to not taking advantage of branching. Once the advantages of branching are understood, we can then discuss different approaches to branching. Some basic branching principles are presented below to provide guidance in implementing a successful branching strategy.

basic branching scenarios using ClearCase

There is no one correct way to branch using ClearCase. Many sites implement a variety of branching strategies that are tailored to their particular development environment. ClearCase provides the building blocks to build a successful branching environment for any size and type of shop. There is however no out-of-the-box branching method or template with ClearCase, and there are trade-offs depending on what type of branching strategy you choose. This document hopes to outline various basic strategies and the pros and cons of each where appropriate in order to provide ideas for constructing a particular branching policy using ClearCase.

A successful branching strategy will encompass not just creating branch types but also effective use of other ClearCase metadata (labels, attributes, triggers, etc.), view creation/administration/maintenance, configuration specifications, naming conventions, and an underlying policy or process to follow. Creating random branches for development does not constitute a branching policy. Ideally you would like your element version trees to map directly to the underlying policy/process being implemented to faciliate communication.

Regardless of your approach to branching, it is always good to merge early and often. Branches that are left alone for long periods of time can be difficult to resolve later. Also a common public branch where changes are eventually gathered helps facilitate the integration and release of software code.

views and branching

Any discussion of branching in ClearCase will ultimately touch upon the use of ClearCase views as well. It is assumed that the reader is familiar with the concepts of a ClearCase view. A ClearCase view is unique in that it provides a transparent workspace for the user to work from that is rule-based. These view rules- configuration specifications- or config specs for short, define not only what set of files you see, but also appropriate branching rules. In general, new branches are created as needed when a file is modified, or checked out. ClearCase views are smart enough to know if a file has a previously-defined branch, and will automatically select that branch if desired by the user. The config specs also define where you want a new branch to be created from, so you are always guaranteed that you will branch from the appropriate starting point.

Two basic approaches to branching are a branch-per-task method, and a persistent or stream approach. Sometime the two are used in combination. Usually an initial branch is created from a static view of a project, or baseline, via a selected label. Time-based view rules can also accomplish the same thing, but a label is more intuitive and makes a version tree more descriptive. Branching off the latest and greatest code is also supported, but is discouraged for any type of work that is being performed where you want to be isolated from new changes being checked in. We will also discuss nested or cascading branching and when this might be desired over single-level branching.

branch-per-task method

The idea behind branching for a given task is that the branch is created for one particular and clearly-defined task, such as a bugfix or enhancement. Generally the task is short-lived relative to the life of the project and encompasses only a very small percentage of the total number of project elements. The task is usually performed on an existing code base, and may use a previous release as a baseline. As a shared file is modified over time by several different tasks, that ClearCase element's history will reflect all of the branches that were used to perform changes on that file, as well as any subsequent metadata, such as merges, labels, and attributes. It is recommended that a separate view correspond to each task at hand, but this is not required. There are advantages to having short-lived views that are subsequently deleted, but a discussion of the pros and cons of a particular view strategy is another topic outside the scope of this document. But it should be mentioned that timely removal of old/obselete views is a good practice to encourage in a ClearCase environment.

With ClearCase, a branch type is first created in the database, and then instances of those branch types are used on one or more elements associated with the branch change. An instance of a branch takes a very small amount of space in the database, and the data container storage for a text file is not affected in any way by the number of branches a file may be a part of- it is stored in an efficient interleaved-delta format. If you have say a bugfix10 task, with a branch of the same name representing that task, only files that are part of that fix will be branched. Branches are not created for every single project file when you choose to branch for a given task, and no redundant copies of files are ever created as a result of branching. This is an efficient and elegant solution to branching that does not waste disk space and allows existing branches to be quickly identified for subsequent coding, reporting, or merging. All of this takes place transparently in the same directory namespace, without copying project source trees for each user. This is commonly referred to in ClearCase as the work in place model.

A standard branch-per-task config spec template for views to use will look like this:

(1) element * CHECKEDOUT
(2) element * /main/branchname/LATEST
(3) element * LABEL -mkbranch branchname
(4) element * /main/0 -mkbranch branchname

For this view rule template, you simply decide what branch you will use, and what release/build label baseline you want to work from.

Note in this example rule (4); this is included as a "catch-all" rule. This can be useful for elements that are newly created since the label was applied. Without this catch-all rule, the view would not pick up such elements. Since view rules are evaluated top-to-bottom, this config spec template for simiple branching guarantees us that we will always select the labelled baseline version of our project first before all others. Some sites use /main/LATEST as an alternate catch-all rule if a view spans multiple repositories that may not have the desired label applied, but this does not constitute a stable baseline by any means.

NOTE: A comprehensive review and discusson on configuration specification view rules is beyond the scope of this paper. Useful on-line man pages to refer to include the config_spec and query_language entries.

Using our template as an example, we can show a config spec for a view working on bugfix2 of the 3.1 maintenance release:

element * CHECKEDOUT
element * /main/bugfix2/LATEST
element * RELEASE_3.1 -mkbranch bugfix2
element * /main/0 -mkbranch bugfix2

Note that this view is using a combination of dynamic and static view rules. On our own private isolated branch, we want to see the latest work on that branch that we have done; conversely, we only want to see a stable baseline view of the project- in this case the files and directories that comprise Release 3.1- for files we did not modify. ClearCase rule-based workspaces are a simple yet powerful mechanism to accomplish this task without having to do any manual updating or resynchronizing of the project data. These four view rules above will successfully allow a user to work on the bugfix2 task in isolation using a stable baseline of the code, and allow access to any file for checkout that is part of the 3.1 release, regardless of the size of the project, and without having to populate a private workspace first.

Also note that we are fully-qualifying the branch names from /main; ClearCase also has a shortcut feature to automatically match an existing branch anywhere on an element tree using the "..." qualifier, which we will look at later. This may be applicable for multi-level branching.

View Profiles

For ClearCase on Windows NT, there is a View Profile tool that can hide the details of view rules for label-based branching and automate the whole process of creating/using view config specs and branches. The project lead or administrator creates one or more view profiles depending on the type of development work being performed. These profiles act as a template for individual users to work from their own branched views. Here is a picture showing the view profiles on Windows NT:

... and here is an example of how a user would set up a private branch using the view profile:

This branch would automatically be created in one more designated "profile VOBs".

Let's see how task-based branching would look in a simple example.

For our example, we have three files in a common directory using a task-based config spec in our view. We are working off the release 2.1 baseline, REL2.1. The eye in the figure points to the version of a file our view "sees". Our view config spec states that we want to work off the specified labelled baseline, so that is what versions are visible in our view:


example config spec for branched view:
element * CHECKEDOUT
element * /main/taskbranch/LATEST
element * REL2.1 -mkbranch taskbranch
element * /main/0 -mkbranch taskbranch

Now let's check out a couple of files for our task:

	cleartool co file2.c file2.h
This will result in ClearCase branching only the files we need to modify; the rest of the project files remain view-only copies that are still accessible from any tools or commands used within the view. Versioned-controlled files and checked out files for local modifications are all displayed transparently in our ClearCase workspace:

We can then test, checkin, and later automatically merge the modified files for our task. If those changes become part of the next release, they may be labelled as well. Our task view would then be complete at this point, and the version trees of the simple example files would be as follows:

Notice that all labelling here is done on a selected release branch; we will see later the advantages of having a designated branch for releases. This method also supports the iterative development approach, where a set of code may undergo many revisions with new code added in or existing code is modified during the development process. It allows changes to be integrated as they become available, as opposed to an all-at-once or waterfall-type integration phase. Trying to integrate massive amounts of code at once can be highly destabilizing and lead to difficulty in producing a stable build.

Over time, a file will accumulate many branches using this method if many different tasks result in changes to one particular file. This is not necessarily a bad thing- it accurately reflects the work performed on the file over time and provides complete accountabilty with a graphical audit trail. We will look at options later to help us "prune" or clean up larger version trees if desired.

Also with a branch-per-task method, you have a choice of what to do with the ClearCase view that was used to perform the task- you can re-use the existing workspace for other tasks by adjusting the view rules, or completely remove the view at a later time when the branched changes are "blessed" or incorporated into a successful build or release. There are many advantages to removing views as work performed in them is complete, especially if a site is also using ClearMake. These advantages include a guaranteed clean workspace to begin work from scratch, removal of extraneous "debris" files that accumulate over time, and removing derived object reference counts to the VOB, which facilitate scrubbing later.

naming conventions for metadata

As you can see, utilizing naming conventions for creation of objects such as branches, labels, and views can greatly facilitate the administration and use of an enterprise-wide application such as ClearCase. Some values commonly used to make up a naming convention include site identifiers, date value, usernames, prefixes, and suffixes among other things. Some examples:

branchname = {bugfixid}_tokyo
build label = [ ALPHA | BETA | RELEASE ]_{rel.seq}_MM:DD:YY
release label = <SYS>_<PROJ>_[R|A|B]xx[.yy[.zz]].[BL[1-9][0-9]*]
viewtag = {USERID}_{taskname}_view   or   viewtag = {USERID}.{machine} .

Rational advises that branch names be lower-case and labels be upper-case. Use of triggers can enforce naming conventions for metadata within a VOB.

stream branches

Another method of branching involves persistent branches, or streams of development, where the view rules associated with these branches do not change much over time. In fact, many tasks may be contained within the given branch stream. These are longer transactions in nature as compared to the branch-per-task model. This can be the case for example for new code being written for a project, or porting a set of code to a new platform. A developer can update their branch over time from a designated shared or release branch, as well as from other sibling branches. Usually several checkins occur in isolation before work on a stream branch is merged into a common shared branch. This type of merge is almost always a non-trivial merge, whereas a branch-per-task model may result in more trival, or automatic merges, where conflicts do not often occur. Depending on the nature of the stream development, such as the case of a port, a significant amount of files may be a part of this persistent branch over time. By contrast, usually a very small percentage of the total project files are branched for a particular task in the branch-per-task model.

Let's look at the case of a developer John working on a vendor port of a given product. This is a major deliverable that has just begun, so relatively little code has been added so far in regards to this project. This port has not been released yet since it just commenced, so fixes and new feature enhancements may be done by John during his overall porting effort. There is most likely a specification document outlining exactly what the requirements are for this vendor port, but these items are probably not uniquely identified in a way that will support a branch-per-task model well. The deliverable in this case is the ported code itself, not necessarily a list of requests or features, so the stream branch associated with this port may in fact encompass many individual tasks. The shared code that John is working on could still be modified by other developers who are performing other work in progress.

Here is an example of what this stream branch of development might look like over time on a shared utility file:

With John's porting work, he is using the same type of config spec that the other branch-per-task views use, but his task happens to encompass many sub-tasks, which are not reflected in the single branch itself as it progresses over time. We will see later with nested branches how we can achieve this with a combination of stream branches and task branches.

It is easier for John to do his porting work if he does not have to change his view rules over time. Note however that if he wishes to work on another unrelated task, such as a bugfix, he will not be able to use his current view without changing the view rules. In such cases, where developers might work on more than one task simultaneously, it is recommended that users create one view per task and work in the view that corresponds to the task at hand. Trying to reuse views too much has its disadvantages, such as overlapping and accumulated debris, winked-in but unused derived objects, and potential confusion with constantly changing the config specs. It would be like using your HOME directory for doing all your development work in. Views should be considered transitory workspaces, not permanent repositories. That is what the VOB is for!

Even though John is staying on his porting branch for a period of time, he can still share changes that are being made in parallel around him. This is one of the powerful features of ClearCase. Note how John picked up the enhancement 20 changes for his port even though that was code added after he began his work. John may have decided that this was a significant feature enhancement that he wanted to include in his vendor port, even though it was not defined within the original specification porting document.

John may also take snapshots or baselines of his work over time, which is reflected in his two build labels. This allows him to rollback to a previous baseline if needed, and also provides visible milestones that others on the project can observe.

"user" branches

Sometimes a hybrid between task branches and stream branches develops at a site which is config-spec adverse. A user branch here is defined to be a branch whose name reflects the actual developer performing work on that branch. What sometimes happens is that instead of using a new view and branch name each time a new task is worked upon, a user may instead simply re-name their existing user branch to something different after they merge, and continue working on in their existing view without modifying their view rules or creating new views for new tasks to work on.

There are several drawbacks to this shortcut method. One is that no one is really sure what you are working on if your current branch name is always the same. Another drawback is if the developer does not choose a useful name for the branch they are renaming, the graphical history becomes meaningless. Also views used in this way tend to gather much debris over time, including not only view private files but winked in derived objects as well. This can cause the derived object pool of a VOB to grow unnecessarily large over time since obselete D.O.s are still being "held" or referenced by old views.

Here is an example of what a version tree might look like over time if existing user branches are renamed say according to the date the task was completed:

There may be some cases where having a user specify their own name in a branch may be appropriate, but these are limited cases. For a site to take full advantage of simple task branching, a branchname that truly describes the task at hand should be used, and a corresponding view should be created for that task, following a defined view rule template. Developers who complete their tasks should later remove the corresponding view. Renaming branch types should never be a substitute for proper view usage and maintenance.

branching from static baselines vs. LATEST rule

You will notice in the branch-per-task examples shown here that there is a label rule present in the config specs. This is an important distinction from simply branching off the latest version of a parent branch. As development/maintenance groups grow and more files become shared, having /main/LATEST as a branchpoint can in fact be too dynamic - there is a possibility to pick up incomplete changes as they are checked in and/or merged. It is much preferable to consider a baseline of code to work from instead, which provides a static view of the project while new work is performed. This can be accomplished via ClearCase's label mechanism.

Consider the following example which illustrates the drawbacks to doing view work without a stable baseline defined. Say we have two tasks - featureA and featureB. Each developer places their changes on their own pre-defined branch using the following config spec template:

element * CHECKEDOUT
element * /main/featurebranch/LATEST
element * /main/LATEST -mkbranch featurebranch

Let's assume that both tasks have an overlapping file in common- global.h . With ClearCase, it is not necessary for the featureA changes to be checked in before featureB work begins on a common file if each task is making changes on their own branch. Thus true parallel development is achieved :

Notice in the previous diagram that the featureB view is not picking up any featureA changes- both developers are working insulated from the rest of the project team on their respective branches.

Now let's assume that featureA work is complete, and it is subsequently merged back into the parent branch, which in this case is defined as the main branch:

Notice how the featureB view, with the /main/LATEST rule present in the config spec, will automatically pick up the latest change of foo.c in their view, yet they only see their insulated version of global.h. This can cause build problems later, because the changes in foo.c made by featureA rely on the associated changes in the global header file global.h to work, which is being concurrently worked on by the developer for featureB. What results is version skew - a view picking up only partial changes in the project.

The simple solution is to lay down one or more labels across the project files. This can be a locked or a fixed label, indicating a past release for example, or a floating label, which is adjusted at pre-defined intervals, such as when code has been tested or "blessed". The config spec for featureB would be modified as follows:

view for featureB using a baseline label
element * CHECKEDOUT
element * /main/featureB/LATEST
element * BASELABEL -mkbranch featureB
element * /main/0 -mkbranch featureB

Now the featureB view would see the following versions of the project files with a baseline label rule implemented:

An alternate is to use a time-based rule in a view, which ClearCase supports, but this is not as intuitive as using labels and baselines established this way do not show up in the version tree history. Labels consume very little space in the database relative to the entire VOB repository, regardless of the number of total elements.

So in general, unless you are a very small shop, or developers do not share many files, branching off /main/LATEST has many pitfalls in a production environment. Some signs a customer is using /main/LATEST too much include many unreserved checkouts, using view rules like /main/{created_by(me)}, using the -version option often on checkouts, and encountering version skew often.

Some sites try to provide a static view of a shared branch via labels or time-based rules without enabling branching. This can lead to an extreme case of version skew where the developer checking in their work doesn't pick up their own changes! This is more commonly known as the "dissappearing checkins" phenomenon, where you are telling ClearCase to not show file/directory versions past a certain point on a particular branch, yet you are adding newer versions on that same branch upon checkin. The solution is quite simple- use branching to make changes to code on a dynamic private branch. A good rule of thumb to follow for development views is to provide a static view of the project code before modification, and when using -mkbranch make sure you dynamically select the LATEST version of the branch you just created.

multiple release branches

Some sites maintain multiple concurrent releases using ClearCase instead of having a single target release branch. Users can still follow the same branching scenario as with a single release branch, with some adjustments.

Let's say we are going to take a release of our product, 2.1, and maintain a concurrent release of fixes applied to 2.1. This maintenance branch will be named 2.1_fixes and the naming convention for the release labels on this branch will be "REL_2.1.X". Following the task-per-branch model, a particular element may contain the following version tree:

A common habit would be to apply the REL_2.1.1 label across all the project files at the time of the release. This has some drawbacks later when branching if you apply a label meant for one release branch across other release branches, since not every element may have a 2.1_fixes branch. In general, it is a good idea to have a final release label correspond to exactly one branch type used for that release. Let's look at an example for a view applying a particular fix to the 2.1_fixes branch with the following config spec that utilizes branching based on a label:

original view for 2.1 maintenance fix 300:
element * CHECKEDOUT
element * .../fix300/LATEST
element * REL_2.1.1 -mkbranch fix300

Note that we introduced a new config spec notation, ".../" ; this shorthand tells the view to match the specified branch anywhere it may exist within an element's version tree.

What if we wanted to modify a file that had not yet been worked on for the 2.1 maintenance release? With the above view, we may get a branch like this on checkout for an element that has no previous 2.1 maintenance work, assuming that all elements receive the REL_2.1.X label at release time:

While this may be functional, it does not follow our policy of creating 2.1 maintenance fixes off the 2.1_fixes branch. Also if you branch exclusively from a label, you may run into problems if that label does not yet exist on a particular element.

The first thing to do is not apply REL_2.1.X labels to the main branch; those labels should only be applied to the 2.1_fixes branch. A release view would look as follows to show the final release of say 2.1.1:

release view for 2.1.1
element * REL_2.1.1 -nocheckout
element * REL_2.1 -nocheckout

The next step is to adjust our previous development view so that it can handle branching if the desired release label or maintenance branch does not exist:

revised view for 2.1 maintenance fix 300:
element * CHECKEDOUT
element * .../fix300/LATEST
element * REL_2.1.1 -mkbranch fix300
element * .../2.1_fixes/LATEST -mkbranch fix300
element * REL_2.1 -mkbranch 2.1_fixes
element * /main/0 -mkbranch 2.1_fixes

Now if we modify our file foo.h, using the updated adjustments for multiple release branches, it will look as follows upon checkout:

This version tree better represents that fix300 is for the concurrent maintenance release based on the 2.1_fixes branch, instead of branching directly off /main. Also notice that the release label REL_2.1.1 is not present on the main branch anymore. Eventually foo.h will receive a REL_2.1.X release label on its intended 2.1_fixes maintenance branch. This can all happen even if this file is also part of a concurrent release, say REL_2.2, being performed on the main branch at the same time. Just as with checkouts/checkins, you are not restricted to serialized releases either when using ClearCase.

merging

A common question that sites ponder is, "who does the merges?". While ClearCase does provide superior automated merge capabilities and an intuitive graphical interface, there is no merge tool on the planet that can automatically resolve coding conflicts should they arise. ClearCase performs a textual diff-and-merge, so binary or data merges are not supported. Someone ultimately has to make the choice of what the final version of a file should be given two or more contributors with conflicting changes.

There are a few approaches:

Some sites allow developers to merge directly into the shared target branch. Other sites control who is allowed to merge on the target branch, whether it is a development, test, or release branch. One way to allow developers to resolve merge conflicts yet restrict access to the shared branch is to have a project lead or CM administrator run the automatic merge in ClearCase, and notify the owners whose checked in changes failed the automatic merge. These developers can then re-synchronize from the shared branch to their own private branch via a merge, and after they check in their updated changes on their private branch, the second merge back to the shared branch will proceed without conflicts, which is also known as a trivial merge. There will be two merge arrows resulting from this type of synch-then-merge scenario: one to the private branch out from the target shared branch for resynchronization, then a trivial merge back in to the same target branch:

This mixed approach allows a target branch to be controlled while still allowing for developers to resolve merge conflicts should they arise. The trade-off is that conflicting merges require two merge operations, even though the second one will be a trivial merge. Developers must also keep their task views around until the code is later merged successfully.

Whether a target branch is controlled or not is a policy decision. The scenario where all developers merge their code to a target branch is a push model, whereas changes that are selectively merged in is more of a pull model. The merge semantics are the same regardless of the particular integration policy. ClearCase can support both a push and a pull model for software development.

cascading branching

A more advanced branching strategy involves using a mix of both branch steams and task branches. This method can be expanded upon to implement a particular policy or process within ClearCase. Since ClearCase supports multiple-level or nested branching, you can represent a staged or promotion model for development to follow in combination with using various metadata.

For our example we will use a persistent branch that will accumulate all of the task changes on to a single development stream. This branch will be named appropriately development. It will provide a single staged area before code is finally released.

A first-pass at a set of development view rules might produce the following very dynamic ruleset:

first draft:
element * CHECKEDOUT
element * /main/development/{taskbranch}/LATEST
element * /main/development/LATEST -mkbranch {taskbranch}
element * /main/LATEST -mkbranch development

While this config spec is syntatically correct, as we have seen earlier, purely dynamic views have their drawbacks. Our "second draft" config spec example will therefore introduce some labels to effectively control what the developer sees in their view to provide stable baselines to work from, as well as adjusting the catch-all rules to use /main/0.

We will use a combination of labels, defined as follows:

With the previous branches and labels defined, we can now look at a second revision of the config spec template for the developer task views:

Second draft using metadata and /main/0:
element * CHECKEDOUT
element * .../{taskbranch}/LATEST
element * DEVBASE -mkbranch {taskbranch}
element * .../development/LATEST -mkbranch {taskbranch}
element * LAST_RELEASE -mkbranch development
element * /main/0 -mkbranch development

Again note the last "catch-all" rule. Since newly created elements will not have any existing labels attached to them yet, we need a rule to reflect this possibility. Also notice we are not solely relying on labels for branching; this is important for when new elements are created in the project. Last but not least, we are using the config spec shortcut notation ".../" to automatically match a branch instance if it exists on a file instead of hard-coding the full branch path.

The developers' views using the template above will automatically create the intermediate branches if necessary when they modify a file for their particular task. This is one of the more powerful features of ClearCase branching. Here is an example of a before and after checkout on a file with nested branchnames automatically generated as needed:

For work on the shared development branch itself, the view config spec is a bit more simplified. The view that will integrate all of the development tasks doesn't need to create its own tasks, and its direct parent is the main branch, so it follows the traditional single-level branch template in this case, with the -mkbranch options removed:

development integration view:
element * CHECKEDOUT
element * .../development/LATEST
element * LAST_RELEASE

This integration view does not include a catch-all rule for looking at the main branch since we assume new elements not part of the previous release must have been introduced by a previous task and hence will have a development branch present. If however the development integration view for the development branch were allowed to make changes without assigned tasks, we could easily add the -mkbranch development clause at the appropriate places and introduce a catch-all rule for the main branch. We could also introduce a time rule in our config spec to assure that file changes do not happen during our development integration period.

Over time, our cascading branch strategy might look like this for a shared file:

This method can easily be extended to add other persistent branches which represent different stages in a company's software lifecycle. We could further introduce other metadata such as attributes to incorporate a test or quality assurance phase during the software lifecycle.

Note in the above example that the shared development branch allows changes to be accumulated and tested early as they are ready, before the final merge to the release branch. Development is never halted for say a test build or an integration phase. Merging and resolving changes early and often before a final release while new development is taking place on shared code facilitates quicker release cycles. The final merge in this example to create REL_2.1 of this file was in fact a trival merge. Shorter release cycles can be realized even with a simple braching model when introducing ClearCase to a new project.

With cascading branching, you are better able to model a software lifecycle process with staging areas, at the expense of more administrative overhead and merging. But since the final merges to the main branch are usually trivial with this method, the extra nested branching might be worth it to some customers.

to prune or not to prune

As branches accumulate over time, some administrators want to "clean up" or prune their version trees. There are two different ways to approach branching maintenance so that version trees appear less cluttered. One is by obseleting existing branches (cleartool lock -obsolete), so that the previous history and data still remains within the repository. Most ClearCase reporting commands and GUIs will not display obseleted data by default.

Another method is to actually physically remove old branches using cleartool rmbranch or rmtype -rmall brtype. Some would argue (the author included) that any past actions that modify a company's source code should be retained for historical and audit purposes. Branches take up very little space in the database relative to the entire repository, so not much disk space gain is realized in removing branch types. The vtree GUI allows a user to effectively filter and search on needed data in a version tree. Most reporting commands in ClearCase will not display obseleted data. And when you remove branches, they are gone for good - they can only be later recovered from a previous backup.

So be prudent with your pruning, or better yet don't remove any branches at all! Obseleting old metadata such as branches is a much more civilized way to retire old or obselete work. The trade-off in disk space to preserve your entire branching history is worth it.

summary points

Some points to summarize and wrap up:


Recommended reading and references

ClearCase User's Manual, UNIX/Release 3.2 and later, Chapters 6, 7, 23, 24
ClearCase User's Manual, NT/Release 3.2 and later, Chapters 5, 6, 7, 14, 15

"How Rational Uses ClearGuide and ClearCase", Debra Minard, CCIUG conference 1997

"Is /main/LATEST too dynamic?", Brian White, CCIUG conference 1995

The Problems of Parallel: Overcoming the Obstacles in Team-Based Software Development, PureAtria 1996 (company marketing literature)

cleartool manual pages for mkbrtype, mkbranch, config_spec, mklbtype, mklabel, query_language


feel free to contact the author at fierro@rational.com