home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam    

  Previous section   Next section

6.8 Editing a Repository

There are times when you need to restructure a project or when something has gone wrong in the repository. In an ideal world, restructures or repairs would all be done using the CVS commands explained in Chapter 3 or the cvs admin command explained in Chapter 7. Sometimes, however, those commands can't do what you're trying to achieve, and you need to modify the repository directly.

The main problem with editing the repository directly is that you may lose historic data. Removing files or directories affects retrieval of older releases of a project and may affect build scripts. Moving or renaming files or directories can affect older releases, build scripts, and instructions within your files.

Always freeze a repository with write locks before hand-editing it.

Always consider backing up and freezing the directories you're hand-editing. If you're just removing a stale lock, this isn't necessary. But you should backup and freeze directories if you're moving, removing, editing, or renaming content files. Try to get your users to commit and release their sandboxes before you hand-edit the repository. They can checkout new sandboxes afterwards.

If a user does not release an old sandbox and tries to act on a filename that the sandbox has records of, but the repository does not have the file, the user will receive strange errors. Correct these errors by releasing the user's sandbox copy of the file in question and then checking out the file again using the new filename.

If you are using the scripting files in the CVSROOT directory, you may need to edit them when a directory is moved or removed.

6.8.1 Moving Files and Directories

CVS does not have a built-in command for moving or renaming a file or directory. This doesn't mean that you can't move or rename files and directories; it just requires ingenuity.

To rename a file or directory, move it from the old name to the new name.

As shown in Chapter 3, the simplest way to move a file involves using cvs remove and cvs add on the sandbox copy of the file. This method is simple, retains the history of the file, and allows mistakes to be corrected easily. The revision numbering for the file starts over at 1.1, but if you find that bothersome, you can change the revision numbering with cvs commit -r revision filename. If this method for moving or renaming a file doesn't suit your needs, you can edit the repository.

There are two ways to move a file by editing the repository and two ways to move a directory. Directories can't be moved easily with CVS commands, as they can't be deleted without editing the repository. Chapter 3 explains how to get the effect of moving a directory without losing historic information.

6.8.1.1 Prerequisites to moving a file

To move a file by editing the repository directly, you need to move all the file's data. Most of this data is stored in the file itself, but if the file is being watched or is flagged for editing with cvs edit, there may be data about it in the fileattr file in the CVS subdirectory of the directory your file is stored in.

If there is data in fileattr about the file you want to move, the record for that file needs to be cleared before you move or remove the file. It is best to clear this record with CVS commands by having users use cvs release or cvs unedit, and cvs watch remove and then using cvs watch off on the file. If you can't clear information for a file out of fileattr with CVS commands, edit fileattr directly and remove all lines with that file's filename. Then, copy the removed lines to the fileattr file in the new location.

6.8.1.2 Moving a file: Method 1

The first method for moving a file is simply to move the file:

  1. Have users cvs release their sandboxes.

  2. Freeze the repository directory that contains the file and the directory it will be moved to, to prevent people from reading or writing to them.

  3. Remove any information about the file from the fileattr file in the same directory. Save this information, if there is any, for step 6.

  4. Move the file with Unix's mv command (e.g., mv old,v new,v).

  5. Change any build scripts or other files that refer to the old name.

  6. Restore fileattr data in the new location.

  7. Unfreeze the repository and allow users to check out sandboxes.

This method for moving a file doesn't affect the file's revision numbering, and it allows the old directory to be removed if all the files are taken out of it in this way.

However, this method damages the historic record for the file being moved. When old revisions of the file or old releases of the project are checked out, the file will be retrieved with its new name. There will also be no record of when the name was changed and why.

6.8.1.3 Moving a file: Method 2

This method involves copying the file, then using CVS commands to move the old copy to the Attic as a dead revision, and finally removing tags from the new copy. Removing the tags ensures that retrieving old revisions by tag works as expected retrieving the old filename, not the new one.

  1. Freeze at least part of the repository (for writing) in a way that allows you to access it as a client, possibly from a sandbox on the same machine as the repository. You need to freeze only the directory that contains the file you want to move and the directory it will be moved to. Because you need to use a sandbox, it may be easier to limit access to the server effectively freezing the whole repository than to use write locks on the affected directories.

  2. Make sure you have an active sandbox that contains the file you want to move.

  3. In the repository, copy the file using the Unix cp command (e.g., cp old,v new,v). You can now remove the lock on the old directory.

  4. Copy the relevant line of the fileattr file from the file's old location to its new location.

  5. In the sandbox, remove the old file with cvs remove old. Commit this change with a message that you've moved the file to the new filename.

  6. In the sandbox, retrieve the new file with cvs update new.

  7. Remove any nonbranch tags from the new file with cvs tag -d tagname new. Use cvs status -v new to get the tagnames. This works because CVS stores tags inside the repository copy of project files.

  8. Merge the file to other active branches, if necessary.

  9. Change any build scripts or other files that refer to the old name.

  10. Unfreeze the repository and allow users to resume work.

This method has the following advantages:

This method has a few problems. The move is recorded only in the commit message in the old file, and retrieving old revisions by date retrieves old revisions with the new filename as well as the old one.

Another issue with this method involves branch tags. Removing the branch tags prevents old branch revisions from appearing under the new name, which is desirable but also means that the new file is not on the branch.

You can remove the old branch tags from the new file to prevent old revisions from being retrieved incorrectly, then add the new file to the branch with cvs add to put the file back onto the branch. But because of the way CVS stores branched files, this may corrupt the file if some of the information in the new file can't be retrieved properly. I recommend that you do not do this, as the data can be difficult to recover if you change your mind.

If you need to move a branched file, I recommend you use method 1 to move it, or omit the step of removing the tags if you use method 2. And if you do remove branch tags, make a backup first.

Example 6-9 and Example 6-10 show how to move a file with this method. Example 6-9 shows the command to copy the file in the repository, and Example 6-10 shows the commands used in the sandbox.

Example 6-9. Moving files with method 2, repository view
/var/lib/cvs/wizzard/src$ cp main.c,v wizzard.c,v
/var/lib/cvs/wizzard/src$
Example 6-10. Moving files with method 2, sandbox view
bash-2.05a$ cvs remove -f main.c
cvs server: scheduling `main.c' for removal
cvs server: use 'cvs commit' to remove this file permanently
bash-2.05a$ cvs update wizzard.c
U wizzard.c
bash-2.05a$ cvs status -v wizzard.c
=  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =  =
File: wizzard.c           Status: Up-to-date
   
   Working revision:    1.8
   Repository revision: 1.8    /var/lib/cvs/wizzard/src/wizzard.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)
   
   Existing Tags:
   pre_beta_0-1         (revision: 1.8)
   
bash-2.05a$ cvs tag -d pre_beta_0-1 wizzard.c
D wizzard.c
bash-2.05a$ cvs commit -m "Moved main.c to wizzard.c"
cvs commit: Examining .
Removing main.c; 9L, 316C written
/var/lib/cvs/wizzard/src/main.c,v  <--  main.c
new revision: delete; previous revision: 1.8
done
bash-2.05a$ cvs commit -f -m "Moved main.c to wizzard.c" wizzard.c
Checking in wizzard.c;
/var/lib/cvs/wizzard/src/wizzard.c,v  <--  wizzard.c
new revision: 1.9; previous revision: 1.9
done
6.8.1.4 Moving a directory: Method 1

Moving a directory within the repository damages the historic record for the project. When old releases of the project are checked out, the directory and the files it contains are retrieved with the new name. There is no record of when the name was changed and why. To move a directory:

  1. Have users cvs release their sandboxes.

  2. Freeze the repository directory to be moved, any subdirectories, and the parent directory, to prevent people from reading from or writing to them.

  3. Edit any of the scripting files in the CVSROOT directory of the repository that refer to the directory you are moving. The most likely of these files is modules. CVS does not edit the scripting files itself, so you need to check only files that you or others with write access to the CVSROOT directory have changed.

  4. Move the directory with mv old new.

  5. Correct any build scripts or other files that refer to the old name.

  6. Unfreeze the repository and allow users to check out new sandboxes.

6.8.1.5 Moving a directory: Method 2

This alternate method of moving a directory preserves the old structure of the project, which can allow older build scripts to continue to work on old versions of the project. This method leaves the old directory in place, available for new files to be added to that directory. You may not always want this result.

  1. Follow steps 1-3 in method 1.

  2. Move the directory with cp old new.

  3. Correct any build scripts or other files that refer to the old name.

  4. Unfreeze the repository enough to allow you to create a single sandbox with permissions to the old directory.

  5. From that sandbox, remove all the files in the old directory with cvs remove.

  6. Unfreeze the repository and allow users to check out new sandboxes.

6.8.2 Deleting Files and Directories

Before deleting any files or directories manually, have your users cvs commit and cvs release their sandboxes. They will get odd errors if they try to commit or work with files and directories that no longer exist in the repository.

6.8.2.1 Deleting a file

When you delete a file with the cvs remove command, CVS sets its state to dead (see Section 6.8.4). If the revision you deleted was the head revision of the trunk, CVS stores the file in an Attic subdirectory of its original directory.

To remove a file from the repository entirely:

  1. Have users cvs release their sandboxes.

  2. Freeze the affected repository directory, to prevent people from reading from or writing to it.

  3. Check whether the file is recorded in the fileattr file and remove the reference if it is. (See Section 6.8.1 earlier in this chapter.)

  4. Remove the file with the Unix rm command.

  5. Unfreeze the repository and allow users to check out new sandboxes.

6.8.2.2 Deleting a directory

CVS does not provide any way to delete a directory with CVS commands, because removing a directory necessitates removing its Attic subdirectory. Even if the directory is empty of active files, if it has ever contained files it will have an Attic subdirectory, which needs to exist to allow old revisions of files or old releases of the project to be retrieved.

If you will not need to retrieve any files stored in a directory's Attic subdirectory, you can delete the directory in the repository with the following method:

  1. Have users cvs release their sandboxes.

  2. Freeze the repository directory that contains the directory to be removed, to prevent people from reading from or writing to it.

  3. Move any project files as shown earlier in Section 6.8.1. Use the cp method (method 2) if you want to retain old revisions; use either method if you don't.

  4. Delete any lock files, the Attic subdirectory, and the CVS subdirectory with rm or rmdir.

  5. Delete the directory with rm or rmdir.

  6. Unfreeze the repository and allow users to check out new sandboxes.

6.8.3 Deleting a Project

A project can be deleted by deleting its directory, as described in the previous section. However, a project directory is more likely to have entries in the scripting files in the CVSROOT directory. The most critical scripting file to check is modules, as this file defines module names that can be used as parameters to most CVS commands.

Ensure that all users have commited and released their project sandboxes before you delete the project. If they attempt to work on a project that has been removed, they will get interesting error messages.

6.8.4 Editing a Project's RCS Files

The project files in the repository are stored in Revision Control System (RCS) format. Applying RCS and SCCS (O'Reilly) by Don Bolinger and Tan Bronson explains RCS in detail. The RCS format is also explained in the CVS source-code document RCSFILES (in the doc directory) and in the Unix and Linux manual page man 5 rcsfile.

If you think you need to edit the repository copy of a project file in order to solve a problem, always try to use the cvs admin commands to fix the problem first. These commands include most of the functions RCS provides for managing an RCS-format file. The cvs admin commands are explained in Chapter 7.

CVS edits an RCS file by editing a copy of the file, then replacing the existing RCS file with the edited copy. Lock the repository directory; then follow this same procedure if you are editing a file manually. You can edit an RCS file with any plain-text editor.

Example 6-11 shows the header of an RCS file.

Example 6-11. RCS file header
head  1.2;
access;
symbols
  beta_0-1_branch:1.1.0.2
  beta_0-1_branch_root:1.1
  pre_beta_0-1:1.1;
locks; strict;
comment @ * @;

The header contains metadata about the file, including the head (most recent) revision of the trunk, the revisions and names for each tag, the RCS lock status (always strict for CVS files), and a comment field bounded with @ symbols.

After the header, the RCS file contains revision metadata for each revision in the file. This metadata includes the revision number, date, author, state, any branches that use that revision as their base node, and the next revision. The next revision is the revision that is one revision older than the current revision on the same branch (or trunk) of the file. After all the revision metadata, the RCS file has a space to store a file description. The file description is bounded by @ symbols.

Example 6-12 shows RCS revision metadata. Note that the branch revision 1.1.2.1 is in the dead state, which means that the revision has been removed with cvs remove. The description of this file is empty, but the @ symbols that would bound a description are present on the line under desc.

Example 6-12. RCS revision metadata
1.2
date  2002.09.13.07.26.27;  author jenn;  state Exp;
branches;
next  1.1;
   
1.1
date  2002.09.12.08.56.41;  author jenn;  state Exp;
branches
  1.1.2.1;
next  ;
   
1.1.2.1
date  2002.09.13.07.42.06;  author jenn;  state dead;
branches;
next  ;
   
desc
@@

The rest of the RCS file contains the file content the actual text of the file and the log messages. The head revision contains the bulk of the file content. Every other trunk revision contains only the differences between that revision and the revision that is one revision newer than it. These differences are stored in the same format used by the diff and patch programs.

Trunk revisions are stored in reverse order. To retrieve any trunk revision, you start with the contents of the head revision and recursively apply the patches stored with each revision to those contents. Work backward chronologically until you reach the desired revision. The next field in the revision metadata tells you which revision's patches should be applied in the next recursion.

Branch revisions are slightly different. If your desired revision is on a branch, you start with the most current revision of the trunk and work backward to the branch point, then work forward through branch revisions.

Revision 1.1 in Example 6-13 contains the code d12 3, which means delete 3 lines starting at line 12.

Each revision starts with the revision number, then the log message (preceded by log and bounded by @ symbols), then the revision text or diff (bounded by @ symbols). If an @ is required inside a section, it is escaped with another @. An email address looks like this: jenn@@nosuch.com.

Example 6-13. RCS file body
1.2
log
@Minor tweaks to the config file.
@
text
@/*
 * Wizzard.h
 * Sep 12 2002
 * Developer: Jenn Vesperman (jenn@@nosuch.com)
 *
 * Headers, macros and constants file for the Wizzard project.
 *
 */
   
#include "config.h"   /* using autoconf */
#include "options.h"  /* manual options that can't be done in autoconf */
   
#define TRUE 1
#define FALSE 0
@
   
1.1
log
@Moving src/wizzard.h to src/config.h
@
text
@d12 3
@

6.8.5 Clearing Locks

When a CVS client process is waiting on a lock in a repository, the client displays messages such as those shown in Example 6-14.

Example 6-14. Waiting for a lock
cvs server: [23:30:43] waiting for jenn's lock in /var/lib/cvs/wizzard/src
cvs server: [23:31:13] waiting for jenn's lock in /var/lib/cvs/wizzard/src
cvs server: [23:31:43] waiting for jenn's lock in /var/lib/cvs/wizzard/src

The process waits 30 seconds between tries. If the process is still waiting for a commit after an unusually long time, you may need to check whether the other user's CVS process has crashed and (if so) remove the lock manually. In CVS 1.11.3 and later, CVS provides the time in UTC. In earlier versions, the time is given in the server's time zone.

The simplest way to determine whether a process has crashed is to check with the user running the client program. If they're uploading large files over a slow link, wait a little longer.

If you can't get in touch with the user directly, you can check the status of the user's process on the repository server by using whatever commands are appropriate on your system (for example, ps -ef on many Unix systems). CVS puts the process ID in the name of the lock file. Check whether that process is still alive and functioning, and see if it is a CVS process. If the process has died or if some other program has taken the process ID, you can remove the lock safely by removing the lock file.

To remove a lock manually, check for a lock file or files in the directory given in the error message. There may be a lock file and a master directory belonging to the user's process. The lock file or files will be the #cvs.lock directory or files whose names start with #cvs.rfl or #cvs.wfl. Remove these files to remove the lock.

In Example 6-15, I have already determined that the process 20233 on the server named nest belongs to a crashed CVS client, so #cvs.wfl.nest.20233 is redundant. The master lock #cvs.lock is owned by the same user and also belongs to the crashed client. Example 6-15 shows how to remove the lock files.

Example 6-16 shows the CVS output to the client once the lock files are cleared.

Example 6-15. Clearing a lock, server view
jenn@nest:/var/lib/cvs/wizzard/src$ ls
#cvs.lock  #cvs.wfl.nest.20233   main.c,v   wizzard.h,v
jenn@nest:/var/lib/cvs/wizzard/src$ rm *cvs.wfl.nest.20233
jenn@nest:/var/lib/cvs/wizzard/src$ rmdir *cvs.lock
Example 6-16. Clearing a lock, client view
cvs server: [23:33:13] obtained lock in /var/lib/cvs/wizzard/src
RCS file: /var/lib/cvs/wizzard/src/main.c,v
done
.
.
.
done
bash-2.05a$

  Previous section   Next section
Top