When you manage the sources, you are managing the files associated with a software project. Traditionally, source management refers to the files used to build the application. Now, it refers to the entire application, including design documents and management files. There are many different source code management systems: CVS, BitKeeper, ClearCase, SubVersion, etc. Each of the source code management systems has a different way of interacting with the source code repository. The focus in this book is on CVS, which, while not command-identical, contains principles shared by the other source code management systems. In addition, CVS is very popular.
The objective of a source code management system is to allow you to control access to a set of files. Let's say that you have two programmers, and instead of using a real source code management system, you use a file server. At one point in time, both programmers modify the same file. If both programmers save their changes, the question is what happens to the individual changes? The answer is not predictable, but we can predict that it will be the wrong source code because a file system will not merge two different changes to a file. Therefore, one programmer's changes will be lost. A source code management system manages these sorts of problems.
If two programmers are manipulating the same file, a source code management system can take two strategies. The first strategy is to provide access to the file for the first programmer and force the second programmer to wait until the first programmer is done with the file. This strategy is called the lock-out strategy , because the file is locked until a programmer unlocks it. The problem with this strategy is that only one person can manipulate the file, and other people have to wait. At first consideration, this is a good strategy because it ensures that files are in a consistent state. However, the problem with this strategy is that only one person can get access to the file. Maybe another person wants to try something or add some code to the file. The other person has to wait until the person who locked the file is done and has released the file.
The second strategy is to allow everybody access to all the files, even if two programmers are manipulating the same file at the same time. However, we can quickly see that this brings up a problem. We'll have multiple versions of the same file that we will have to merge. The thought of manually merging the files is scary for any developer because it means sources could be lost. The reality is that source code management systems that use this strategy are extremely good at knowing how to merge source files. Therefore, the result is that these source code management systems are superior to lock-based file management systems. They are simpler to manage and integrate into an overall development process.
We will now discuss how to use the GUI tool wincvs . This tool exists on other platforms in essentially the same form (in UNIX, it's gcvs; in Macintosh, it's MacCVS ). There are dozens of other tools to use, and you can use the command line as well. However, a GUI tool will ideally make your CVS experience that much simpler. The examples shown in this chapter can be used to download only the demos.
Every CVS repository is defined by a CVSROOT variable, which is an environment variable if the command line is used. Figure 11.1 shows how to define the CVSROOT variable
In Figure 11.1 the dialog box WinCvs Preferences allows a user to enter the user details without having to define the CVSROOT path . In the dialog box are a number of text boxes that need to be filled in. The Authentication textbox is a drop-down list of the different types of authentication mechanisms. For the most part, you will be using pserver, but local and ssh are also available. Local authentication is a very loose authentication because a read/write-accessible local directory is used. The ssh authentication is very useful for those with remote workers who need to retrieve sources from the Internet. The ssh will encrypt the requests , thus making it very difficult for anybody to decipher the usernames, passwords, and sources. The Path textbox references the path where the CVS repository is located on the server. The Host Address textbox is an address that contains the CVS sources. The User Name textbox specifies the username used to log in to the CVS repository. In Figure 11.1, the User Name is "cgross", which for downloading is normally "anonymous." The CVSROOT textbox is a generated string created by the values of the other textboxes. Clicking on the OK button will save these values.
If you want to download or upload a project, you must log in. You do this by using the WinCvs menu item Admin -> Login. If the login is a first-time login, then a password will be expected. Enter the password, or if the user "anonymous" is used, enter "anonymous", which is a default password. Listing 11.26 shows a successful login.
cvs -d :pserver:firstname.lastname@example.org:/home/cvs/samples login Logging in to :pserver:email@example.com:2401:/home/cvs/samples ***** CVS exited normally with code 0 ***** CVSROOT: firstname.lastname@example.org:/home/cvs/samples (password authentication)
Adding a new module to a repository means adding new sources. For example, the sources for this book have been made available using CVS. The advantage of this is that the developer can download the sources, and then get the latest updates when they arrive . However, since the author is the only one who can write to the database, the source tree does not go willy-nilly in all directions. The author can patch the sources with updates received by individual readers. (Hint: If you want other topics explained, or if you see problems or potential ways to improve the sources, then send in the patch files to the email address jseng -email@example.com .) Figure 11.2 shows how the initial menu is used to upload a new module to the repository.
From the left pane, which contains the various directories, select a specific directory with the cursor. Then, right-click and select the menu item Import module, which will cause WinCvs to go through the various directories and select a file set to be added to the repository. A small dialog box appears, which is the file and directory iterator. Once a file set has been defined, you can tweak how the files will be added, as shown by the dialog box in Figure 11.3.
In Figure 11.3, the dialog box Import filter contains a list of file extensions and types that it found. In this list box, you can edit what WinCvs should do with a file before importing to the repository. To edit a file type, select the item from the list box and then click on the Edit button. When you do so, a dialog box similar to Figure 11.4 appears.
In Figure 11.4, the dialog box Entry description defines how to describe a file type. This is important because it defines how CVS handles merges and conflicts. The file type that we edited was for the extension cpp . The extension cpp represents a C++ source code file. Typically, this file will be treated as a text file. This means CVS will allow merges and other text operations.
If, instead, you want to treat the C++ source code file as a binary file, then you need to select the Force binary radio button in the dialog box. If a file is treated as binary, it means that no attempts will be made to translate the file, so merges and conflict resolution are not possible. Of course, for a truly binary file like an executable, this is a good thing. Instead of your attempting to merge a binary file, which does not work in any scenario, the different versions of the file replace each other. Binary is more like a file-server approach, except that the old versions are kept as history in the repository.
Indicating that a file is Unicode-based means that the file is text-based but uses the Unicode character set, which is 16 bits wide in contrast to 8 bits. Selecting the Ignore option in this dialog box causes the wincvs tool not to include the specified file extension when you add new modules.
When you let wincvs import the files using a search, all of the directories found will also be imported. In the case of the sources, this is not desired since the word processing documents used to create this book and the associated reference materials are also part of the jseng module (including these documents will most likely not impress the publisher). These are private to the author and cannot be added. The wincvs tool does not have an option to prune directories. Therefore, to get around this, we will import the directories manually. To do this, we do the same thing as we did in Figure 11.2. This time, though, we will import the subdirectory src . If we click the Continue button in the dialog box Import filter instead of Edit, we see a dialog box similar to Figure 11.5.
In Figure 11.5, the dialog box Import settings defines the details used to identify the module in the CVS repository. The textbox where the text "jseng/src" is located is the name of the remote repository location. The default that wincvs generated is "src", which corresponds to the name of the directory being imported. Generally this is acceptable, but not in this instance. This is because a directory within the module is being imported. Therefore, the text needs to be corrected to the value "jseng/src," where jseng is the actual module name. The Vendor tag textbox represents the name of the vendor that released the sources. The Release tag textbox represents the name of the initial release. More about tags will be discussed a bit later. The textbox for the log message is a description of the initial release. It is important that you always give the information on why something has been released, changed, or deleted. This makes it simpler to figure out which changes have occurred.
We now turn our attention to an important detail. When you're importing a set of sources using wincvs , the CVS directories used to manage the local sources are by default not generated. This means the imported directory will not be under source control. In the dialog box Import settings, it is important to select "Create CVS directories while importing" from Import options tab, as shown in Figure 11.6.
Once the source files have been imported, the source directory will change appearances in the wincvs window and will indicate that the sources are managed by CVS, as shown in Figure 11.7.
In Figure 11.7, the CVS-managed directories appear as icons with a checkmark. Individual files that have a little pencil beside them mean that file is read/write. If the pencil is crossed out, then the file is read-only. A CVS-managed file has a normal paper icon as you can see in Figure 11.7. A non-managed CVS file has a small icon with a question mark.
If instead of importing a module, you want to download one, you again call upon the context menu shown in Figure 11.2. This time, select Checkout module from the menu item; a dialog box similar to Figure 11.8 appears. Note that checking out the sources does not mean locking the sources, as would be the case in a locking-source code management system.
In Figure 11.8, the dialog box Checkout settings has only one textbox that needs filling in. The textbox where the value "generic" is typed in is called the module textbox. The module is the same thing that we imported in the Adding a New Module section of the chapter. In the case of Figure 11.8, we are attempting to download the module "generic." The module name can include subdirectories like the one shown in Figure 11.5, where the module name "jseng/src" was used.
Another useful option is the ability to retrieve sources according to a date, tag, or branch, as shown in Figure 11.9.
In Figure 11.9, the sources that belong to the revision/tag/branch developer are being selected. (We will discuss the concept of sources or tags in more detail later in this chapter.) It is also possible to select sources according to a specific date. To do so, select the By date checkbox and then define a date for the sources. The idea of the Checkout options tab in the dialog box is allow you to download the sources in a particular state. Once all of the parameters have been set, you can click on the OK button, and the sources will be downloaded to the local machine.
In the course of developing, you will make the application changes to the file. Sometimes, you will want to write the changes to the CVS server. When you're using wincvs , changes are highlighted using a red icon. In Figure 11.10, the file build.xml is highlighted to indicate that is has been changed since the last update from the CVS server. To commit the changes, select the file and click on the context mouse button to make the context menu appear, as shown in Figure 11.10.
The context menu shown in Figure 11.10 has a number of available items. If you choose Update selection, the last release from the CVS server is pulled down. However, before that happens, a dialog box similar to Figure 11.11 appears. Note that the Update selection option can also be applied to directories, which is essentially saying, "get me the latest sources for a specific module."
The dialog box Update settings, shown in Figure 11.11, allows you to exactly specify how the selection will be updated. If you only want to get an idea of the changes, then select the "Checkout files to standard output (avoids stickiness)" checkbox. This will dump the contents of the updated file(s) to the little console at the bottom of wincvs , as shown in Figure 11.11. The "Do not recurse into sub-folders" checkbox is used to stop updating all directories when a directory has been chosen . The checkbox "Reset any sticky date/tag/`-k' option" removes any tag or date information, essentially downloading the latest sources. The "Create missing directories that exist in the repository" checkbox is an extremely important checkbox, because when you're updating directories or modules, the default is not to get new directories. This means a developer could retrieve only part of the sources. If you're updating, you should check this option all the time. The "Get the clean copy" checkbox discards the old changes and downloads a fresh copy. No options are selected in Figure 11.11, and Listing 11.27 is the result of updating a modified file.
cvs update -P build.xml (in directory e:\book\jseng\src\) M build.xml ***** CVS exited normally with code 0 *****
In Listing 11.27, CVS updated the file, but notice that things were changed. Instead of the old file being removed, a merge occurred. The merge is indicated by the letter M in front of the file build.xml . Although nobody but the author has updated the old file, in a team scenario, an update like this can cause a more complicated merge with conflicts. More about this a bit later in the chapter.
Let's go back to Figure 11.10, which showed the context menu. This time, we'll select the menu item Commit settings, and a dialog box similar to Figure 11.12 appears.
In Figure 11.12, the "Enter the log message" textbox is used to define a descriptive message for the log files. Clicking on the OK button will cause the file to be committed and updated. The log output of a successful commit is shown in Listing 11.28.
cvs commit -m "Updated the build file to add a comment" build.xml (in directory e:\book\jseng\src\) Checking in build.xml; /home/cvs/samples/src/build.xml,v<build.xml new revision: 1.2; previous revision: 1.1 done
In Listing 11.28, notice how the version number jumps to 1.2 from 1.1, where it says new revision: 1.2.... In CVS, version numbers are not determined by the individual user, but by the number of commits that have occurred. Of course, when the sources have been branched, there are some numbering conventions where sub-number versions are generated, but this is generally the rule. Therefore, a number like 1.345 means that there have been 345 commits of the file.
Adding and removing files is not difficult with the wincvs tool. Figure 11.13 shows a file that has been added to the sources, but not the CVS repository. The file junk.txt has a little question mark next to it, which indicates that it has not been added to CVS repository.
To add the file to the repository, use the Add button icons, as shown in Figure 11.14.
In Figure 11.14, the mouse is hovering over an icon in the toolbar that has a little red cross. That little red cross toolbar button adds the file using the default add mode, which means let wincvs determine if the file is binary, text, or Unicode-based. If you need to force the file to be added in a specific mode, use the two buttons beside the mouse. The toolbar button with a little red cross and number 10 is used to add the file as a binary file. The toolbar button with a little red cross and letter U means to add the file as being Unicode-based. Once you've clicked on the correct Add toolbar button, the file is added to the repository. It is important to realize that the reference of the file has been added to repository. The file itself has not been committed. In wincvs , the file will appear with a read icon indicating changes. Therefore, to commit the changes, use the same sequence as defined in the Updating, Making, and Undoing Changes section of this chapter.
To delete the file from the repository, select the file and then use the toolbar button that looks like an X, which is located beside the Add toolbar buttons in Figure 11.14. When you click on the button, the file is deleted from the repository. However, as with the Add, the delete has not been committed, and an additional commit has to be issued to fully delete the file from the repository.
One of the powers of a source code management system like CVS is in its ability to tag, branch, and merge sources. In this section of the chapter, we will create a brand new module. The module will be called "test" and it includes a single file called simple.txt . The contents of simple.txt after the initial addition to the repository are shown in Listing 11.29.
Here is a single line of text
Listing 11.29 is simple and shows a single line of text. Adding a second and third iteration, we create Listing 11.30.
Here is a single line of text Line is added in second iteration Line is added in third iteration
To track our changes, wincvs has an interesting ability to graphically show the various changes. The way to show the graphical diagram is to select the menu item Graph Selection from the context menu shown in Figure 11.10. The graphical change log is shown in Figure 11.15.
In Figure 11.15, there are four boxes that contain numbers. The box with the number 126.96.36.199 indicates an initial addition to the repository. The boxes with the numbers 1.1, 1.2, and 1.3 define individual changes. If one of the boxes is selected, the log message and change statistics are displayed in the output window. Listing 11.31 shows the code for the change 1.2.
Revision: 1.2 Date : 2003/6/19 9:55:02 Author : 'cgross' State : 'Exp' Lines : +2 0 Description : Second iteration
In Listing 11.31, six pieces of information are displayed about an individual change. Most of them are obvious, but the change "Lines" is not. "Lines" indicates the number of lines, added and deleted. The +2 indicates that two lines have been added, and the 0 indicates that no lines have been deleted. If any lines were deleted, then they would be indicated using a minus sign and associated number. The purpose of the "Lines" property is to display how often a module changes. Remember way back to the initial chapters of this book, where it was necessary to find buggy modules that cause the most problems. The "Lines" property could be an indicator.
After all of these changes, you will now want to tag the current file as a released version. Tag the selection using the Tag Selection toolbar button, shown in Figure 11.16.
Once you've clicked on the Tag Selection toolbar button, a dialog box similar to Figure 11.17 appears.
In Figure 11.17, the dialog box Create tag settings has one text field and some checkboxes. The only required field is the textbox that defines the identifier of the tag. Note that the tag identifier cannot have spaces, which is a limitation of CVS. Once you've defined the tag identifier, click on OK. The various checkboxes are used to modify how the tag is generated and for which files. To see the results of our tagging, the change log file for the file simple.txt is generated again. It's shown in Figure 11.18.
Notice now in Figure 11.18 that the box with the number 1.3 has a connected box with the text "ReleaseVersion." This tag now specifies that whenever anybody checks out the sources with the tag "ReleaseVersion," that person will get version 1.3 of the file simple.txt . Tagging is useful when you're trying to reproduce bugs or create stable versions from an active source tree.
Having tagged the source file, let's branch the current version into a stable bug-fix branch and a development branch. The reason we want to do this is so that we can continue bug fixes on a source code file, but not include development changes. However, when we create a final version, we need to include the bug fixes in the final version. One branch will be used to track bugs, and the other branch to track new developments. Once the new developments have been completed, it is necessary to merge the two branches again. At this point, the sources are synchronized, and called a version that most likely has a release tag.
To create a branch, click on the Branch Selected toolbar button, as shown in Figure 11.19.
After you click on the Branch Selected button, a dialog box will appear and in the title area will be the text "Create branch settings." This dialog box will appear similar to the dialog box used to tag a branch as shown in Figure 11.17. The name of the new branch is "developerbranch," which is entered into the New branch name textbox.
At this point, the current source tree downloaded on the hard disk is still the main trunk and not the branched developer version. Making a change to the file simple.txt will update the main branch. To get the developer branch of the sources, the source code tree would have to be checked out again, but in the checkout of the sources the branch to retrieve has to be defined.
When you check out and commit the changes of a developer branch-based file, the version numbers will change. For example, the numbering could be 188.8.131.52. What this version number says is that the second branch with the first commit modifies the 1.3 version file from the main trunk. Putting it a bit simpler, the 1.3 in the branch indicates the version that the branch is based on from the main trunk. The 2 is the unique identifier for the developer branch, and the trailing 1 represents the revision number of the file.
Listing 11.32 represents the developer branch state of the file simple.txt .
Here is a single line of text Added in developer branch Line is added in second iteration Line is added in third iteration Second add in developer branch
Listing 11.33 represents the main branch of the file simple.txt .
Here is a single line of text Line is added in second iteration Line is added in third iteration Added in release branch
The challenge is the merging of the two files, which is handled automatically when you use CVS. wincvs makes it easy to perform a merge by doing an update with a merge.
To do that, the directory has to be selected and then merged. In Figure 11.20, the directory to be merged is selected.
In Figure 11.20, the directory "test," which represents the source tree for the main trunk, is highlighted. If you right-click on it, the context menu appears. Select the menu item Update selection. You'll see a dialog box similar to Figure 11.21 when you select the Merge options tab in the dialog box Update settings.
In Figure 11.21, the dialog box Update Settings with the tab Merge options selected exposes some radio buttons. The radio button "Only this rev./tag" is selected. This makes a drop-down list box become available; enter the text "developerbranch." What this dialog box is now saying is, " please check out the developerbranch of the test module on the current main trunk of the test module." Clicking OK will start the merge process. The results will then generate a file.#simple.txt.1 and an updated simple.txt file. The file name .#simple.txt.1 is the original file, so the old reference data is kept. Listing 11.34 shows the state of the file simple.txt after the merge.
Here is a single line of text Added in developer branch Line is added in second iteration Line is added in third iteration <<<<<<< simple.txt Added in release branch ======= Second add in developer branch >>>> 184.108.40.206
In Listing 11.34, the second line of text that was added in the developer branch was merged into the main trunk without any problems. However, the last lines added in both the developer trunk and the main trunk were problematic. The merge processor has determined these files to be problematic and to need additional changes. At this point, it would be up to the developer to figure what the correct state is. An easy way to fix this up, is to an use a conflict editor, as shown in Figure 11.22.
In Figure 11.22, the conflict editor highlights the areas of conflict using square brackets in the margin. The developer can then select the conflict and merge it to the main program by copying one merge above the other or vice versa. Once the conflicts have been looked at and resolved, the file is committed back to the main trunk.
The process of managing merges is what the team leader does. In fact, this should not be a surprise to the team leader because the team leader should know what the sources are doing. Some people think that managing conflicts and merges like this is extra unnecessary work. The reality is that it makes the work easier because multiple people can work with multiple states of the source code tree. Imagine having to add a module that depends on another module. If the other module is constantly changing, then the added module will constantly have to adjust for the instability. This adds to the development time. It would be simpler to develop individual units and then perform an integration test when the merge has been carried out. However, this strategy does take some time for novices to get used to.