CVS Branch and Tag Primer
Jeff Semke
Pittsburgh Supercomputing Center
4/21/98
In attempting to construct a CVS repository containing multiple versions of
original NetBSD source distributions, as well as our own custom modifications
to each of these, I found that the CVS man page was vague and unclear.
This document assumes general familiarity with the CVS system, including the
concepts of modules, the repository, and basic checkout and commit operations.
Branches and tags are very different things, though they appear similar
on the outside. Understanding the difference is the key to being able
to support a multi-branched repository that can change as our custom code
is modified for all versions of NetBSD.
The main concepts that are addressed in this document are:
- Tree structure
- Code in a CVS repository can be thought of as a tree. The tree shape
is not based on the directory structure of the code, but rather on the
versions of the code.
- Trunk
- As code gets modified and is committed back to the repository, it
adds another revision to extend the tree. If no branches are specified
when checking out working directories, the tree simply grows in a line
along the main trunk of the tree.
- Tags
- A tag can be used to name a particular revision of the code. This
tag can later be used to compare different revisions, to go back to an
earlier version, or to specify a place to create a branch.
- Branches
- Branches allow different development paths to be tried. They can
be created from the main trunk, or from other branches.
1. The trunk of the tree
As the code gets modified, each revision extends the trunk of the tree.
The trunk itself is named only by the module name, not by tags or
branches. You can get a copy of the latest revision of the main trunk
to work on with the command
This creates a working directory called my_module, and it contains
the files and subdirectories of my_module. After you make your
changes, you enter them into a new revision on the main trunk with the
command
2. Branches
2.1 Creating a Branch
Branches can be added to the repository tree in order to allow different
development paths to be tried, or to add parallel development of code
to different base versions. To create a branch, you can use
or
with the -b option.
Do not be fooled! Even though the same commands can be
used to create tags, branches are completely
different than tags.
2.1.1 rtag
To create a branch from the main trunk of my_module at the
revision that was last committed, use the command
cvs rtag -b Branchname my_module
To create a branch from a tagged revision of my_module,
use the command
cvs rtag -r Tagname -b Branchname my_module
Both commands immediately create a branch in the repository
without requiring a cvs commit to
enact. You do not need to be in a checked-out working directory
to do this.
2.1.2 tag
If you are in a working directory, you can create a new branch
in the branch or trunk from which you checked out your
working directory (not including the changes you've made to
your working directory since the last commit) by using the
command
Like rtag, the change is immediate, and does not wait for
a commit command.
Your working directory remains on the old branch, at a point after
the branch you just created. To move your working directory to the
new branch, use the command
When you are finished making changes to your working directory, save
it as the next revision on the new branch with
2.1.3 Both rtag and tag
Note that both rtag and tag work
directly on the repository and take effect immediately without a
commit command. Rtag takes effect at the specified
place (the end of the main trunk by default), while tag
takes effect at the place where the working directory was checked
out or last committed.
2.2 Checking Out a Branch to Work On
To check out the latest revision of a branch to work on, use the
command
cvs checkout -r Branchname my_module
When you do a cvs commit, the changes are merged in on
the branch from which they were checked out.
2.3 General
Note that branch names refer to the latest revision of the branch
they are on, not the state they were in when the branch was
created.
3. Tags
A tag provides a means to name a certain revision of the code, or
take a "snapshot" of it at a certain point in time. This tag
only applies a named handle to the code at that point in time, it
does not create a new branch of code.
3.1 Creating a Tag
In order to name the current end of the main trunk of a module,
use the command
cvs rtag Tagname my_module
In order to name the current end of a branch of a module, use
the command
cvs rtag -r Branchname Tagname my_module
Otherwise, to name the code that your working directory was
checked out from (without the changes you made to your
working directory since the last commit), use the command
3.2 Using a Tagged version of code
You can check out tagged versions of code with the command
cvs checkout -r Tagname my_module
This creates a working directory with the code as it was when the
tag was created. While branch names refer to the latest code at
the end of a branch (and as such, are dynamic), tag names refer
to the static version of code that existed upon the tag's creation.
As a result, you cannot commit changes back into the tree at the
tagged place that you checked them out from.
If desired, a new branch can be created at the place the tag
was applied, then changes can be committed into the new branch as
follows:
cvs rtag -r Tagname NewBranchname my_module
cvs update -r NewBranchname
Finish changes to the working directory, then
cvs commit
To access this version of the code again later (along with any
changes that were made by others since you last accessed it), use
cvs checkout -r NewBranchname my_module
4. Merging branches
4.1 Using Update
cvs update -j TagOrBranch1 -j TagOrBranch2 my_module
The above command will locate the differences between TagOrBranch1
and TagOrBranch2. Of the lines that are different between them,
the lines in TagOrBranch2 will be patched, or merged, into the
sources in your working directory.
An annoying problem that I have not yet solved is that new files that
appear in TagOrBranch2 but not in TagOrBranch1 do not
get created by the merge. The only thing I know of to get these files
into the new version is to checkout TagOrBranch2, copy the files
into the merged working directory, and do
4.2 Using Checkout
cvs checkout -j TagOrBranch1 -j TagOrBranch2 my_module
The above command will locate the differences between TagOrBranch1
and TagOrBranch2. Of the lines that are different between them,
the lines in TagOrBranch2 will be patched, or merged, into the
latest revision on the main trunk of my_module.
In order to have these differences merged into a different branch, and
then have that branch checked out, use
cvs checkout -r BranchToMergeTo -j TagOrBranch1
-j TagOrBranch2 my_module
Like update, file that were created between TagOrBranch1
and TagOrBranch2 do not get created automatically.