One of the most helpful yet underused facilities of CVS is the
tag
.
CVS’s tagging feature allows you to label a revision
for later retrieval. This feature also allows you to fork development
so that you can work on two or more versions of your project
simultaneously. The version that has been forked off is called a
branch
,
and the main line of development is called the
trunk
.
This chapter explains tags, branches, and how to merge branches and trunks. It also discusses why and when to branch and provides strategies and hints for using branches effectively in your project.
CVS allows you to retrieve any checked-in revision of a file. While retrieving revisions of individual files is useful, it’s even more useful to be able to retrieve the compatible set of revisions that make up a complete, functional version of a project, such as all the revisions that become release 1.0 of a program, or that become the first edition of a book.
Tagging is a way of marking a group of file revisions as belonging together. You can’t use the revision numbers for this purpose, because revision 2.3 of one file might belong with revision 2.17 of another. Figure 4-1 shows a group of file revision numbers, with a string denoting which revision numbers belong to the same release.
CVS allows you to create a single tag that denotes all the file revisions that connect to that virtual string. When you want to look at all the file revisions that belong to a tag, CVS can “pull the string” to locate the tagged revisions. Figure 4-2 shows the same group of files, with the string pulled tight to show which set of revisions belong to that tag.
A tag can be used to mark a specific revision of a single file or a specific set of revisions of a group of files—in essence, naming the string. The tag then gives you a convenient way to retrieve that revision or matched set of revisions. Remembering the significance of a text string is much easier than remembering a version number. Tags are often used to record the version number used by the developers, rather than the CVS revision number, which is used primarily as a CVS internal designation.
Tag
names must start with a letter and can contain alphanumeric
characters,
hyphens (-), and
underscores (_). Each tag must be unique
within the tagged file. You tag files with the cvs
tag
and
cvs
rtag
commands, explained in Section 4.1.1 and Section 4.1.2 of this chapter.
I
recommend that you use meaningful tag names. Tag names should
immediately tell you something about the revisions they tag and, if
they tag across several files, why those revisions belong together.
For example, release-1-3-beta
,
release-2-13-patch-5
,
testing-1-5-alpha
, and
release-2-5-stable
are all effective tag names.
Set a standard tag-name format for your project and encourage your developers to use it. Use names that describe versions of the project, using the version-naming convention that your developers are familiar with. Create tag names that your team can use to choose revisions, rather than allowing them to rely on the CVS revision numbers. The CVS numbers are intended to be internal tools for CVS’s use and can be difficult for humans to relate to stages of the project.
Tip
Section 7.3.7 in Chapter 7 includes the start of a script that can be used to enforce tag-name standards automatically.
There are two reserved tag names. CVS uses the
BASE
tag name for the revision that was last
synchronized with the repository. CVS uses the
HEAD
tag name for the most recent revision in
the repository.
If you and your coworker both check out revision 1.23 of the
main.c
file, you both start with a
BASE
of 1.23. If your coworker commits twice,
the BASE
revision for your sandbox is still
1.23, because you haven’t synchronized with the
changes in the repository. The HEAD
revision is
now 1.25, because your coworker’s two commits were
given revision numbers 1.24 and 1.25. The BASE
revision for your coworker’s sandbox has become
1.25, because his sandbox copy of main.c
was
synchronized to revision 1.25 when he committed his second change.
Use
the
cvs
tag
command to tag the files in the current sandbox
directory and all subdirectories. By default, cvs
tag
adds the tag to the BASE
revision. You can specify files to tag by providing their filenames
as an argument to cvs tag
.
The syntax for the cvs tag
command is:
cvs [cvs-options
] tag [command-options
]tagname
[filenames
]
cvs tag
determines which files and revisions to
mark based on your sandbox, but it marks them based on the revision
that was most recently synchronized with the repository. If changes
have occured in the sandbox since the files were last synchronized
with the repository, those changes will not be
reflected in the tagged revisions.
The
-c
command option
to cvs tag
allows you to check whether your
sandbox files have been modified and not committed before you tag the
files. If cvs tag -c
finds uncommitted changes,
it will stop without tagging any files. If you want to tag the
revision in the repository, without the uncommitted changes, omit the
-c
and rerun the cvs tag
command. If you want to tag the revision in the sandbox, commit your
changes before rerunning cvs tag
.
You can use dates, existing tags, or revision numbers to determine
which revisions to tag. Use the
-r
revision
or
-r tagname
options to cvs
tag
to specify a revision or an existing tag, and use the
-D date
option to specify a date. If you’re using a date,
CVS will tag the latest revision before the date you specify. See
Chapter 11 for more information on dates.
The -f
option
can be used only in combination with -r
or
-D
. This option instructs CVS to use the
HEAD
revision if no revision can be found to
match the revision specified by -r
or
-D
.
By default, cvs
tag
acts
recursively down the sandbox subdirectories. The
-l
option restricts it to the local directory. You can
also use -R
to explicitly instruct CVS to act
recursively, should you feel the need to be that explicit.
Example 4-1 shows how to use cvs
tag
to tag the files in the current sandbox directory with
the tagname pre_alpha_0-1
.
Example 4-1. Using cvs tag
bash-2.05a$ cvs tag pre_alpha_0-1
cvs server: Tagging .
T Changelog
T INSTALL
T Makefile
T README
T TODO
cvs server: Tagging doc
cvs server: Tagging doc/design
T doc/design/AcceptanceTest.doc
T doc/design/Analysis.rtf
T doc/design/Requirements.doc
T doc/design/Specification.rtf
cvs server: Tagging doc/plan
T doc/plan/Schedule.rtf
cvs server: Tagging lib
cvs server: Tagging man
cvs server: Tagging src
T src/config.h
T src/main.c
The cvs
rtag
command allows you to tag files without referring to
a specific sandbox. Instead of using the sandbox to determine which
revisions of which files to tag, rtag
relies on
the parameters to the command. You must use either the
-r
or -D
options to specify
which revision of the files in question to tag, and you must specify
at least one directory name, filename, or module name. Modules are
explained in Chapter 7. If you specify multiple
directories, files, or modules, separate them with spaces.
The syntax for the
cvs
rtag
command is:
cvs [cvs-options
] rtagcommand-options tagname filenames
Example 4-2 shows the cvs rtag
command being used to apply the pre_alpha_0-2
tag to all files within the wizzard
directory
and its subdirectories. The -r HEAD
option
specifies that the pre_alpha_0-2
tag be applied
to the HEAD
revision of all files.
Example 4-2. Using cvs rtag
bash-2.05a$ cvs -d cvs:/var/lib/cvs rtag -r HEAD pre_alpha_0-2 wizzard
cvs rtag: Tagging wizzard
cvs rtag: Tagging wizzard/doc
cvs rtag: Tagging wizzard/doc/design
cvs rtag: Tagging wizzard/doc/plan
cvs rtag: Tagging wizzard/lib
cvs rtag: Tagging wizzard/man
cvs rtag: Tagging wizzard/src
If you are in a sandbox when you use the cvs
rtag
command, CVS uses the repository referenced in that
sandbox’s CVS
directory as the
repository to search for the files to be tagged. If you are in a
sandbox that is connected to a repository other than the one you want
to act on, leave the sandbox or use the -d
repository_path
CVS option as I’ve
done in Example 4-2.
If your current working directory is not a sandbox, you can specify
the repository with either the CVSROOT
environment variable on the client machine or the -d
repository_path
CVS option.
When you want to tag the current revision of any file in the
repository, use -r HEAD
. Be aware
that CVS operations are not atomic, so if someone commits while you
are tagging and you use -r HEAD
, you may find
that one directory has been tagged at the point before your
coworker’s commit and another has been tagged after
it.
When using the -D
option,
be aware that, unless a time has been specified, CVS tags the most
recent revision at midnight on the day in question. This means that
if you use -D 12 Feb 2002
, CVS tags the file
revisions as they were at 12:00 A.M. on 12 February 2002, local time.
Date formats are listed in Chapter 11.
Most of the options to cvs tag
can be used the
same way with cvs rtag
. The
-l
and -R
options control
recursion, and the -r
, -D,
and -f
options specify revisions as they do with
cvs tag
. The -c
option to
cvs tag
is not used with cvs
rtag
.
To list the tags on a file, use
cvs status -v
in a sandbox that includes the file.
This command also provides information, such as the current sandbox
(or working
) revision, the current repository
revision, and any sticky information in the current sandbox. The tags
are listed at the bottom of the report. You may note that some tags
have the word “branch” beside the
revision number; these are the tags at the base of a branch,
explained in Section 4.3
later in this chapter. Example 4-3 shows the
use of cvs status
to show tags for
main.c
.
Example 4-3. Listing file tags
bash-2.05a$ cvs status -v src/main.c
= = = = = = = = = = = = = = = = = = = = = = = = = = = = =
File: main.c Status: Up-to-date
Working revision: 1.9
Repository revision: 1.9 /var/lib/cvs/wizzard/src/main.c,v
Sticky Tag: (none)
Sticky Date: (none)
Sticky Options: (none)
Existing Tags:
pre_alpha_0-2 (revision: 1.9)
pre_alpha_0-1 (revision: 1.9)
To retrieve a tagged file or set of files, use the
-r
tagname
option to cvs
checkout
or cvs update
. Use
checkout
to create a new sandbox, and use
update
to modify an existing sandbox. If you
retrieve a set of tagged files into an existing sandbox, any existing
files will be overwritten with the tagged revisions, but changes you
have made since the files were last synchronized with the sandbox
will be merged forward into the new revisions. Example 4-4 shows a checkout of a tagged
sandbox.
Example 4-4. Checking out a tagged sandbox
bash-2.05a$ cvs -d cvs:/var/lib/cvs checkout -r pre_alpha_0-2 wizzard
cvs server: Updating wizzard
U wizzard/Changelog
U wizzard/INSTALL
U wizzard/Makefile
U wizzard/README
U wizzard/TODO
cvs server: Updating wizzard/doc
cvs server: Updating wizzard/doc/design
U wizzard/doc/design/AcceptanceTest.doc
U wizzard/doc/design/Analysis.rtf
U wizzard/doc/design/Requirements.doc
U wizzard/doc/design/Specification.rtf
cvs server: Updating wizzard/doc/plan
U wizzard/doc/plan/Schedule.rtf
cvs server: Updating wizzard/lib
cvs server: Updating wizzard/man
cvs server: Updating wizzard/src
U wizzard/src/config.h
U wizzard/src/main.c
When you check out or update a sandbox using a nonbranch tag or a
date (branches are explained later in this chapter), the tag or date
is sticky
on the files in that sandbox. A sandbox
checked out with a date or a nonbranch tag is a static representation
of the project at that point. You cannot commit changes to a file
checked out as static. Stickiness applies only to the sandbox copy of
a file and does not affect the repository. See Section 4.2 of this chapter for
more details.
Normally, tags are intended to remain fixed, to mark a specific moment in time. Sometimes, you do need to remove, rename, or move a tag. Do this with caution, as these actions may discard historical information and may be impossible to undo.
There are special tags called branch
tags
, explained in Section 4.3 of this chapter. If
you try to remove or move a branch tag, CVS returns an error message
and will not delete or move the tag, though you can force CVS to
remove or move the branch tag with the -B
option.
Warning
Do not delete, move, or rename a branch tag without an extremely good reason and a very recent backup of the repository, as doing so can cause data loss.
There usually is no need to remove a correctly placed tag from a file. However, if you make an error when tagging, you may want to remove the tag and try again.
To remove a
tag, use the -d
option:
cvs tag -dtagname
[filename
]
or:
cvs rtag -dtagname
filename
If you use the rtag
command outside a sandbox,
you need to specify the repository path. If you use
rtag inside a sandbox, CVS searches the
CVS
subdirectory to determine the repository.
The
tag
command must be used within a sandbox, and by default acts on the
files in the current sandbox directory and its subdirectories. CVS
searches the CVS
subdirectory to determine the
repository.
Example 4-5 shows the use of cvs
tag
to remove a tag. The user is in the top level of the
project’s sandbox.
The most common reason to move a tag is to correct a tagging mistake. Some project teams also like to have a mobile tag that marks the most recent version that is ready for release or the current bugfix version of the project, and they move that tag when they finish a new version.
To move a tag from one revision to another revision in the same file
or set of files, use the -F
option to
cvs
tag
and
cvs
rtag
. Use
-r
to designate the revision to move the tag to
and -F
to designate the tag to move. Example 4-6 shows the use of cvs rtag
to move a tag, from within a sandbox. The status report for the file
is shown before and after the move. Because I am using
rtag
rather than tag
, I
need to specify the full path from the repository root directory to
main.c
, including the project name. I
don’t need to specify the full path with
cvs status,
because I’m in the
sandbox.
Example 4-6. Moving a tag
bash-2.05a$cvs status -v src/main.c
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = File: main.c Status: Up-to-date Working revision: 1.9 Repository revision: 1.9 /var/lib/cvs/wizzard/src/main.c,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) Existing Tags: pre_alpha_0-1 (revision: 1.9) bash-2.05a$cvs rtag -r 1.8 -F pre_alpha_0-1 wizzard/src/main.c
bash-2.05a$cvs status -v src/main.c
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = File: main.c Status: Up-to-date Working revision: 1.9 Repository revision: 1.9 /var/lib/cvs/wizzard/src/main.c,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) Existing Tags: pre_alpha_0-1 (revision: 1.8)
Files that have been removed from the main trunk using cvs
remove
, or that
were never on the trunk, are stored in an
Attic
subdirectory in the repository. These
files may be associated with old tags that should be removed, moved,
or renamed. There is no easy way to perform these tasks for files in
the Attic
directory using cvs
tag
, but cvs rtag
provides the
-a
option, which applies -d
and -F
to tags in removed files
(i.e., in the Attic
) from the appropriate module
or directory.
If you are using the -r revision
option with
tag
or rtag
, CVS searches
Attic
files to determine whether the revision
existed in those files. The -a
option is
unnecessary if -r
is specified.
If you or one of your development team has added a tag that does not conform to your tag-name standards, or that is inaccurately named, you can rename it.
CVS does not include a command to rename a tag, but the -r
option to tag
and
rtag
makes it easy to add a new tag to the
revisions that were tagged with an existing tag. Then you can remove
the old tag. (Do not try to use this approach with branches.)
Example 4-7 shows how to rename a tag. The goal is
to rename pre_alpha_0-1
to
pre_beta_0-1
. First, cvs tag
-r
is used to tag all the
pre_alpha_0-1
files with
pre_beta_0-1
. Next, the unwanted
pre_alpha_0-1
tag is deleted via a cvs
tag -d
command. The effect is the same as renaming
pre_alpha_0-1
to
pre_beta_0-1
.
Example 4-7. Renaming a tag
bash-2.05a$cvs tag -r pre_alpha_0-1 pre_beta_0-1
cvs server: Tagging . T Changelog T INSTALL T Makefile . . . cvs server: Tagging src T src/config.h T src/main.c bash-2.05a$cvs tag -d pre_alpha_0-1
cvs server: Untagging . D Changelog D INSTALL D Makefile . . . cvs server: Untagging src D src/config.h D src/main.c
If a file has been removed from the project in the revisions you’re tagging, the file will not be tagged. If the file is not added again, this won’t matter.
If a file has been removed and then added again, there is no simple
way to show whether the tag doesn’t exist in that
file because the tag was created between the remove and the second
addition, or because the tag is older than the file. You can use
dates to determine which is the case, or you can issue the command
cvs
rdiff -s -r tagname project
. The -s
option to rdiff provides a summary report that
lists files that have been changed, added, or removed.
To tag a removed file as well as existing files, use the
-r
option to cvs tag
and
cvs rtag
. Using -r HEAD
is
typical.
If you are tagging against the HEAD
, you may
want to find a way to prevent others from changing the repository
between the time you decide the files are ready to be tagged and the
time you actually tag them. Some suggestions for doing this are
included in Section 6.7.1
of Chapter 6.
Tagging makes it easier to retrieve snapshots of a project. The basic rule is to tag every time you reach a significant stage of a project. At an absolute minimum, tag every time you branch and tag on completion of each release of a project.
Devise your own in-house tagging strategy. The following list of times to consider tagging is heavily biased toward programmers:
On completion of each major feature
At each milestone or each major phase of a project
Just before dropping an existing feature
Just before testing begins
Before making changes that might break working code
Just before splitting off a branch
Just after merging a branch
Use meaningful tag names in a fixed format, including all the essential information in the tag name. This is one possible, but very detailed, format for tag names:
version
-[alpha-|beta-][test-|final-|patch-][patch#-
][pub|priv]
When you need to check out an older version of the code to test it or create a patch, you need an easy way to identify the exact version you’re after. This tag-name format lists the version number, whether the tagged release is a test or final release, the release’s stage of testing, and whether it is an internal or external release.
Remember, this format is just an example. Use your own format, based on your own project team’s needs. Most project teams prefer a shorter format than the one shown here.
Get Essential CVS now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.