Parallel Development with Subversion

advertisement
Parallel Development with Subversion
Basic Branching Strategy
Thomas Engelin
© Itancan Consulting AB 2010
Thomas Engelin, Itancan Consulting AB
1. Abstract
Code development in a large commercial environment often leads to the same source code
being edited simultaneously by several developers. The standard way to deal with this is to use
branches in the version control system. To support work in branches, a process is needed that
sets up rules around this.
This document presents a basic branching strategy when working with Subversion. The
document defines a number of definitions and properties related to branches, such as:
Product delivery model
Branch terminology
Branch lifecycle
Branch types
Branch consistency
Roles and ownership
Push- or pull deliveries between branches
Using these building blocks, the document then presents a branching strategy which consists of
four different levels of branch usage that can be applied depending on development needs.
Finally, Subversion commands to support this branching strategy are presented.
Parallel Development with Subversion - Basic Branching Strategy
1
Thomas Engelin, Itancan Consulting AB
2. The Reason for Parallel Development
By parallel development I mean changes to the same source code is done in more than one
track simultaneously. In practice, it means the same source code is updated by multiple
developers working on different tasks.
Some reasons for having parallel development might be:
The product exists in multiple variants. Perhaps different customers require slightly different
functionality or different release cycles.
It must be possible to stabilize a release (perform bug fixing) while at the same time work
on new functionality for coming releases.
Prototyping work might have to be done that should not affect the normal development
work. Maybe large portions of the code base must be experimented with and version
controlled by a prototyping development team that works independently from the other
development team.
The integration strategy requires that it is possible to select the exact contents of the next
release as late as possible when the gain and risk of each new feature and bug fix is known.
This implies that different development teams finish different tasks in isolated areas, and
that the integration activity means that selected tasks make up the next release.
The project typically wants to have full control over parallel development, and a common way to
deal with it is to use branching within the version control system.
Parallel Development with Subversion - Basic Branching Strategy
2
Thomas Engelin, Itancan Consulting AB
3. Product Delivery Model
When designing a CM- or branching strategy, the requirements come from the company’s
delivery model. A delivery model answers questions like:
How often will we release the product?
How will the releases be named?
Can more than one release be active at the same time?
For how long do we have to do maintenance (bug fixes) on a release?
Besides bug fixes, can there be functionality growth in maintenance work?
Will we support product variants?
Will there be function growth in product variants?
The delivery model can be represented visually:
Quality
3.2
2.2
1.2
3.1
2.1
1.1
3.0
2.0
1.0
Functionality
The product starts from nothing and functionality is added. At some point release 1.0 is created.
If the product delivery model states that there should be no functionality growth in maintenance
work, only quality increasing work (bug fixes) are performed to reach releases 1.1 and 1.2.
On the other hand, if functionality growth is allowed in maintenance work, the graph changes
slightly as in releases 3.1 and 3.2.
The consequences of a product delivery model can be huge. Long maintenance tails (1.1, 1.2,
1.3, 1.4 ... 1.n), different releases being active at the same time (1.0, 2.0, 3.0 ...), growth of
functionality in maintenance work (difficulty to cut maintenance tails), and multiple product
variants (duplicated testing etc.) all tie up valuable resources.
3.1. Integration model
The product delivery model puts requirements on the integration model. Relevant questions are:
How many simultaneous release branches do we need?
When can a release branch be deleted (or hidden)?
Do we need temporary integration branches?
How many levels of branches (branching depth) do we need?
3.2. Branching strategy
The product delivery model and integration model together with the development process finally
dimension the branching strategy, where typical parameters are:
What types of branches do we need, and how long will they live?
What development roles do we have, and how does that affect branch ownership?
Will we enforce consistency rules for branches, and will we use push- or pull deliveries?
We will now take a closer look at these branching strategy parameters, or properties.
Parallel Development with Subversion - Basic Branching Strategy
3
Thomas Engelin, Itancan Consulting AB
4. Branch Properties
4.1. Terminology and branch lifecycle
This section describes the typical lifecycle of a branch, and Subversion terms used when dealing
with branches. Note that this is how branches are mentioned in the Subversion manual (Ref. [1])
but when setting up a branching strategy there might be deviations. For example the number of
syncs and merge backs depends on the branch type and can actually be zero in some cases.
So, this is how branches should be dealt with in theory.
resolve
conflicts
delete or lock
branch
child branch
create
branch
merge
back
sync
parent branch
(might be trunk)
R3
R5
R12
RN
RN+1
4.1.1. Create child branch
At revision R3 a child branch is created from the parent branch (which might be trunk). The
child- and parent branches now have the exact same contents.
4.1.2. Sync and resolve conflicts
Work is done in both parent- and child branch. Normally, the child branch is repeatedly synced
with the contents of the parent branch. Here, revisions R5 and R12 are synced to the child
branch.
If the same code has been updated in both the parent- and child branch, there might be a
conflict. Conflicts are normally resolved in the child-most branch, since we don’t want to freeze
the parent branch during conflict resolution.
4.1.3. Merge back child branch
When work on the child branch is finished, it is merged back to the parent branch. Before merge
back, the child branch is normally synced with revision RN and possible conflicts are resolved
(again in the child branch). The merge back operation results in a new revision. RN+1.
4.1.4. Delete or lock child branch
When the merge back is finished, the child branch can be deleted or locked 1 for additional
commits. The change set between RN and RN+1 records the changes done in the child branch.
4.1.5. Create release
The tags directory in the Subversion repository collects all releases. A release is created directly
from trunk or from a release branch.
1
With a pre-commit hook that triggers on a property, a branch can be set to read-only.
Parallel Development with Subversion - Basic Branching Strategy
4
Thomas Engelin, Itancan Consulting AB
4.2. Branch types
This section describes the purpose of different branch types. Every branch type is associated
with certain type of work and certain roles, and the number of syncs and merge backs differ
between the branch types.
Which branch types that should be part of a branching strategy depends on the needs, such as
the phase in the development cycle the project is in.
4.2.1. Trunk
This is normally the main development line that plays the role of parent branch to the most
other branch types. All code additions should finally be integrated to trunk, either via direct
commits or via merges from other branch types.
4.2.2. Release branch
A release branch provides an isolated work area where bugs can be corrected without
disturbance from ongoing development work, or without disturbing ongoing development work.
A release branch is created from trunk, and new releases (tags) are created from this branch.
While solved bugs are merged back to trunk, the trunk content is never synced to the release
branch! When no more releases or merge backs will be made from the release branch, it can be
deleted or locked.
4.2.3. Feature branch
A feature branch provides an isolated work area for implementing new functionality, and for
sharing that functionality within a smaller team.
A feature branch is created from trunk. Trunk content is repeatedly synced to the feature
branch. The finished work is merged back to trunk, and the feature branch is deleted.
4.2.4. Private branch
A private branch provides an isolated work area for one developer. The purpose can be to add
functionality or to correct a bug.
A private branch is created from trunk or from a release- or feature branch. Content from the
parent branch is repeatedly synced to the private branch. The finished work is merged back to
the parent branch, and the private branch is deleted.
Parallel Development with Subversion - Basic Branching Strategy
5
Thomas Engelin, Itancan Consulting AB
4.3. Branch consistency
This section describes the consistency state a branch can be in. When a branch is in a discrete
state it contains only complete code additions such as a number of complete bug fixes and a
number of complete new features.
The opposite state, continuous, means that the branch contains part of a bug fix or part of a
new feature. This happens when a code addition requires more than one commit before it is
completely delivered to the branch.
4.3.1. Continuous
Additions are made using commits from any kind of work. Any revision on the branch (which
might be trunk) can contain fragments from any ongoing solution.
For example; the revision at t1 contains the complete solution for bug fix BUG-95, but only
parts of feature FuncX. Thus, the branch (or trunk) is in a continuous state at revision R.
R
bug-95
c
c
c
trunk or branch
fea-funcx
c
c
c
If a new release is created from revision R we call it a dirty release, since the customer will get
a complete bug fix plus certain parts of feature FuncX 2.
4.3.2. Discrete
All additions are complete features or complete bug fixes. Every revision on the branch (which
might be trunk) contains a complete step of functionality.
For example; the revision at t1 contains the complete solution for bug fix BUG-95, but no code
from feature FuncX. Thus, the branch (or trunk) is in a discrete state at revision R.
c
c
c
R
bug-95
trunk or branch
fea-funcx
c
c
c
If a new release is created from revision R we call it a clean release, since the customer will get
one complete bug fix and nothing else.
2
When delivering a dirty release such as this, the customer gets no value from feature FuncX - only code
that adds risk! Because the feature is not completely delivered, it is probably not completely tested, so we
actually send away code that adds nothing else besides possibly bugs!
Parallel Development with Subversion - Basic Branching Strategy
6
Thomas Engelin, Itancan Consulting AB
4.4. Roles and ownership
This section describes how branches are owned and managed. Ownership implies authority to
decide about merge operations and branch creations/deletions.
The owner of a branch is in charge of mainly three things:
When to create the branch
What to merge into the branch and when to do it
When/If to delete the branch
By having roles with clear responsibilities, we minimize the risk with child branches that
‘someone’ forgot the merge back to the parent branch etc.
Typical roles:
4.4.1. Integration Leader
The integration leader owns the trunk and all release branches.
4.4.2. Team Leader
A team leader owns feature branches.
4.4.3. Developer
A developer owns his or her private branches.
Parallel Development with Subversion - Basic Branching Strategy
7
Thomas Engelin, Itancan Consulting AB
4.5. Delivery types
This section describes how changes are delivered (merged) between different branches.
4.5.1. Pull delivery
When code additions to the trunk or branch are controlled by an integrator at planned points in
time, we’re talking about pull deliveries.
child
Pull delivery
- Integration plan
- Owner
parent
Pull deliveries require process overhead. The work is done in isolated child branches, and an
integrator pulls in finished branches planned for the current release. The parent branch owner
decides when to merge back a child branch, and performs the merge.
What branches to merge back to the parent branch, in what order, and when to do it is decided
by the parent branch owner.
In the picture below the integrator is about to create a new release. With pull deliveries we gain
very high flexibility. Bug-95 is integrated first, then bug-88. After that fea-funcx is integrated.
The release is now complete. The new feature fea-funcy will become part of the next release –
maybe it has not been tested enough or it is not requested by the customer right now.
bug-88
bug-95
fea-funcy
fea-funcx
The integrator can apply tests between every piece that gets integrated, rollback additions, redo
the integration and so on.
Parallel Development with Subversion - Basic Branching Strategy
8
Thomas Engelin, Itancan Consulting AB
4.5.2. Push delivery
When code additions to the trunk or branch are done directly by developers at any point in time,
we’re talking about push deliveries.
child
Push delivery
- Asynchronous
- Non-owner
parent
Push deliveries require almost no process overhead at all. The drawback is that there is no
control over what gets added to the trunk or branch.
When using push deliveries, the child branch owner decides to merge back the child branch to
the parent branch, and also performs the merge.
Child branches are merged back to the parent branch in any order and at any point in time.
Parallel Development with Subversion - Basic Branching Strategy
9
Thomas Engelin, Itancan Consulting AB
5. Branching Strategy
The branching strategy presented in this section uses the branch properties described in the
previous section. The strategy consists of four ‘maturity levels’ where an increase in level
indicates a higher degree of branch usage. Note that maturity level N is not in any way better
than maturity level N-1, both probably have their place in the development process.
5.1. Maturity level 0
Commits are made directly against trunk, no branches are used. All work is mixed on trunk;
release work and new features. At certain points, feature freeze is decided and a tag is created
from a specific revision.
tags
R
R
trunk
c
c
c
c
c
c
This is probably a useful level when starting a new project and the code growth is high and only
internal test releases are in sight. The need for branches is very low and would not motivate the
branching overhead cost. Later in the project a shift to a higher maturity level is probably
needed.
The release precision is low since the branch consistency is continuous (dirty releases), more or
less anything might slip into a release. It might be hard to pick the best revision for a release.
Corrections for a release will enter the next release together with new features.
Since we have no branches, there is no issue with push- or pull deliveries.
Traceability is low; to know what went in to a release requires some detective work (reading
commit logs). It is impossible to select the exact contents of a release at integration time and
use planned and delayed integration3.
3
That is, to be able to pick the desired bug fixes and new features that should go into the release after they
have been completed.
Parallel Development with Subversion - Basic Branching Strategy
10
Thomas Engelin, Itancan Consulting AB
5.2. Maturity level 1
The trunk is used for new functionality and release branches are used for release stabilization
work. Commits are made directly against the trunk and the release branches. A release branch
results in one (x.0) or multiple (x.0, x.1, x.2...) tags.
When a new release is to take place, a feature freeze on trunk is decided and the release branch
is created where bugs can be corrected.
Before every new tag made from a release branch, code freeze is decided and the tag is created.
After every created tag, bug fixes are merged back to trunk. Note that there are no sync
operations from trunk to the release branch since we don’t want to pollute the release branch
with new functionality intended for the next release!
When the release branch is no longer needed, it is deleted or locked.
tags
R
c
R
c
c
c
c
release branch
trunk
c
c
c
c
c
c
This level is useful when there is a need to have control over releases but at the same time be
able to develop new features without too much branch overhead.
The release precision is still low since the branch consistency is continuous (dirty releases).
It might still be hard to pick the best revision for a release branch; however, corrections on a
release can be done separately from the development of new features!
The trunk owner pulls in bug fixes from the release branch. We might even have several release
branches in parallel if that is needed according to the product delivery model.
Traceability is still low; to know what went in to a release branch requires reading commit logs.
It is not possible to pick out the exact contents for a release and use planned and delayed
integration.
Parallel Development with Subversion - Basic Branching Strategy
11
Thomas Engelin, Itancan Consulting AB
5.3. Maturity level 2
Feature branches are used for new functionality, and release branches are used for release
stabilization work. Commits are made directly against feature- and release branches, but never
directly against trunk!
When new functionality is to be implemented, a feature branch is created where a development
team can work in isolation from other development teams implementing other functionality or
stabilizing releases.
New functionality is added to the feature branch by the development team. As soon as new
code gets merged into trunk (from other feature branches or from release branches), a sync is
done to keep the feature branch as synced as possible with trunk. When the feature is finished
it is merged back to trunk and a complete step of functionality has been added. The feature
branch is then deleted.
tags
R
c
c
R
c
c
c
release branch
trunk
feature branch
c
c
c
At this level all additions to trunk are complete features or bug fixes. The trunk is always in a
discrete state where clean releases can be made from. This level gives the project lead good
control over what gets released, but more importantly it is now possible to delay the integration
of finished features and bug fixes until it is time to create the next release.
The trunk owner pulls in finished features and bug fixes according to an integration plan, and it
is now possible to mix the contributions in different order, to apply tests at certain points in the
integration work, and to rollback additions that actually don’t fit into the release.
Traceability is high; we know what went in to every release branch without detective work.
Parallel Development with Subversion - Basic Branching Strategy
12
Thomas Engelin, Itancan Consulting AB
5.4. Maturity level 3
Feature- and release branches are used exactly as in level 2, but now all commits are done on
private branches! Every developer working on a task creates a private branch for that task.
A task might be a bug to solve on a release branch, or a well-defined piece of functionality on a
feature branch. In some cases a private branch could be created directly on the trunk if there is
a need for a small code addition (however, to keep the branch model as homogenous as
possible it is better to create a feature branch for that purpose).
Event though it sometimes might be overkill to use private branches on a feature branch, it is
highly recommended to solve every bug in its own private branch. If the product has variants
more than one logical ‘trunk’ for the product might exist, and to solve the same bug in several
product variants is a lot easier if every bug solution can be located in its own private branch.
tags
R
c
c
R
c
c
c
private branch
release branch
trunk
c
c
feature branch
private branch
c
c
At this level, bug fixes and pieces of new functionality are pushed from a private branch to the
parent branch by the responsible developer. The trunk owner pulls in bug fixes from release
branches and new functionality from feature branches.
Every revision on trunk and on release- or feature branches denotes a complete step in
functionality – these branches are now always in a discrete consistent state.
Parallel Development with Subversion - Basic Branching Strategy
13
Thomas Engelin, Itancan Consulting AB
5.5. Mixed maturity levels
In reality a development team might start out working on level 0. When the need for more
controlled releases appear they might start to use release branches. To keep track of solved
bugs, every bug is associated with its own private branch. For large refactoring work or
prototypes the development team sometimes creates a feature branch, but most of the new
functionality is committed to trunk directly.
This is actually a mix of all four levels. The use of branches depends on the needs, so the
project lead should beforehand define what branch types to use and when to use them.
The four levels presented can be seen as templates to base your own branching strategy on.
5.6. Multiple ‘trunks’
Sometimes a branch is used to hold a product variant, that is, a configuration of the product
that should live and be maintained for a long time. Such a configuration branch can be seen as
a second ‘trunk’; it is never deleted, and never merged back to its parent branch.
It is possible to develop and maintain a configuration branch regardless of maturity level.
However, in order to have control over what gets merged between the base product and the
product variant, maturity level 3 will make things a lot easier.
A common case is when a bug fix has been implemented, for example in the base product, and
needs to be transferred to the product variant.
If the bug fix was made in a private branch, then the complete solution (with no other solution
fragments) can be found between two revision numbers after it has been merged back to the
parent branch on the source side (base product).
In such case it is possible to transfer the bug fix to the product variant in two ways:
Use cherry-picking to merge the exact revision from source to destination
Use normal sync- and merge back mechanisms between parent and child to transfer the
bug fix to the destination
Either way, on the destination side a branch could be created where the bug fix is received.
Conflict resolution could take place in this branch before it is merged into the configuration
branch.
5.7. Branch naming conventions
When different branch types are used it might be wise to apply a naming convention for
branches such as this:
Branch type
<type>
<identifier>
Example
Release branch
rel
Intended release ID
rel-1.0
Feature branch
fea
Feature name
fea-funcx
Private bug fix branch
bug
Bug tag
bug-95
Private feature branch
prv
Developer ID + sequence
prv-thomas-02
Configuration branch
cfg
Product variant name
cfg-customerx
Parallel Development with Subversion - Basic Branching Strategy
14
Thomas Engelin, Itancan Consulting AB
6. Subversion Commands
This section shows the Subversion commands used when dealing with the branch types
described in this document.
6.1. Assumptions
$REPO is the URL to a repository containing the directories branches, tags and trunk.
Before any sync or merge back operation, no local changes should exist in the workspace.
Subversion is configured to use a good diff/merge tool (in $HOME/.subversion/config).
6.2. Release branches
Release branches are never synced, but possibly merged back several times. When the release
branch is no longer needed, it can be deleted or locked.
Create a new release branch from trunk, revision 200:
svn cp $REPO/trunk@200 $REPO/branches/rel-1.0 -m "Created rel-1.0"
Create a release from the release branch:
svn cp $REPO/branches/rel-1.0 $REPO/tags/rel-1.0 -m "Release rel-1.0"
Create another release from the same release branch:
svn cp $REPO/branches/rel-1.0 $REPO/tags/rel-1.1 -m "Release rel-1.1"
Merge back the release branch:
cd <top directory in working area for trunk>
svn update
svn merge $REPO/branches/rel-1.0
... resolve conflicts ...
svn ci -m "Merged back rel-1.0"
Lock the release branch:
cd <top directory in working area for release branch>
svn propset branch:locked TRUE .
svn ci –m "Locked rel-1.0"
Delete the release branch:
svn delete $REPO/branches/rel-1.0 -m "Deleted rel-1.0"
6.3. Feature branches
Feature branches are synced multiple times but only merged back only once. After the merge
back, the feature branch is deleted.
Create a new feature branch from trunk, revision HEAD:
svn cp $REPO/trunk@HEAD $REPO/branches/fea-funcx -m "Created fea-funcx"
Parallel Development with Subversion - Basic Branching Strategy
15
Thomas Engelin, Itancan Consulting AB
Sync the feature branch with trunk contents:
cd <top directory in working area for feature branch>
svn update
svn merge $REPO/trunk
... resolve conflicts ...
svn ci -m "Synced"
Merge back the feature branch:
cd <top directory in working area for trunk>
svn update
svn merge --reintegrate $REPO/branches/fea-funcx
... resolve conflicts ...
svn ci -m "Merged back fea-funcx"
Delete the feature branch:
svn delete $REPO/branches/fea-funcx -m "Deleted fea-funcx"
6.4. Private branches
Private branches are synced multiple times but only merged back once. After the merge back,
the private branch is deleted.
Create a new private bug fix branch from a release branch, revision 243:
svn cp $REPO/branches/rel-1.0@243 $REPO/branches/bug-95 -m "Created bug-95"
Sync the private bug fix branch with the release branch contents, revision HEAD:
cd <top directory in working area for private bug fix branch>
svn update
svn merge $REPO/branches/rel-1.0@HEAD
... resolve conflicts ...
svn ci -m "Synced"
Merge back the private bug fix branch:
cd <top directory in working area for release branch>
svn update
svn merge --reintegrate $REPO/branches/bug-95
... resolve conflicts ...
svn ci -m "Merged back bug-95"
Delete the private bug fix branch:
svn delete $REPO/branches/bug-95 -m "Deleted bug -95"
6.5. Conflict resolution
File conflicts are resolved using a diff/merge tool. Structural conflicts (tree conflicts) are
resolved in the following manner. To see the current tree conflicts
cd <top directory of working area>
svn update
svn status
M
.
!
C dirA/file1
>
local missing, incoming edit upon merge
The first step is to investigate how the structure should look after resolved the conflicts. Also,
the affected files must be investigated so their contents will be correct. When that is known,
update the working area to have the desired structure and file contents. Then run
svn resolve --accept=working dirA/file1
Resolved conflicted state of 'dirA/file1'
When all conflicts have been resolved, the changes can be committed.
Parallel Development with Subversion - Basic Branching Strategy
16
Thomas Engelin, Itancan Consulting AB
7. References
[1]
Subversion manual
http://svnbook.red-bean.com/en/1.5/svn-book.pdf
Parallel Development with Subversion - Basic Branching Strategy
17
Download