Chapter 4. Tagging and Branching

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.

Tagging

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.

File revisions that belong together

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.

Tagged file revisions

Figure 4-2. Tagged file revisions

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.

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 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

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.

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.

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, 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.

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 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.

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]

or:

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 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.

Example 4-5. Removing tags

bash-2.05a$ cvs tag -d pre_alpha_0-2        
cvs server: Untagging .
cvs server: Untagging doc
cvs server: Untagging doc/design
cvs server: Untagging doc/plan
cvs server: Untagging src

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

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)

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.

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

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

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.

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 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:

  • 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.