6.9. Editing a Repository
There are times when you need to restructure a project or fix things up 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 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 back up 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.9.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 means that doing so requires ingenuity.
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.
126.96.36.199. 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 commandsby having users use cvs release or cvs unedit, and cvs watch removeand then use 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 copy the lines with that file's filenames to the fileattr file in the new location. If you're renaming the file but keeping it in the same directory, replace the old name with the new name.
188.8.131.52. Moving a file: Method 1
The first method for moving a file is to move the file:
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 is retrieved with its new name. There will also be no record of when the name was changed and why.
184.108.40.206. 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 expectedretrieving the old filename, not the new one.
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 approach 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.
Examples 6-11 and 6-12 show how to move a file with this method. Example 6-11 shows the command to copy the file in the repository, and Example 6-12 shows the commands used in the sandbox.
Example 6-11. Moving files with method 2, repository view
Example 6-12. Moving files with method 2, sandbox view
220.127.116.11. 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:
18.104.22.168. Moving a directory: Method 2
This alternative 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.
6.9.2. Deleting Files and Directories
Before deleting any files or directories manually, have your users run cvs commit and cvs release on 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.
22.214.171.124. Deleting a file
When you delete a file with the cvs remove command, CVS sets its state to dead (see "Editing a Project's RCS Files," later in this chapter). 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:
126.96.36.199. 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:
6.9.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 committed 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.9.4. Editing a Project's RCS Files
The project files in the repository are stored in Revision Control System (RCS) format and are easily recognized by the ,v at the end of the filename. Applying RCS and SCCS, by Don Bolinger and Tan Bronson (O'Reilly), 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-13 shows the header of an RCS file.
Example 6-13. RCS file header
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-14 shows RCS revision metadata. Note that the branch revision 188.8.131.52 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-14. RCS revision metadata
The rest of the RCS file contains the file contentthe 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, CVS starts with the contents of the head revision and recursively applies the patches stored with each revision to those contents. CVS then works backward chronologically until it reaches the desired revision. The next field in the revision metadata tells CVS which revision's patches should be applied in the next recursion.
Branch revisions are slightly different. If your desired revision is on a branch, CVS starts with the most current revision of the trunk and works backward to the branch point, then works forward through branch revisions.
Revision 1.1 in Example 6-15 contains the code d12 3, which means delete 3 lines starting at line 12.
Example 6-15. RCS file body
Each revision starts with the revision number, then the log message (preceded by log and bounded by at symbols (@)), then the revision text or diff (bounded by at symbols). If an @ is required inside a section, it is escaped with another @. An email address looks like this: jenn@@nosuch.com.
6.9.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-16.
Example 6-16. Waiting for a lock
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 he's 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 or ps -auwx 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-17, I determine 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-17 shows how to remove the lock files.
Example 6-17. Clearing a lock, server view
Example 6-18 shows the CVS output to the client that was waiting for the lock, once the lock files are cleared.
Example 6-18. Clearing a lock, client view