Branches in CVS
CVS allows you to isolate changes onto a separate line of
development, known as a branch. When you change files on a branch, those
changes do not appear on the main trunk or other branches.
Later you can move changes from one branch to main trunk (or from main
trunk to the branch) by merging. Merging involves first running cvs
update -j, to merge the changes into the working directory. You can then
commit that revision, and thus effectively copy the changes
onto another branch.
When to create a new branch
For NetBeans/Forte I see two possible reasons for creating a new branch:
- Official branch for a new release. Such a branch will be created by
an experienced person who knows what he is doing and therefore it
might be expected that there won't be any problem.
- Private branch(es) created by a developer. Such branches are
typically used for unstable code that will be merged into the main
trunk as soon as it will became stable. This document presents
some recommendations that should be followed when someone will create
a private branch.
Recommendations for working with branches
- Read carefully CVS documentation. If you have any
problem, don't hesitate to ask someone who can help you.
- Do not use any CVS expandable keywords ($Id$, $Log$, etc.), as these
interact poorly with merges. For example, suppose that you have used
$Revision$ in your code. And that you have two versions of the file
with revisions 1.2, 1.1.2.1. Then you might get the following results
from a merge:
<<<<<<< file1
key $Revision: 1.2 $
=======
key $Revision: 1.1.2.1 $
>>>>>>> 1.1.2.1
If you *really* need to use CVS keywords, please, consult CVS
documentation or (better) ask someone who knows how to do it.
- Make a tag (cvs tag) whenever you do anything at all interesting
(esp. before and after any merge); it never hurts and it may save you
later. Remember that tags are global, so choose them descriptively so
they will not conflict. For example unstable_rmi_merged_on_Sep_6
can be used.
It may be useful for example if you perform more sequential merges.
Suppose you have an branch R1fix and you have completed the first step
of your unstable development. You want to merge it into the main trunk
and then continue development in unstable branch. After some time you
will repeat merging, etc.
+-----+ +-----+ +-----+ +-----+ +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk
+-----+ +-----+ +-----+ +-----+ +-----+
! *
! * (merge)
! *
! +---------+ +---------+ +---------+
Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 !
+---------+ +---------+ +---------+
If you just use the cvs update -j R1fix m.c command again,
CVS will attempt to merge again the changes which you have already
merged, which can have undesirable side effects.
If you tag R1fix branch after every merge into the trunk, it is
possible to use the new tag in merge command and merge only what was
changed since the last merge.
- When you make modifications and check them in, change as few lines as
possible (e.g. do not reindent the whole file just for fun), and don't
mix unrelated changes in a single commit; these will make merging go
more smoothly and reduce the chance of error.
- Use branches if you need to do some unstable development - it is good
for you because you can store all your versions and CVS server is
probably backed up, it is good for other people because your code
doesn't break builds. But do not use a lot of local unstable branches,
because they may be hard to keep track of. If you need to do some
unstable development for a while and need a branch, then use one.
How to work with branches
- create a new branch
-
cvs tag new_branch_name_root
cvs tag -b -r new_branch_name_root new_branch_name
it creates a tag for main trunk (before the merge) ant then it
creates a branch based on the current revisions in the working
copy, assigning that branch the name `new_branch_name'.
Example:
cvs tag rmi_root
cvs tag -b -r rmi_root unstable_rmi
creates a new unstable branch for rmi module to main trunk
-
cvs tag -r some_existing_branch new_branch_root
cvs tag -b -r new_branch_root new_branch_name
-r new_branch_root says that this branch should be rooted at
the revision that corresponds to the tag new_branch_root. It need not be
the most recent revision -- it's often useful to split a branch off
an old revision (for example, when fixing a bug in a past release
otherwise known to be stable).
Example:
cvs tag -r boston boston_rmi
cvs tag -b -r boston_rmi unstable_rmi_boston
creates a new unstable branch for rmi based on current boston
branch
Note: difference between tag and rtag is that
tag command adds a tag to checked out version of files
whereas rtag uses the most recent versions in the repository.
These can have different effects, e.g. if someone checks in new
stuff while you are working.
- access to an existing branch
-
cvs checkout -r branch_name
Example:
cvs checkout -r boston
-
cvs update [-r branch_name]
the above command switches to the specified branch name - it merges
any changes.
Note: do not use update -r. It is potentially very
confusing (although it does do exactly what it says, usually you
don't really want to do that). Always check out branches into
separate, clearly labeled directories. For example, ~/src/boston for
boston branch, ~/src/jaga for main trunk, etc. Make a fresh checkout of
the whole repository, with most stuff on the trunk and just your
unstable code on the branch.
Example:
cd unstable_rmi
cvs checkout nbsrc
cvs checkout -r unstable_rmi rmi
...
cvs update
- merging
-
cvs tag before_merge
cvs update -j branch_name test.java
cvs rtag -r branch_name branch_name_merged_on_date
cvs commit -m proper message"
cvs tag after_merge
merges all changes on branch to the main trunk, tags it and commit
to CVS. It is also possible to used revision numbers instead of
tags but tags makes work with merging more smooth.
-
cvs tag before_merge_on_date
cvs update -j branch_name_merged_on_date -j branch_name
cvs rtag -r branch_name branch_name_merged_on_date
cvs commit -m proper message"
cvs tag after_merge_on_date
merges all changes since specified merge to the main trunk,
tags it and commit to CVS.
Example:
cd jaga/rmi
cvs tag before_merge_Sep_10
cvs update -j unstable_rmi_merged_on_Sep_6 -j unstable_rmi
cvs rtag -r unstable_rmi unstable_rmi_merged_on_Sep_10
cvs commit -m "merged a new feature"
cvs tag after_merge_Sep_10
It is also possible to merge main trunk into a branch. This is
conceptually probably harder, but follows the same principles.
Suppose an example that you are working on unstable branch and
someone else has been checked some fixes into the main trunk. You
want to integrate these fixes into your code too but not to
affect the main trunk with your unstable code and later to merge your
unstable code to the main trunk.
Example:
## create an unstable branch
cvs rtag unstable_root my_module
cvs rtag -b -r unstable_root unstable my_module
mkdir unstable
cd unstable
cvs checkout -r unstable my_module
## some work in the branch whereas someone else has fixed trunk
## merge the trunk to the branch
cvs rtag main_trunk_fixed my_module
cvs tag unstable_before_merging_main_trunk_fixed
cvs update -j unstable_root -j main_trunk_fixed
[resolve conflicts and commit]
cvs tag unstable_after_merging_main_trunk_fixed
## some work in both main trunk and the branch
## finally, merge the branch to the trunk
cd stable
cvs checkout my_module
cvs tag before_merge_unstable
cvs update -j main_trunk_fixed -j unstable
[resolve conflicts and commit]
cvs tag after_merge_unstable
Suppose the following example:
/- 1 | 2 | M1 | 5 (branch)
root
\- 3 | 4 | 6 | 7 | M2 (trunk)
Numbers are changes (revisions) made by user. Suppose that every
revision is stored as a diff to previous revision.
M1 is a result of merging the trunk (rev. 4) to the branch.
M2 is a result of merging the branch (rev. 5)to the trunk.
So current status at M1 is root+1+2+3+4, status at 5 is
root+1+2+3+4+5 and status at 7 is root+3+4+6+7.
Finally M2 is created (cvs update -j 4 -j branch) as <status at 7>
+ (<status at 5> - <status at 4>) = root+3+4+6+7 +
(root+1+2+3+4+5 -(root+3+4)) = root+3+4+6+7 + (root+1+2+3+4+5 - root-3-4))
= root+3+4+6+7 + (+1+2+5) = root+1+2+3+4+5+6+7
In practice, hovewer, update -j -j literally takes the two revisions you
asked for, and more or less runs diff3 on them and your working file.
It does not know or care what changes you made where--that is your job.
cvs update always runs a single merge, it does not do anything in steps.
The example above show how one can check whether result of merging would
be what he wants.
|
What is CVS?
Sources & Branches
NetBeans 5.0 CVS
CVS Checkout Instructions
CVS Check-in Instructions
|