Subversion In Fifteen Minutes

2006-05-27, Nicholas 'OwlManAtt' Evans

Introduction to Subversion

The goals of this document are to give you, the reader, enough information to begin using Subversion for your own projects. This will not explain every single feature of Subversion in depth, but enough to get you comfortable.

But before I can begin explaining how to use Subversion, I must first explain what Subversion is and give a bit of backstory.

Why Use Subversion?

Subversion is a system designed to control your source code. You may occasionally see the acronym 'SCM' associated with Subversion and its like. 'SCM' stands for 'software configuration management', because Subversion is also very good at managing plaintext configuration files. However, I will be focusing on source control.

There are a number of reasons that you may want to use a piece of software to manage your source code. If you are working collaboratively on a project, letting each developer have their own copy of the code on their local machine is great. It will prevent you two from overwriting the other developer's changes. Of course, it will not stop you two from completely changing the API, so its worth noting that Subversion is not a replacement for communicating.

But what if you are working on a project alone? You can still use Subversion. Source control management software also tracks changes to your code. If you break your application, and you cannot figure out why, you will always have the older (and functional) version to compare your changes against.

Finally, Subversion also offers a bit of protection against those early-morning 'oops, I just ran rm -rf on my project!' screwups. You know, the ones that happen before you get your morning coffee.

Where did Subversion come from?

To make a long story short, Subversion came from a lot of malcontent CVS folks. CVS, another revision control system, is an older package. Unfortunately, its developers have been unwilling to add certain features and fix a number of outstanding issues with how CVS works.

Subversion was started to remedy this. On the Subversion FAQ, they state that their goal is to take over the CVS user base.

Among the things that Subversion has done to differ from CVS, we have atomic commits (if one part of your commit fails, the entire process is rolled back, preventing your source code from being broken), tracking of changes to folders and renaming of files, and a simpler approach to tagging/branching (more on these shortly).

Subversion is also open-source software, released under a license similar to the MIT license.

Basics Concepts

First of all, I am going to make a few assumptions for the remainder of this document. One, you are using a UNIX or UNIX-like operating system (Sorry Windows users, I have no experience with Subversion under your OS. Look into TortoiseSVN, a very nice GUI for Subversion under Windows.). Two, I will assume that you are comfortable with the command line. And three, I will assume that you have already installed Subversion (check with your OS vendor first to see if they have a Subversion package!).

Got all of that? Good. I shall refrain from actually demonstrating how to do many of the things in this unit until later on. This is a concepts section, designed to familiarize you with how version control works, and I want my readers to stay focused on these important concepts before diving in.

So on to the basics!

Working With Subversion

Subversion can operate in a few different ways. In its simplest form, you will set up a central repository on your local machine, and use that as your Subversion 'server'. In fact, with this setup, there is no actual server running; just a bunch of files on your file system that the Subversion client knows how to work with.

It is also possible for Subversion to do its local filesystem thing remotely if you have SSH set up on your Subversion server. This is one of the ways Subversion is powerful due to its simplicity; there is no custom server to worry about setting up and managing certificates or keys for. It will use your existing SSH setup!

However, when working over SSH, it is advisable to use keys and ssh-agent to cache your passphrase. Otherwise, Subversion will prompt you to enter your password/passphrase several times when communicating with the server.

So you are a software developer, ready and raring to get hacking on that cool new project you have just discovered. The project website says that, if you want to submit a bug report or fix, you need to work with the development version in the trunk.

So what is this 'trunk'? In revision control software, you will usually see tags, branches, and trunk. These are three different 'categories' for versions of the project. Trunk is traditionally the cutting-edge version that is being actively developed, a lot like CVS HEAD.

'Branches' are for alternative copies of your project. For example, if you need to do a massive reorganization of the software's files (moving your classes around to other files, creating folders full of libraries, etc), and you need to preserve other developers' abilities to keep developing against a copy of the software that still works (for example, is not in the middle of a massive reorganization likely to cause problems until its complete), you might want to create a 'branch'.

The branch would allow you to have your own version-controlled copy of the project, allowing you to freely cause as many bugs as you want. The other developers would continue committing changes to trunk, and at the end of your reorganization, you can merge their changes and put your branch into trunk.

Finally, there are tags. Tags are used for milestone releases. After a year of development, I may have released versions 1.0, 1.5, and 2.0 of my application. For a number of purposes, you might want to keep these versions of the software accessible. (Keep the poor sysadmin who needs to rebuild a production system that crashed in mind; they may require version 1.0 of the software!) You can create a tag, so these versions are frozen in time. (Or not so frozen; you can still commit against them. You might do this to apply security fixes to older versions that are still being used...)

It is worth noting that when you use Subversion to copy things, it does not actually create a full copy of your files. It creates symlinks to the initial version, and stores changes as part of the copy. This cuts down on disk usage and hassle.

So what are these 'working copies' and 'commits' I keep referring to?

Both of these things relate to the whole Subversion process. It sounds cliche, yes, but I think that is a good way to describe it.

One begins this process by checking out some code from Subversion. The checkout creates a copy of code (be it from trunk, a branch, or a tag) that is intended for your to play with. This copy is your 'working copy'. When you make changes to it by modifying the files, adding files, deleting files, etc, you tell the central repository about this by committing your changes. Usually, your commit is accompanied by a log message summarizing your changes for posterity. And later, you can go back and compare your working copy against other versions.

There is a gotcha associated with this process. What happens when another developer fixes the same bug as you and commits it a minute before you commit your change? You both edited the same portions of the same file!

Subversion handles this by notifying you that there is a conflict. It rolls back your commit, preventing your repository from getting half of the changes, and marks up your code (or creates files with .rOLDREV and .rNEWREV extensions, if Subversion does not know how to add the comments).

It is up to you to resolve this conflict. How you do it is completely up to you. You can say 'aw, shucks, Dave fixed it first!' and use his version. You can call Dave up and talk to him about how his changes make the application less scalable, so you should use your version. Whatever you do, at the end, you need to make appropriate changes to your copy of the code and tell Subversion that the conflict is resolved.

At the end of the day, that only goes to show - Subversion is not a replacement for communicating with other developers!

Using Subversion

Okay. I think I have covered all of the concepts that you need to understand the basics of source control and Subversion. Now for what everyone has been waiting for - the practical section!

I am going to generate a Rails project to use as my example codebase. I am doing this because Rails generates lots of folders and files that I can use as examples of real-world usage.

        nevans@bell:/tmp/svn/example_project$ ls
        app/        config/ doc/ log/    Rakefile script/ tmp/
        components/ db/     lib/ public/ README   test/   vendor
        

The Rails project above is our initial, unversioned stuff. It's all boiler-plate Rails stuff too, in case you were wondering. I have yet to make any customizations, so this project has the honor of being put into version control from its very start!

First, we want to create a repository. I am going to create a repository for this one project only, but it's very easy for you to set up a Subversion repository for more than one project by putting the trunk/, branches/, and tags/ folders into another folder. Read on; that should make sense in a few seconds.

        nevans@bell:/tmp/svn$ svnadmin create /tmp/svn/repository

        nevans@bell:/tmp/svn$ svn mkdir --message="Setting up the directories..." \
        file:///tmp/svn/repository/trunk \
        file:///tmp/svn/repository/tags \
        file:///tmp/svn/repository/branches

        Committed revision 1.
        

I did two things. First, I used the svnadmin command to initialize my repository. This creates the boilerplate configuration files, DB files, and a few other things Subversion repositories require.

My second command created our project's directory structure. The directory structure I show here is by no means required. You could ignore it completely and put your code right into the repository. This is merely a good practice, and as such, you are free to ignore it when you deem it appropriate.

As a bit of a side note - Subversion did not create actual directories on the file system. It added the directories to its internal database. If you are looking for your folders in the repository, do not bother; they do not exist on the file system.

The --message flag is optional. This is the summary that gets put into the log file. If you do not specify your message on the command line, Subversion will invoke your favorite text editor (from the environment variables VISUAL or EDITOR) and ask you to enter one through that.

You may notice that I used URLs to specify the folders to create. This is yet another thing Subversion does to make life easy. But do not fear that you will need to type out lengthy URLs every time you invoke Subversion. Once you have your working copy, it will Just Know.

Subversion also gave me back a revision number. You can use this to specify a revision to check out or run a diff against later on, if you so desire. Alternatively, you would use a date and time to specify a revision. These revision numbers are also not specific to a tag/branch/project. They are integers that increment throughout the entire repository.

So now I have my Subversion repository ready to store my Rails project in. It's time to add it in!

You can add your project to the repository in one of two ways. Subversion has an 'import' command that will indiscriminately take everything and slap it right into the repository. You can also opt to turn your project into a working copy and do a more selective import. Since I want to show you best practices, I am going to do the gentler, more selective import.

We begin by checking trunk out into out project. Trunk is empty right now, so there is no need to worry about overwriting anything.

        nevans@bell:/tmp/svn/example_project$ svn checkout \
        file:///tmp/svn/repository/trunk .

        Checked out revision 1.
        

This creates a hidden folder, .svn, in the directory example_project. Now we need to tell Subversion about all of the stuff in our project to add it to the working copy. (A lot of output is going to be omitted below for brevity's sake.)

        nevans@bell:/tmp/svn/example_project$ svn add *
        A   app
        A   app/controllers
        . . .
        A   vendor
        A   vendor/plugins
        

This queues a whole lot of folders and files up to be committed. We can see what Subversion thinks is going on here with the status command.

        nevans@bell:/tmp/svn/example_project$ svn status
        . . .
        A   log/test.log
        A   log/development.log
        A   log/server.log
        . . .
        

The 'A's next to the file mean that the file is queued to be added. There are a number of symbols Subversion will use here. Please refer to the Subversion book linked at the end of the tutorial for a complete listing of symbols and their definitions.

Above, we see several log files that are going to be added. However, we really do not want our logfiles to be put into version control. To remedy the situation, we need to set an ignore on the log files and remove them from the add queue.

The ignore can be achieved by setting a Subversion property on our log folder.

        nevans@bell:/tmp/svn/example_project$ svn propset svn:ignore "*.log" log

        property 'svn:ignore' set on 'log'
        

You can specify multiple ignores by providing a file with each filter on a separate line.

        nevans@bell:/tmp/svn/example_project$ cat ~/ignore_list
        *.log
        *.pid

        nevans@bell:/tmp/svn/example_project$ svn propset svn:ignore -F ~/ignore_list log

        property 'svn:ignore' set on 'log'
        

Then you just remove the files from the queue with revert.

        nevans@bell:/tmp/svn/example_project$ svn revert log/*

        Reverted 'log/development.log'
        Reverted 'log/production.log'
        Reverted 'log/server.log'
        Reverted 'log/test.log'

        nevans@bell:/tmp/svn/example_project$ svn status
        . . .
        A   tmp/sockets
        A   log
        A   Rakefile
        . . .
        

As you can see from the status command, Subversion no longer has an interest in the .log files stored in the log folder. The folder will still be created as a part of the project, but log files inside will not be versioned.

Had we not set our ignore and only reverted the files, Subversion would put a '?' symbol next to the log files in the output of status, indicating that it did not know anything about those files.

Ignore is not the only property that you can set with setprops. There are a variety of other pieces of meta information you can set, like the copyright status of a file, or a file's license. For more details, please see the Subversion book.

Now we may finally check in our changes! (Checking in is just another term for commit, by the way.)

        nevans@bell:/tmp/svn/example_project$ svn commit --message="The initial Rails project."

        Adding   README
        Adding   Rakefile
        Adding   app
        . . .
        Transmitting file data ........................................
        Committed revision 2.

        nevans@bell:/tmp/svn/example_project$ svn update

        At revision 2.
        

All we need is a simple commit. Again, you do not need to specify the summary on the command line. You also see that I run an update after committing. This is to sync up the differences (mainly log messages and revision numbers here) between the repository and my local copy. Update is also used to update your local copy's code against the repository when another developer commits his changes.

After running an update, if we want to see the changelog, we simply run the log command.

        nevans@bell:/tmp/svn/example_project$ svn log
        ------------------------------------------------------------------------
        r2 | nevans | 2006-05-27 12:09:22 -0400 (Sat, 27 May 2006) | 1 line

        The initial Rails project.
        ------------------------------------------------------------------------
        r1 | nevans | 2006-05-27 11:07:01 -0400 (Sat, 27 May 2006) | 1 line

        Setting up the directories...
        ------------------------------------------------------------------------
        

You can also specify two dates to get all log messages from between those two points in time with the -r option.

So now, what if you make changes and want to see the differences between your working copy and the repository? Subversion has diffing built right in to do this for us. Say, for example, I have modified the config/database.yml file.

        nevans@bell:/tmp/svn/example_project$ svn diff
        Index: config/database.yml
        ==============================
        --- config/database.yml (revision 2)
        +++ config/database.yml (working copy)
        @@ -14,7 +14,7 @@
          adapter: mysql
          database: example_project_development
          username: root
        - password:
        + password: 12345_hay_thats_my_luggage!
          host: localhost

# Warning: The database defined as 'test' will be erased and
        

Doing the 'diff' without a specific file will go through everything below the current working directory and show the differences. You, however, could run 'svn diff config/database.yml' to only view differences for that one file.

The output is like what you would see from a normal diff. The lines marked with a dash no longer exist, and the ones with a plus have been added. There are also unmodified lines shown around the changes to provide you with context.

Alright. Now you can get to coding you application. In the course of development, you find that you have created a new file, foo.txt. Status tells us Subversion knows nothing of this file (with a ? icon). So we need to tell it that this file is a vital part of our project with add.

        nevans@bell:/tmp/svn/example_project$ svn add foo.txt
        A   foo.txt

        nevans@bell:/tmp/svn/example_project$ svn commit --message="Added foo.txt."
        Adding foo.txt
        Transmitting file data ..
        Committed revision 5.
        

So once you have worked in it for eight or nine years and had it thoroughly tested, you're ready to release version 1.0. You want to create a tag for this release, called 1.0, using that tag folder we created all those years ago.

We start by making a temp folder. We're going to check the whole repository out to make things easy on us.

        nevans@bell:/tmp/svn/temp$ svn checkout file:///tmp/svn/repository/ .
        A   trunk
        A   trunk/test
        A   trunk/test/unit
        . . .
        Checked out revision 2.
        

With all of this came our tags/ and trunk/ folder. Now, we just copy trunk to a folder in the tags/ directory.

        nevans@bell:/tmp/svn/temp$ svn copy trunk/ tags/1.0
        A   tags/1.0

        nevans@bell:/tmp/svn/temp$ svn status
        A + tags/1.0
        

Status tells us that we are adding (A) a copy (+) of something. Commit that and you will have successfully released version 1.0 of example_project.

But, what if you realize that releasing version 1.0 will leave you and all of your development team jobless? You can always delete the tag. Again, I will preform an action from our working copy of the entire repository.

        nevans@bell:/tmp/svn/temp$ svn delete tags/1.0/
        D   tags/1.0/test/unit
        D   tags/1.0/test/test_helper.rb
        D   tags/1.0/test/functional
        . . .

        nevans@bell:/tmp/svn/temp$ svn commit --message="Bye!"
        Deleting tags/1.0

        Committed revision 4.
        

Conclusion

That, in short, is Subversion. Subversion supports a number of other advanced features that I have not detailed here. As such, it would be worthwhile for you to check out the online Subversion book if you want to continue your Subversion education.

If you do not require more advanced features, than you should at least skim through the book, because it has a number of useful tables. It is a much more complete reference than this tutorial.

Additional Reading

Legal / Credit

This tutorial is copyright 2006, Nicholas 'OwlManAtt' Evans. You may redistribute this tutorial, in its entirety or in portions, provided that you do not change its text. This legal notice must also be included if you reproduce just a portion or portions.

Thanks goes out to the authors and publisher of the Subversion book for releasing it in a digital format, free of charge. Credit also must go to Ezra Zygmuntowicz, for without a chapter from his upcoming book, I would have never felt comfortable enough with Subversion to write this tutorial.

Finally, I need to thank DSL.Net for making me hate CVS.

If you wish to contact me, the author, please see this URL:

http://owlmanatt.com/contact

This has been a Yasashii Syndicate production.