Versioning and Branching With CVS


Table of Contents

Getting started with CVS

CVS is in the gnu bin directory:
/usr/local/gnu/bin .
Make sure you have this directory in your path. Also, make sure that you set the CVSROOT environment variable. Not doing this will make most commands fail.
setenv CVSROOT /usr/lsrc/impulse/dist/imp-sim
The man pages are in the gnu man page directory (add that to your MANPATH).
/usr/local/gnu/man

Using CVS

The latest greatest IMP-SIM source tree resides in the CVS trunk. The trunk is the version of our files that are checked out by default, ie. no branch is specified. But in general, we never get code (checkout) direct from the trunk.

IMP-SIM development always takes place on branches from the trunk to allow concurrent development. Each developer starts out with a private copy(branch) of the source tree. A branch can be modified without interfering with any other branch. At regular intervals all the work in the branches is merged together to form a new trunk.

Making your branch

The first step in this development environment is creating your own branch of code on which to work. Remember to set the environment variable CVSROOT to /usr/lsrc/impulse/dist/imp-sim . Create a branch with a command following this form
	cvs rtag -r imp-4 -b i4-retrac spin 
i4-retrac will be the name of this branch (often the name is, confusingly, called a tag in the cvs docs). This is the naming convention for branches
	iVersionNumber-User 
Getting your code tree is a seperate step from creating your branch. This command checks out a tree in the subdir imp-sim in the current directory.
	cvs checkout -r i4-retrac imp-sim
This is your own tree you can freely change. Each file is backed by an RCS file in CVSROOT. To restore a file to its checked out condition, remove it and restore it with update.
	rm file
	cvs update file
Branches must be created from the new trunk after every merge.

Tags versus Branches

Most cvs operations have a "-r rev" option where rev can be either a branch or a tag. Both branches and tags represent RCS labels stored in the RCS headers in the repository RCS files. It is CVS, not RCS, that intreprets the labels to mean tags or branches.

A branch is a lineage created by cvs tag -b file
A tag is a freeze marked by cvs tag file

Use 'cvs stat -v file' to examine the branches and tags for "file" stat -v shows these labels for src/mmc/mmc_maint.c:

	i4-retrac                       (branch: 1.3.2)
        release-4                       (revision: 1.3)
        r3-merge                        (revision: 1.3)
        release-3                       (revision: 1.1.1.2)
        release-2                       (revision: 1.1.1.1)
        release-1                       (revision: 1.1.1.1)
        origin-branch                   (branch: 1.1.1)
						.
					       /|\
						|
				"revision:" is a tag.  "branch:" is a branch.
Checkout by tag release-3 will retrieve version 1.1.1.2
	cvs checkout -r release-3 src/mmc/mmc_main.c
Checkout from branch i4-retrac will retrieve 1.3.2.x where x is the latest version on that branch.
	cvs checkout -r i4-retrac src/mmc/mmc_main.c 

Distributing code from your branch

As frequently as every two days, we may merge the branches into a new trunk. To contribute your code to the trunk, or to distribute it to other branches, it must be commited and tagged.

You may commit to the RCS files in CVSROOT as often as you want. cvs commit corresponds to the RCS ci command. A commit is a good way to preserve a file before experimenting with a radical change. You can always restore previous versions using cvs update as described above.

In your working directory, commit files with a command of this form

	cvs commit -m "log message" files/dirs
You can commit individual files or entire directories. Directories are committed recursively unless you use the -R flag. The message goes into the $Log: index.html,v $ The message goes into the Revision 1.1 1998/11/16 18:27:45 lizhang The message goes into the Initial revision The message goes into the area and RCS log of each file commited. If the message is omitted, commit will start an editor so you can enter the log message. Read log messages with cvs log file.

In the top direcotry, make a tag with a command of this form

	cvs tag i4-retrac-jun1 imp-sim
The tag command puts a label in every RCS file to identify the revision that corresponds with the file in your branch.

When your branch is committed and tagged, advertise that tag to the merger or others interested in picking up your code.

What update and checkout do

Update and checkout (alias get) are often interchangeable, hence confusing. They will modify your tree, but never ever delete files that are modified. Modified (uncommitted) files are reported as "M filename" and are not deleted.

cvs update -r branch will replace your branch with branch adding and deleting files to make the tree look like branch.

cvs update -j branch will merge the diffs between branch and the trunk into your tree. Previously modified files are affectd as well as untouched parts of your branch. No files will be added or deleted. When branch has new files, you must add those files to your branch with tag -b and update -r as usual.

if you want your file to be updated from the trunk type cvs upd . if you want to update it and move it back from your current branch to the trunk type cvs upd -A . you do not have to remove the file to do it.

Shortcuts

The IMP-SIM tree is large so whole tree commands can take several minutes. To save time, you can apply the commands discussed so far to just the code subtrees your are interested in, such as only sal and sys. In particular the gdb part of the tree is very large and often untouched.

One option for major subtrees, is to branch and checkout everything, then rm -rf subtree. Then cvs commands will ignore that subtree.

Working on subtrees can speed up cvs ops but there are caveats. Be sure you, and anyone you hand tags to, knows you are not working on a complete tree.

Doing cvs operations after the fact on parts of the tree you did not expect to change can be very time consuming and frustrating. A good way to stay aware of the CVS status of your branch is with this command

	cvs update -q dirs
The update command will report files in dirs that cvs does not know about and report the modifications that have not been commited. The list it generates displays these code characters next to the filenames.
M Modified and not commited.
A Added and not commited.
R Removed and not commited.
? New unknown file. Possibly needs to be added.

Adding new files

To add a new file to the cvs system, first create the file. Then add it to the trunk with this sequence
	create file
	cvs add file
	cvs update -A file
	cvs commit file
And add the new file to your branch
	cvs tag -b i4-retrac file
	cvs update -r i4-retrac file
Other people can add the new file to their branch the same way
	cvs rtag -b i4-wilson imp-sim/path/file
	cvs update -r i4-wilson file

Adding new directories

Add a new directory with these commands
	mkdir dir
	cvs add dir
	cvs update -A dir

Removing obsolete files

Remove a file from future versions of the trunk with these commands
	cvs update -A file
	rm file
	cvs remove file
	cvs commit file
The file remains in CVSROOT so older versions of the trunk can be recreated. It also remains in the current branches until the next branch off the next trunk. To remove files from current branches, use this sequence
	cvs tag -d i4-retrac file
	cvs update file

Reorganizing the tree

This is CVS's major weakness. Renaming files or directories, or moving files around in the tree can be done, but is a pain, and is not for the faint of heart. At merge time, when no branches exist, in CVSROOT copy the RCS files from the old name or location to the new name. Use the normal cvs remove commands to delete the file with old name.

Merging Tags into Your Branch

Merging is best left to the merger who creates new trunks, but on occasion you just can't wait. It is important to keep track of what tags you have merged into your branch and tell the merger what tags you have picked up. The simplest scenario is where you never distributed a tag or merged any tags into your branch. More complex scenarios require careful tracking of who has merged what where. File adds and removes must also be tracked by hand.

Suppose you want to merge i4-retrac-jun1 into your branch. Further you know that i4-retrac-jun1 has had no other tags merged into it and you have not merged anything else into your branch. Merge with a command of this form

	cvs update -j imp-4 -j i4-retrac-jun1 imp-sim
The update -j (for join) applies the diffs it finds between imp-4 and i4-retrac-jun1 to all the files in the spin tree. The actual merging is done with rcsmerge and diff3.

Now suppose i4-retrac-jun15 is announced and you need to again merge it into your branch. Merge with a command of this form

	cvs update -j i4-retrac-jun1 -j i4-retrac-jun15 imp-sim
This will merge into your code only the differences between the June 1 tag and the June 15 tag. This avoids applying the imp-4 to jun1 mods yet again. Those would be duplicate changes/conflicts and you would have to go edit the conflicts out.

When you have modified a patch of code yourself and then merge someone elses change to that same code, a conflict results the automatic tools cannot resolve. Both versions of that patch of code will appear in the merged file marked with banner lines like this

	<<<<<<< filename
	your existing version of code segment
	=======
	version form the tag being merged (tag happens to be version 1.7 of filename)
	>>>>>>> 1.7
Search for the banner lines with grep and resolve them by hand.

Weirdness

Ctrl-C
CVS uses the system(3) call to run the RCS commands so Ctrl-C often only kills the subprogram instead of CVS. Use Ctrl-Z to stop it and then kill CVS (kill %).
Path names
Some commands only work on the current directory (add and release). Sometimes you have to give the whole path (mk/kernel/alpha/trap.c) or can work with partial paths (alpha/trap.c) if a CVS subdir already exists the current directory.
Current branch
CVS's idea of the current branch for a particular file is affected by several things. If it thinks all the files in a dir belong to a single branch, new operations will default to that branch. If it sees a mixture or no files at all, the default is the head of the main trunk. The "sticky tag" is the default rev for the -r flag on all the CVS operations. Update -r sets the sticky tag, which is reported by stat -v.

lizhang@cs.utah.edu
Last updated October 29, 1998