CVS allows you to retrieve any checked-in revision of a file. Although 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. This ability is also important for compliance with standards and regulations.
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 an imaginary string denoting which revision numbers belong to the same release.
Figure 4-1. File revisions that belong together
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.
Figure 4-2. Tagged file revisions
A tag can mark a specific revision of a single file or a specific set of revisions of a group of filesin 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, not to mention a handful of different version numbers for each file. 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.
4.1.1. Tag Names
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 "Tagging by Sandbox" and "Tagging by Date or Revision" later in 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. Assign names that describe versions of the project, using the version-naming convention that your developers are familiar with. Create tag names from which your team can identify revisions, rather than allowing them to rely on the CVS revision numbers. The CVS numbers are intended for internal reference by CVS, and do not map to the way that people typically number software releases.
4.1.2. Reserved Tag Names
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 of the trunk 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.
4.1.3. Tagging by Sandbox
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 occurred 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 of 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 tags 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.
Example 4-1 shows how to use cvs tag to tag the files in the current sandbox directory with the tag name pre_alpha_0-1. Figure 4-3 shows tagging in the graphic client Cervisia.
Example 4-1. Using cvs tag
Figure 4-3. Tagging with Cervisia
4.1.4. Tagging by Date or Revision
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] rtag command-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 (the HEAD is the most recent revision on the trunk; see "Reserved Tag Names," earlier in this chapter). Figure 4-4 shows tagging in TkCVS.
Example 4-2. Using cvs rtag
Figure 4-4. Tagging in TkCVS
If you are in a sandbox when you use the cvs rtag command, CVS can use 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 using the cd command 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 most recent 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. To avoid this problem, tag the sandbox with cvs tag, or use a date. In some cases, you can also use -r with a preexisting tag name (such as when creating a branch, explained later in this chapter).
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.
4.1.5. Retrieving Tagged Files
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, as explained in "Branching" later in this chapter. Example 4-3 shows the use of cvs status to show tags for main.c. Figure 4-5 shows the tag list in TkCVS.
Example 4-3. Listing file tags
Figure 4-5. Listing file tags in TkCVS
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 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, and Figure 4-6 shows checking out a tagged sandbox in Cervisia.
Example 4-4. Checking out a tagged sandbox
Figure 4-6. Checking out a tagged sandbox in Cervisia
When you check out or update a sandbox via 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 that was checked out as static. Stickiness applies only to the sandbox copy of a file and does not affect the repository. See the "Stickiness" section later in this chapter for more details.
4.1.6. Removing and Moving Tags
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 "Branching," later in this chapter. If you try to remove or move a branch tag, CVS returns an error message and does not delete or move the tag, though you can force CVS to remove or move the branch tag with the -B option.
188.8.131.52. Removing a tag
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 -d tagname [filename]
cvs rtag -d tagname 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 issued from 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. Figure 4-7 shows tag removal in Cervisia.
Example 4-5. Removing tags
Figure 4-7. Removing a tag in Cervisia
184.108.40.206. Moving a tag
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
220.127.116.11. Moving or removing tags from Attic files
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.
18.104.22.168. Renaming a tag
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
4.1.7. Removed Files
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, as this refers to the most recent revision (of the trunk) in the repository.
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 "Freezing a Repository" in Chapter 6.
4.1.8. Tagging Strategies
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:
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:
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.