CVS: Open standard for version control
Write / Jim Blandy translation / Marvida
(Published in the 5th "programmer" in 2002)
The translator is pressed by: CVS, the Concurrent Versions System, is a version control system that accounts for dominance. It has the characteristics of open source, "Network-transparent", from individual developers to large distributed development team, can use it to use it to perform project version control:
l Its client / server access method allows developers to access the latest code from any web connected to the Internet.
l The unreserved CHECK-OUT version control model avoids people who use exclusive CHECK-OUT models.
l There is a customer tool on most platforms.
Many popular open source items, like Mozilla, Gimp, Xemacs, KDE and GNOME, have used CVS.
(Recorded from http://www.cvshome.org)
What is the use of CVS?
CVS maintains the history of the source tree according to a series of changes. It records each change through the modified time and the user name of the change. Usually, the changeman also provides some text, which describes why make changes. Based on this information, CVS can help developers to answer this question:
l Who made specific changes?
l When did they make changes?
l Why do they make changes?
l What other changes they also do?
How to use CVS?
Let's take a look at the basic CVS command before discussing too many conflict terms and concepts.
Set your warehouse (repository)
CVS records each person's change in a specific project in a directory tree called a warehouse. Before using CVS, you need to set the cvsroot environment variable to the warehouse. Whether you are responsible for your project configuration management, they must know what this is; maybe they have made global definitions for CVSROOT somewhere.
In any case, the CVS warehouse in our system is "/ u / src / master". This way, if your shell is CSH or its derived version, you need to enter the command:
SetENV CVSROOT / U / SRC / MASTER
If your shell is BASH or some other Bourne Shell variant, then enter:
Cvsroot = / u / src / master
Export cvsroot
If you forget this, CVS will complain when you try to use it:
$ cvs checkout httpc
CVS Checkout: No CvsRoot Specified! please use the `-d 'Option
CVS [Checkout Aborted]: or set the cvsroot environment variable.
Check Out Work Content
CVS is not working on a normal directory tree; you need to work in the directory you created in CVS. Just like you check it out before reading a book from the library home, you use the CVS Checkout command to get it from CVS before you operate a directory tree. For example, suppose you currently work in HTTPC, a normal HTTP customer:
$ CD
$ cvs checkout httpc
CVS Checkout: Updating HTTPC
U httpc / .cvsignore
U httpc / makefileu httpc / httpc.c
U httpc / poll-server
The command CVS Checkout HTTPC means: "Treasure the source tree called HTTPC from the warehouse specified by the CVSROOT environment variable."
CVS puts tree in subdirectory named "httpc":
$ CD HTTPC
$ ls -l
Total 8
DRWXR-XR-X 2 Jimb 512 OCT 31 11:04 CVS
-rw-r - r - 1 Jimb 89 Oct 31 10:42 Makefile
-rw-r - r - 1 Jimb 4432 OCT 31 10:45 httpc.c
-RWXR-XR-X 1 Jimb 460 OCT 30 10:21 Poll-Server
Most of these files are your work copy of your HTTPC source. However, subdirectory called "CVS" is different. CVS uses it to record additional information of each file in this directory to help determine what changes you have done after you check out.
Change the file
Once CVS creates a working directory tree, you can edit, compile and test the files contained in the directory through the usual way - they are just a file.
For example, suppose we try to compile our new packages:
$ Make
gcc -g -wall -lnsl -lsocket httpc.c -o httpc
Httpc.c: in function `TCP_CONNECTION ':
Httpc.c: 48: Warning: Passing Arg 2 of `Connect 'from incompatible Pointer Type
It seems that "httpc.c" has not been ported to this operating system. We need to convert a parameter for Connect. To fix this problem, 48 lines must be from:
IF (Connect (Sock, & Name, SizeOf (Name)> = 0)
Change to
IF (Connect (STRUCK, STRUCKADDR *) & Name, Sizeof (Name))> = 0)
Now it should be able to compile:
$ Make
gcc -g -wall -lnsl -lsocket httpc.c -o httpc
$ httpc get http://www.cyclic.com
... HTML Text for Cyclic Software's Home Page Follows ...
Merge your changes
Because each developer uses their own work catalog, your changes to your work directory do not automatically become visible to other developers in your development group. I don't get ready, CVS will not announce your changes. When you have completed the test of your changes, you must submit them (commit) to the warehouse to enable them to be used by other members of the group. We will describe the cvs commit command below.
However, if another developer has changed the same file you change, or the same line, what should I do? Whose changes should be successful? In general, it is impossible to automatically answer this question; CVS undoubtedly no ability to make such judgments.
Thus, CVS requires your source to keep synchronization with any changes to other group members before you submit your changes. CVS update command is responsible for this:
$ CVS Update
CVS Update: Updating.
U Makefile
RCS file: /u/src/master/httpc/httpc.c ,v
Retrieving Revision 1.6
Retrieving Revision 1.7merging Differences Between 1.6 and 1.7 INTO HTTPC.C
M httpc.c
Let's take a line in one way:
U Makefile
"U file" formal line of line of lines of this file has been explicitly updated (Updated); another person has changed this file, and CVS has copied the modified file into your home directory.
RCS file: ...
Retrieving Revision 1.6
Retrieving Revision 1.7
Merging Difference Between 1.6 and 1.7 INTO HTTPC.C
These messages indicate that some people have changed "httpc.c"; CVS puts their changes with your mergers and did not find conflicts on any text. Number "1.6" and "1.7" are revision numbers that identify specific points in the history of the file. Note that CVS just merges the changes into your work copy; the warehouse and other developers' work directory are not disturbed. To test the merged text, make sure it is effective.
M httpc.c
The "M File" form of the way is that the file has been modified (modified) and contains the modified changes to other developers. These are changes you need to submit. In this way, "httpc.c" now contains your modifications and other users.
Because CVS has merged other people's changes into your source, it is best to determine that the program can work:
$ Make
GCC -G -WALL -WMISSING-Prototypes -lnsl -lsocket httpc.c-h httpc
$ httpc get http://www.cyclic.com
... HTML Text for Cyclic Software's Home Page Follows ...
Submit your changes
Now you have made your source to keep the latest situations where the rest of the group, and test them, you can submit your changes to the warehouse and make them a group's remaining members. The only file that is modified by you is "httpc.c", but the list runs CVS Update to get the modified file from CVS is always reliable:
$ CVS Update
CVS Update: Updating.
M httpc.c
As expected, the only file mentioned by CVS is "httpc.c"; it said that the file contains changes you have not submitted. You can submit them like this:
$ cvs commit httpc.c
At this moment, CVS will start your favorite editor and prompt you to enter the log message to describe the change. When you exit the editor, CVS will submit your changes:
CHECKING in httpc.c;
/ @rc/master/httpc/httpc.c ,v <- httpc.c
New Revision: 1.8; Previous Revision: 1.7
Now you have already submitted your changes, and they are visible to other members of the group. When another developer runs CVS Update, CVS will combine your changes to "httpc.c" into their working directory.
Check
Now you may be curious, other developers have made what changes "httpc.c". In order to view log entries for specific files, you can use the cvs log command:
$ cvs log httpc.c
RCS file: /u/src/master/httpc/httpc.c ,v
Working file: httpc.c
HEAD: 1.8
Branch:
LOCKS: STRICT
Access List:
Symbolic names: Keyword Substitution: KV
Total Revisions: 8; SELECTED Revision: 8
Description:
The One and ONLY SOURCE FILE for the TRIVIAL HTTP CLIENT
----------------------------
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
...
You can ignore most of the text here; the part to be carefully viewed is the log entry behind the first line. Assume that closer modifications are often more interesting, so these entries appear in the order of reverse annual month. Each entry describes a change in the file and can be parsed as follows:
Revision 1.8
Each version of the file has a unique revision number. It looks like "1.1", "1.2", "1.3.2.2", or "1.3.2.2.4.5". By default, the revision 1.1 is the first version of the file. Each subsequent revision gets a new number by adding the rightmost number.
Date: 1996/10/31 20:11:14; Author: Jimb; ...
This line gives a change date, as well as the user name of the person submitted; the remaining parts of the line are not interesting.
(TCP_CONNECTION): CAST ...
This is (quite obvious) log entries that describe the changes.
The CVS log command can select the log entry through the date range, or the revision number; the details see the CVS Manual.
If you actually want to view the changes you are discussing, you can use the CVS DIFF command. For example, if you want to view Fred as a revised version 1.7, you can use the following command:
$ CVS DIFF -C -R 1.6 -R 1.7 httpc.c
Let's take a look at the meaning of each part before we look at the output of this command:
-c This option requires CVS DIFF to make people more understandable formats for its output. (I don't know why this is not the default option.)
-r 1.6 -r 1.7
This tells the CVS to display the changes you want to turn your HTTPC.C's revision 1.6 to amendment 1.7. If you like, you can request a wider revision; for example, -r 1.6 -r 1.8 will display Fred's changes and your nearest changes. (By specifying the revision: -r 1.7 -r 1.6, you can even request a change to be reversed, as if they are being revoked (undo). This sounds strange, but sometimes it is useful.)
httpc.c
This is the name of the file to check. If you don't give a specific file, CVS will generate a report for the entire directory.
Here is the output of this command:
INDEX: httpc.c
============================================================================================================================================================================================================= ================= rcs file: /u/src/master/httpc/httpc.c ,v
Retrieving Revision 1.6
Retrieving Revision 1.7
DIFF-C -R1.6 -R1.7
*** httpc.c 1996/10/31 19:15:23 1.6
--- httpc.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 * text, size_t len)
--- 62,69 ----
}
! / * Return Non-Zero iff Header Is A Prefix of Text, Ignoring
! Difference in case. Header Should Be Lower-Case, And
Null-terminated; len is the length of text. * /
Static int
Match_header (char * text, size_t len)
****************
*** 76,81 ****
--- 77,84 ----
For (i = 0; i { Char t = text [i]; IF ('A' <= T && T <= 'Z') T = 'a' - 'a'; IF (header [i]! = t) Return 0; } It takes a little effort to get used to this output, but it is undoubtedly worth understanding. Interesting parts are starting from the first two rows by *** and - the beginning; they describe older and newer comparison files. The remaining part consists of two bulk (hunk), and each bulk begins with a row of asterisk. Here is the first large block: **************** *** 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 * text, size_t len) --- 62,69 ---- } ! / * Return Non-Zero iff Header Is a Prefix of Text, Ignoring! Difference in case. Header stay be lower-case, and Null-terminated; len is the length of text. * / Static int Match_header (char * text, size_t len) The text from the older version appears behind the *** 62, 68 ***; the text from a newer version appears behind --- 62,69 - line. Each pair of digital indicates the range of the display line. CVS provides contexts around the change and will actually affect the "!" Character. Thus, we can see that the single line of the upper half is replaced by the second half of the two lines. Here is the second large piece: **************** *** 76,81 **** --- 77,84 ---- For (i = 0; i { Char t = text [i]; IF ('A' <= T && T <= 'Z') T = 'a' - 'a'; IF (header [i]! = t) Return 0; } This large block describes two rows that are inserted, they are marked with " " characters. In this case CVS omits the old text because it is extra. CVS uses a similar large block format to describe deletion. Like a UNIX DIFF command, the output from CVS DIFF is often referred to as patch (PATCH) because conventioners have used this format to publish errors and new features. While properly understanding, the patch also contains sufficient information, allowing a program to apply the changes described by the patch to an unmodified text file. In fact, the given patch is used as an input, and the UNIX Patch command is doing this. Increase and delete files CVS is based on other modifications to create and delete files, which records such an event in the history of the file. That is, the CVS record directory and the history of the files they contain. CVS does not assume that the newly created file should be placed under its control; in many cases such assumptions will be wrong. For example, we don't need to record changes to target files and executables, because their content can always be created from source files (we hope this). Conversely, if you have created a new file, CVS Update tag it with "?" Character until you tell CVS what you want to do. To add files to your project, you must create this file first, then use the cvs add command to add tags to it. Thus, the next time the call to CVS Commit will increase the file into the warehouse. For example, here you can add a readme file to the HTTPC project: $ ls CVS Makefile httpc.c poll-server $ vi readme ... ENTER A Description of HTTPC ... $ ls CVS Makefile Readme Httpc.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 additioncvs address: 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 dd ... CVS Prompts you for a log entry ... RCS file: / u / jimb / cvs-class / rep / httpc / readme, V DONE Checking in readme; / u / src / master / httpc / ready, V <- readme Initial Revision: 1.1 DONE CVS treats the deleted file in a similar manner. If you delete a file and run CVS Update, CVS will not assume that you want to delete the file. Instead, it will be moderate - it recreates the file through its last recorded content, and marks it with "U" characters, just like any other update. (This means that if you want to undo your changes to your files in your working directory, you can simply remove them, then let CVS Update recreate them.) To remove files from the project, you must first remove the file, then use the CVS RM command to do it to delete the tag. Thus, the next time the call to CVS Commit will remove the file from the warehouse. The file submitted through the CVS RM is not destroyed the history of the file. It just adds a new revision, marked as "do not exist". The warehouse has the previous record of the previous content and can be restored when needed - for example, by CVS DIFF or CVS log. There are several strategies to rename files; the easiest is to quickly rename files in your working directory, and run CVS RM for old names, run CVS AddDs for new names. The disadvantage of this method is that the log entry for the old file content will not be entered to the new file. Some other strategies avoid this quirk, but there are other more strange problems. You can add a directory like a normal file. Write a good log entry If we can use the CVS Diff to get the actual text of the change, why should I write a log entry? Obviously, the log entry can be shorter than the patch and allow the reader to obtain a comprehensive understanding of the change without having to deepen its details. However, a good log entry should describe the cause of developers to make changes. For example, a bad log entry written for the revised version 1.7 shown above: "This is accurate, but it is completely useless; CVS DIFF provides the same information, more clear. A better log entry is: "Make the test to not sensitive to case." Because others will have a general understanding of the code, so that the purpose of the purpose: HTTP customers should ignore the difference in cases when parsing the reply. Conflict As mentioned above, the CVS Update command combines the changes made by other developers into your working directory. If you have modified the same file and other developers, CVS combines their changes and your changes. It is easy to imagine how this is working when the different area of the file is applied to the file. But what happens when you have modified the same line and another developer? CVS said this situation is conflict and left it to you to eliminate it. For example, suppose you have just added a wrong check to the host name lookup code. Before submitting your changes, you must run CVS Update so that your source is synchronized: $ CVS Update CVS Update: Updating. RCS file: /u/src/master/httpc/httpc.c ,v Retrieving Revision 1.8 Retrieving Revision 1.9 Merging Differences Between 1.8 and 1.9 INTO HTTPC.C RCSMERGE: WARNING: CONFLICTS DURING MERGE CVS Update: conflicts found in httpc.c C httpc.c In this example, another developer has changed the same area of the file you change, so CVS will complain about conflict. Do not print "M httpc.c" as it usually do, print "C httpc.c" to indicate a conflict in this file. To eliminate conflicts, open the file in your editor. CVS tag conflict text: / * Look Up The IP address of the host. * / Host_info = gethostbyname (Hostname); <<<<<<< httpc.c IF (! Host_info) { FPrintf (stderr, "% s: host not found:% s / n", progName, hostname; Exit (1); } ======= IF (! Host_info) { Printf ("httpc: no host"); Exit (1); } >>>>>>> 1.9 SOCK = Socket (PF_INET, SOCK_STREAM, 0); It is important to understand how CVS is judged. CVS does not understand the semantics of your program; it just simply treats its source code as a text file tree. If a developer adds a new parameter to a function and fixes its caller, another developer also adds new calls to that function, but there is no new parameters, it is definitely a conflict. - These two modifications are inconsistent - but CVS will not report the conflict. CVS's understanding of conflicts is strict in the sense of text. Fortunately, conflicts in practice are rare. Typically, they seem to stem from two developers attempt to solve the same problem, lack communication between developers, or the design opinions of the program. Assigning tasks to developers in a reasonable way can reduce the possibility of conflict. Many version control systems allow developers to lock (LOCK) to prevent other people from changing them until he submits his changes. Although the lock is appropriate in some cases, it is obviously not a better solution than the method used by CVS. Changes can usually be merged correctly, while developers sometimes forget to release locks; in both cases, explicit locking will result in unnecessary delays. Moreover, the locking can only prevent text; if two developers make changes to different files, locking does not prevent the semantic conflicts described above.