Out with the old, in with Subversion
Subversion is a relatively new product explicitly designed to overcome the known shortfalls of CVS. It is a well designed software package, boasting modern features such as atomic commits, fast branching and tagging, the possibility to rename or move files and directories, support for binary files, and lightweight network transactions. Another nice feature of Subversion is that it is easy to set up an Apache server to provide HTTP (or HTTPS) access to your repository, so you can browse your repository using an ordinary Web browser. Subversion also provides a more sophisticated authentication model than CVS, and allows more fine-grained control over user rights.
One of the principal differences between Subversion and CVS is the way the two systems keep track of changes. CVS is a file-based system that maintains a separate version history for each individual file. Subversion, on the other hand, keeps track of revisions. A revision can be thought of as a snapshot of your project directory structure and contents at a given point in time. Revisions are the cornerstone of Subversion's atomic updates. Updating the Subversion repository is a bit like updating a relational database using transactions: when you commit a set of changes, you are guaranteed that either all of your changes have been integrated into the repository, or none at all have.
An offshoot of this strategy is that a given set of changes can be viewed as a distinct bundle and attributed to a particular developer. It also makes for more efficient handling of binary files and for much faster tagging and branching. This notion of atomic updates is painfully absent from CVS. As a result, in CVS, if a commit fails or is interrupted for some reason, the repository can be left in an unstable state: some files will have been correctly updated, whereas others will still be in their previous version.
Subversion in action
The Subversion commands are quite similar to those used in CVS, which ensures a CVS user will not feel too lost when getting started with Subversion. Importing a project into a Subversion repository looks something like this:
Listing 6. svn import
$ svn import . http://svn.mycompany.com -m "Initial import"
Adding trunk
Adding trunk/javapetstore
Adding trunk/javapetstore/setup
Adding trunk/javapetstore/setup/sql
Adding trunk/javapetstore/setup/sql/javadb
Adding trunk/javapetstore/setup/sql/javadb/delete.sql
Adding trunk/javapetstore/setup/sql/javadb/cities.del
...
Adding trunk/javapetstore/web/images
Adding (bin) trunk/javapetstore/web/images/purple-jellyfish-med.jpg
Adding (bin) trunk/javapetstore/web/images/fish3.gif
Adding (bin) trunk/javapetstore/web/images/california-desert-tortoise.jpg
Adding (bin) trunk/javapetstore/web/images/CIMG9129-s.jpg
...
As you can see in the above example, Subversion automatically recognizes binary file formats and handles them appropriately.
Directory structure
The presence of a trunk
directory in the above code hints at another important difference between CVS and Subversion. By convention, all branches (including the main branch, or trunk) are stored in separate directories in Subversion, as are tags. The recommended Subversion directory structure is to create three subdirectories in the project root directory, as shown here:
+ trunk
+ branches
+ tags
In practice, you can set up your directory structure in one of several ways. If your projects are very separate, and you want different tags and branches for each project, you might opt for a structure like this one:
+ myproject1
+ trunk
+ branches
+ tags
+ myproject2
...
If all the projects in a repository are intimately related, you might prefer the following:
+ trunk
+ myproject1
+ myproject2
...
+ branches
+ tags
Programming operations
Like CVS, Subversion expects you to check out a working copy for your day-to-day programming, like so:
Listing 7. Checking out javapetstore in SVN
$ svn checkout http://svn.mycompany.com/trunk javapetstore
A javapetstore/setup
A javapetstore/setup/setup.xml
A javapetstore/setup/sql
A javapetstore/setup/sql/javadb
...
Updating your files in Subversion is also very similar to doing it in CVS, as shown here:
Listing 8. Updates, SVN style
$ svn update
U javapetstore/main/java/src/UpdatedClass.java
D javapetstore/main/java/src/DeletedClass.java
A javapetstore/main/java/src/AddedClass.java
G javapetstore/main/java/src/MergedClass.java
C javapetstore/main/java/src/ConflictingClass.java
...
Updated to revision 10.
When Subversion updates your local files, it gives you a summary of the modifications it has performed. All the files affected by changes in the repository are listed, each with a code indicating the type of modification: "U" for Update, "D" for Deleted, "A" for Added, "G" for successfully merGed, and "C" for Conflicting.
Conflicts are handled similarly in Subversion and CVS, though Subversion does it in a slightly more readable format:
Listing 9. Output of a failed merge
<<<<<<< .mine
result = useMyFunction();
=======
result = useBertsFunction();
>>>>>>> .r21
Subversion will not let you commit until you confirm that you have resolved the conflict, using the svn resolved
command:
$ svn resolved javapetstore/main/java/src/ConflictingClass.java
You can also add, remove, or rename files and directories using fairly intuitive commands such as svn add
, svn copy
, svn delete
, and svn move
.
Finally, you commit your changes using svn commit
:
Listing 10. Committing changes in SVN
$ svn commit -m "These are my changes"
Adding LocallyAddedFile.java
Deleting LocallyDeletedFile.java
Sending ModifiedFile.java
Transmitting file data ..
Committed revision 11.
Tagging, branching, and merging
In Subversion, you tag a revision simply by copying a particular revision (or the latest revision on the trunk) into the tags directory, as shown here:
$ svn copy -m "Tagging Release 1.0" http://svn.mycompany.com/trunk http://svn.mycompany.com/tags/release-1.0
Branching is similar, though you may prefer to create a branch based on your local working copy:
$ svn copy -m "Creating new alpha development branch" . http://svn.mycompany.com/branches/release-1.1-alpha
Now, to switch to this branch, you can use the svn switch
command:
$ svn switch http://svn.mycompany.com/branches/release-1.1-alpha
Finally, you can merge your changes back into the main trunk using svn merge
. Merging is one of the less simple aspects of Subversion. To merge back the changes you've made in your new branch, you first need to find out what has been changed in the main trunk since you branched. You can do this using the svn log
command with the --stop-on-copy
option (to ensure that the log stops at the creation of the branch):
Listing 11. Change log for a branch
$ svn log http://svn.mycompany.com/branches/release-1.1-alpha --stop-on-copy
r54 | john | 2007-07-27 22:00:38 +1200 (Fri, 27 Jul 2007) | 1 line
------------------------------------------------------------------------
r15 | john | 2007-06-03 22:00:31 +1200 (Tue, 03 Jul 2007) | 1 line
.
.
.
r11 | john | 2007-06-03 21:59:08 +1200 (Tue, 03 Jul 2007) | 1 line
This means that the changes in the branch spanned revisions r11 to r54. So, to merge these changes back into the main trunk, you could switch back to the trunk and run the svn merge
command:
Listing 12. Merge 'em (note the comment)
$ svn switch file:///data/svn/dev-repos/javalamp/trunk
$ svn merge -r 11:54 http://svn.mycompany.com/branches/release-1.1-alpha
...
$ svn commit -m "Merged changes from branch release-1.1-alpha into trunk"
It is important to comment the commits when merging, as Subversion does not keep track of branching and merging explicitly.
Subversion in summary
Overall, Subversion is a well-designed and well-built version control system, with a host of advanced features, good IDE support, good documentation, and wide community support. Indeed, Subversion is well on the way to fulfilling its mission of replacing CVS as the standard in open source version control systems. On the downside, Subversion doesn't make it eas to keep track of branches and merges. The copy-based Subversion tagging mechanism is certainly fast, but it lacks the intuitive nature of a command like svn tag
.