This document originally from http://www.cvshome.org/docs/blandy.html
Introduction to CVSby Jim BlandyModified for Roselab to maintain LINUS by Pat Fleming The snippets of actual code below are not from LINUS but from the original examples. However, the filenames and paths are correct for using CVS to edit LINUS files on grserv. What is CVS for?CVS maintains a history of a source tree, in terms of a series of changes. It stamps each change with the time it was made and the user name of the person who made it. Usually, the person provides a bit of text describing why they made the change as well. Given that information, CVS can help developers answer questions like:
How to use CVS -- First SketchBefore discussing too many vague terms and concepts, let's look over the essential CVS commands. Setting your repositoryCVS records everyone's changes to a given project in a directory tree called a repository . CVS will remember where this is after the first time you use CVS if you keep your local working directory tree and just update it (see below). If you remove your local working directory tree you must give the command below with full path to copy the repository tree to your local working directory tree again. On our system, the CVS repository is '/opt/cvsroot' on grserv. To use CVS on other machines in the Roselab you will want to enter the following commands: setenv CVS_RSH ssh setenv CVSROOT user@grserv.bph.jhu.edu:/opt/cvsroot where "user" is your login name. Better yet put those commands in your .tcshrc or .cshrc file. Or if you are using the bash shell put the following commands in your .bashrc file:
export CVS_RSH= "ssh" export CVSROOT= "user@grserv.bph.jhu.edu:/opt/cvsroot"Or a third option is just to enter the relevant information on the command line as shown in the next example. Checking out a working directoryCVS doesn't work on ordinary directory trees; you need
to work within a directory that CVS created for you. Just
as you check out a book from a library before taking it
home to read it, you use the (In the following "user" is your login name.) $ cd dir/with/no/linus/ $ cvs -d user@grserv.bph.jhu.edu:/opt/cvsroot checkout linus/PYLINUS cvs checkout: Updating linus/PYLINUS U linus/PYLINUS/install_data.py U linus/PYLINUS/setup.cfg U linus/PYLINUS/setup.py cvs checkout: Updating linus/PYLINUS/Src U linus/PYLINUS/Src/Makefile . . . U linus/PYLINUS/Src/linusc.c U linus/PYLINUS/Src/sedscript cvs checkout: Updating linus/PYLINUS/pylinus U linus/PYLINUS/pylinus/Atom3d.py U linus/PYLINUS/pylinus/__init__.py U linus/PYLINUS/pylinus/add_waters.py U linus/PYLINUS/pylinus/bumpcheck.py . . . cvs checkout: Updating linus/PYLINUS/pylinus/utils U linus/PYLINUS/pylinus/utils/Rasmol.py U linus/PYLINUS/pylinus/utils/__init__.py U linus/PYLINUS/pylinus/utils/concnt.py . . . $ The command (If you don't specify
to obtain only the 1.0 version.) CVS puts the tree in a subdirectory named 'linus/PYLINUS': $ cd linus/PYLINUS $ ls -l total 28 drwxrwxrwx 2 fleming users 4096 Dec 13 14:50 CVS/ -rw-rw-rw- 1 fleming users 7611 Dec 13 14:23 install_data.py drwxrwxrwx 4 fleming users 4096 Dec 13 14:50 pylinus/ -rw-rw-rw- 1 fleming users 37 Dec 13 14:23 setup.cfg -rw-rw-rw- 1 fleming users 924 Dec 13 14:23 setup.py drwxrwxrwx 3 fleming users 4096 Dec 13 14:50 Src/ Your working copies of the
Making changes to filesOnce CVS has created a working directory tree, you can edit, compile and test the files it contains in the usual way -- they're just files. For example, suppose we try compiling the package we
just checked out: (This will overwrite your current working
installation of pylinus in $ python setup.py install running install running build running build_py creating build creating build/lib.darwin-6.2-PowerMacintosh-2.2 creating build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus copying ./pylinus/__init__.py -> build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus copying ./pylinus/add_waters.py -> build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus copying ./pylinus/Atom3d.py -> build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus . . (many lines deleted) . byte-compiling /usr/local/lib/python2.2/site-packages/pylinus/utils/torsion.py to torsion.pyo byte-compiling /usr/local/lib/python2.2/site-packages/pylinus/utils/viewout.py to viewout.pyo removing /tmp/@5680.0.py running install_data copying pylinus/utils/ribosome.dat -> /usr/local/lib/python2.2/site-packages/pylinus/utils $ This procedure will also compile and install the C coded subroutines that LINUS uses if available. Merging your changesSince each developer uses their own working directory,
the changes you make to your working directory aren't
automatically visible to the other developers on your
team. CVS doesn't publish your changes until you're
ready. When you're done testing your changes, you must
commit them to the repository to make
them available to the rest of the group. We'll describe
the However, what if another developer has changed the same files you have, or the same lines? Whose changes should prevail? It's generally impossible to answer this question automatically; CVS certainly isn't competent to make that judgment. Thus, before you can commit your changes, CVS requires
your sources to be in sync with any changes committed by
the other team members. The $ cvs update cvs update: Updating . U Makefile RCS file: /opt/cvsroot/linus/PYLINUS/Src/linusc.c,v retrieving revision 1.6 retrieving revision 1.7 Merging differences between 1.6 and 1.7 into linusc.c M linusc.c $ Let's look at this line-by-line:
Since CVS has merged someone else's changes into your source, it's best to make sure things still work: $ python setup.py install running install running build running build_py creating build creating build/lib.darwin-6.2-PowerMacintosh-2.2 creating build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus copying ./pylinus/__init__.py -> build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus copying ./pylinus/add_waters.py -> build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus copying ./pylinus/Atom3d.py -> build/lib.darwin-6.2-PowerMacintosh-2.2/pylinus . . (many lines deleted) . $ It seems to still work. Committing your changesNow that you have brought your sources up to date with
the rest of the group and tested them, you are ready to
commit your changes to the repository and make them
visible to the rest of the group. The only file you've
modified is `linusc.c', but it's always safe to
run $ cvs update cvs update: Updating . M linusc.c $ As expected, the only file CVS mentions is 'linusc.c'; it says it contains changes which you have not yet committed. You can commit them like so: $ cvs commit linusc.c At this point, CVS will start up your favorite editor and prompt you for a log message describing the change. When you exit the editor, CVS will commit your change: Checking in linusc.c; /opt/cvsroot/linus/PYLINUS/Src/linusc.c,v <-- linusc.c new revision: 1.8; previous revision: 1.7 $ Now that you have committed your changes, they are
visible to the rest of the group. When another developer
runs Examining changesAt this point, you might well be curious what changes
the other developer made to `linusc.c'. To look
at the log entries for a given file, you can use the
$ cvs log linusc.c RCS file: /opt/cvsroot/linus/PYLINUS/Src/linusc.c,v Working file: linusc.c head: 1.8 branch: locks: strict access list: symbolic names: keyword substitution: kv total revisions: 8; selected revisions: 8 description: The one and only source file for LINUS ---------------------------- revision 1.8 date: 1996/10/31 20:11:14; author: jimb; state: Exp; lines: +1 -1 (tcp_connection): Cast address structure when calling connect. ---------------------------- revision 1.7 date: 1996/10/31 19:18:45; author: fred; state: Exp; lines: +6 -2 (match_header): Make this test case-insensitive. ---------------------------- revision 1.6 date: 1996/10/31 19:15:23; author: jimb; state: Exp; lines: +2 -6 ... $ Most of the text here you can ignore; the portion to look at carefully is the series of log entries after the first line of hyphens. The log entries appear in reverse chronological order, under the assumption that more recent changes are usually more interesting. Each entry describes one change to the file, and may be parsed as follows:
The If you would actually like to see the change in
question, you can use the $ cvs diff -c -r 1.6 -r 1.7 linusc.c Before we look at the output from this command, let's look at what the various parts mean:
Here is the output from the command: Index: linusc.c =================================================================== RCS file: /opt/cvsroot/linus/PYLINUS/Src/linusc/linusc.c,v retrieving revision 1.6 retrieving revision 1.7 diff -c -r1.6 -r1.7 *** linusc.c 1996/10/31 19:15:23 1.6 --- linusc.c 1996/10/31 19:18:45 1.7 *************** *** 62,68 **** } ! /* Return non-zero iff HEADER is a prefix of TEXT. HEADER should be null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) --- 62,69 ---- } ! /* Return non-zero iff HEADER is a prefix of TEXT, ignoring ! differences in case. HEADER should be lower-case, and null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) *************** *** 76,81 **** --- 77,84 ---- for (i = 0; i < header_len; i++) { char t = text[i]; + if ('A' <= t && t <= 'Z') + t += 'a' - 'A'; if (header[i] != t) return 0; } $ This output takes a bit of effort to get used to, but it is definitely worth understanding. The interesting portion starts with the first two
lines beginning with *************** *** 62,68 **** } ! /* Return non-zero iff HEADER is a prefix of TEXT. HEADER should be null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) --- 62,69 ---- } ! /* Return non-zero iff HEADER is a prefix of TEXT, ignoring ! differences in case. HEADER should be lower-case, and null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) Text from the older version appears after the
Here is the second hunk: *************** *** 76,81 **** --- 77,84 ---- for (i = 0; i < header_len; i++) { char t = text[i]; + if ('A' <= t && t <= 'Z') + t += 'a' - 'A'; if (header[i] != t) return 0; } This hunk describes the insertion of two lines, marked with `+' characters. CVS omits the old text in this case, because it would be redundant. CVS uses a similar hunk format to describe deletions. Like the Unix Adding and deleting filesCVS treats file creation and deletion like other changes, recording such events in the files' histories. One way to look at this is to say that CVS records the history of directories as well as the files they contain. CVS doesn't assume that newly created files should be
placed under its control; this would do the wrong thing
in many circumstances. For example, one needn't record
changes to object files and executables, since their
contents can always be recreated from the source files
(one hopes). Instead, if you create a new file, To add a file to a project, you must first create the
file, and then use the $ ls CVS Makefile linusc.c poll-server $ vi README ... enter a description of linusc ... $ ls CVS Makefile README linusc.c poll-server $ cvs update cvs update: Updating . ? README --- CVS doesn't know about this file yet. $ cvs add README cvs add: scheduling file `README' for addition cvs add: use 'cvs commit' to add this file permanently $ cvs update --- Now what does CVS think? cvs update: Updating . A README --- The file is marked for addition. $ cvs commit README ... CVS prompts you for a log entry ... RCS file: /opt/cvsroot/linus/PYLINUS/README,v done Checking in README; /opt/cvsroot/linus/PYLINUS/README,v <-- README initial revision: 1.1 done $ CVS treats deleted files similarly. If you delete a
file and then run To remove a file from a project, you must first delete
the file, and then use the Committing a file marked with There are several strategies for renaming files; the
simplest is to simply rename the file in your working
directory, and run You can add directories just as you would ordinary files; Writing good log entriesIf one can use However, a good log entry describes the
reason the developer made the change. For
example, a bad log entry for revision 1.7 shown above
might say, "Convert t to lower-case." This would be
accurate, but completely useless; Handling conflictsAs mentioned above, the It's straightforward to imagine how this works when the changes apply to distant regions of a file, but what happens when you and another developer have changed the same line? CVS calls this situation a conflict, and leaves it up to you to resolve it. For example, suppose that you have just added some
error checking to the host name lookup code. Before you
commit your change, you must run $ cvs update cvs update: Updating . RCS file: /u/src/master/linusc/linusc.c,v retrieving revision 1.8 retrieving revision 1.9 Merging differences between 1.8 and 1.9 into linusc.c rcsmerge: warning: conflicts during merge cvs update: conflicts found in linusc.c C linusc.c $ In this case, another developer has changed the same region of the file you have, so CVS complains about a conflict. Instead of printing `M linusc.c', as it usually does, it prints `C linusc.c', to indicate that a conflict has occurred in that file. To resolve the conflict, bring up the file in your editor. CVS marks the conflicting text this way: /* Look up the IP address of the host. */ host_info = gethostbyname (hostname); <<<<<<< linusc.c if (! host_info) { fprintf (stderr, "%s: host not found: %s\n", progname, hostname); exit (1); } ======= if (! host_info) { printf ("linusc: no host"); exit (1); } >>>>>>> 1.9 sock = socket (PF_INET, SOCK_STREAM, 0); It's important to understand what CVS does and doesn't consider a conflict. CVS does not understand the semantics of your program; it simply treats its source code as a tree of text files. If one developer adds a new argument to a function and fixes its callers, while another developer simultaneously adds a new call to that function, and does not pass the new argument, that is certainly a conflict -- the two changes are incompatible -- but CVS will not report it. CVS's understanding of conflicts is strictly textual. In practice, fortunately, conflicts are rare. Usually, they seem to result from two developers attempting to address the same problem, a lack of communication between developers, or disagreement about the design of the program. Allocating tasks to developers in a reasonable way reduces the likelihood of conflicts. Many version control systems allow a developer to lock a file, preventing others from making changes to it until she has committed her changes. While locking is appropriate in some situations, it is not clearly a better solution than the approach CVS takes. Changes can usually be merged correctly, and developers occasionally forget to release locks; in both cases, explicit locking causes unnecessary delays. Furthermore, locks prevent only textual conflicts; they do not prevent semantic conflicts of the sort described above, if the two developers make their changes to different files. Adding new projects to the repository
When you begin using CVS, you will probably already have several
projects that can be
put under CVS control. In these cases the easiest way is to use the
Unless you supply a log message with the `-m' flag, CVS starts an editor and prompts for a message. The string `pycluster' is a vendor tag, and `start' is a release tag. They may fill no purpose in this context, but since CVS requires them they must be present. You can now verify that it worked, and remove your original source directory.
Erasing the original sources is a good idea, to make sure that you do not accidentally edit them in Pycluster-v1.28, bypassing CVS. Of course, it would be wise to make sure that you have a backup of the sources before you remove them. |