Section 6.9. Editing a Repository


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

To rename a file or directory, move it from the old name to the new name. To create a new project from parts of an older project, move the files into a directory in the repository root directory, or move a whole subdirectory into the repository root.


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

6.9.1.2. Moving a file: Method 1

The first method for moving a file is 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 CVS/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 is retrieved with its new name. There will also be no record of when the name was changed and why.

6.9.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 expectedretrieving 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 to which it is moving. Because you need to use a sandbox, it may be easier to limit access to the servereffectively freezing the whole repositorythan 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 oldfile,v newfile,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 oldfile. 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 newfile.

  7. Remove any nonbranch tags from the new file with cvs tag -d tagname newfile. Use cvs status -v newfile 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:

  • Checking out old revisions by tag works perfectly.

  • The log of changes is intact.

  • Revision numbers are intact.

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.

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.


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

 /var/lib/cvs/wizzard/src$ cp main.c,v wizzard.c,v /var/lib/cvs/wizzard/src$ 

Example 6-12. 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.9.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 run cvs release on 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.9.1.5. 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.

  1. Follow steps 1 through 3 from 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.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.

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

  1. Have users run cvs release on 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 CVS/fileattr file and remove the reference if it is. (See "Moving Files and Directories," 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.9.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 run cvs release on 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 "Moving Files and Directories." 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.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.

In CVS 1.12.12, a commitid field was added to the RCS files. This identifies which commit or import the file was last changed in, and is unique to the particular commit or import session. It is the same in all files committed or imported during the session.


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

 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-14 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-14. RCS revision metadata

 1.2 date  2006.04.13.07.26.27;  author jenn;  state Exp; branches; next  1.1; 1.1 date  2006.04.12.08.56.41;  author jenn;  state Exp; branches   1.1.2.1; next  ; 1.1.2.1 date  2006.04.13.07.42.06;  author jenn;  state dead; branches; next  ; desc @@ 

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

 1.2 log @Minor tweaks to the config file. @ text @/*  * Wizzard.h  * Apr 12 2006  * 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 @ 

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

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

 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$ ps -p 20233 PID  TT  STAT      TIME COMMAND jenn@nest:/var/lib/cvs/wizzard/src$ rm *cvs.wfl.nest.20233 jenn@nest:/var/lib/cvs/wizzard/src$ rmdir *cvs.lock 

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

 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$ 




Essential CVS
Essential CVS (Essentials)
ISBN: 0596527039
EAN: 2147483647
Year: 2006
Pages: 148

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net