For me it’s been bloody confusing trying to figure out how the free, open-source Subversion version control system works. But now, after some study, I think I get it. Because I doubt I’m the only one whose head swam trying to understand this thing, I’ve been keeping notes on what I’ve discovered.
Like many things, Subversion is fundamentally pretty simple once you understand a few basic concepts. But the initial sources I turned to were confusing.
Subversion is an essential resource for developing code. There are versions for Unix, Mac OS (i.e., command-line Unix in Terminal), and Windows. Because I use a Mac, I won’t discuss the Windows version, but it’s similar to the Unix versions except for how file paths are handled.
You can use Subversion to manage any type of files you like, but most often it’s used to hold source code for computer software projects, saved as text files. Because Subversion allows you to compare text files to see precisely how one differs from another, it’s most useful dealing with text. For the purpose of this note, I’ll assume you’re using Subversion to manage text files. (Note that your project can be a mixture of many different kinds of files if that’s what works for you. Subversion doesn’t care.)
Why a version control system?
Subversion does the following:
How does Subversion work?
Subversion consists of repositories, where your project is stored and archived, and one or more working copies of whatever project or projects you’re working on.
The repository is a database containing a complete historical record of whatever projects you assign to it. You can have as many repositories as you like. Each repository can have any number of projects. Internally, each repository links and condenses all of its files together. Some people combine all their projects into one big repository (probably the most common practice), but others use a different repository for each project.
A repository is simply a place to store stuff in an organized fashion. You CANNOT access its information directly. You can’t even directly traverse its internal directory structure. Every interaction with a repository MUST be done using “svn”, the Subversion command line utility, or one of the GUI interfaces. Using this utility, you can check stuff in, check stuff out, display and organize its contents, and run diagnostics and comparisons of the information it stores.
The working copy is a group of files in one directory tree, ready for you to work on the way you usually do. (For example, a Rails project would be contained in a single working copy.) These files have been “checked out” from the repository, and Subversion keeps track of them. Subversion adds a hidden “.svn” directory to every directory and subdirectory in a working copy. This hidden data tells Subversion where the assigned repository is, making interacting with the repository almost effortless.
If you want to add or modify files or directories within the working copy, use “svn” commands to do so, just as you MUST to modify the repository.
If you want a clean copy without any of the added Subversion metadata, you can export it at any time from either the working copy or the repository.
Basic rules of working with Subversion
This is the part that initially I had the most trouble with. If you don’t follow these rules, you get confusing and unhelpful results.
1. Whenever you are referring to the repository, you MUST use URL notation. For the sake of brevity, let’s say I’ve created my repository in the directory “/Users/jim/svn” (in my user directory), containing two projects (subdirectories) “apples” and “pears.” Assuming I’m just working on a local machine, I refer to the first project’s repository as “file:///Users/jim/svn/apples” and to the second as “file:///Users/jim/svn/pears”. If I forget and refer to “/Users/jim/svn/apples”, Subversion will complain that “/Users/jim/svn/apples” is not a working copy. (Ditto “file://~/svn/apples”)
2. Conversely, when referring to the working copy, you must use file path notation. So if my working copy is at “/Users/jim/Documents/apples”, that’s how I refer to it. (You can’t use “~/Documents/apples”.)
3. The working copy keeps track of the repository it’s assigned to. (You can change it if you need to.) So in many commands in which you interact with the repository, which one you’re using is assumed. Similarly, if you omit the path, the present working directory (”.”) is assumed.
4. Again, as noted above, the repository database encodes all its information. You can’t access the information directly; you must use the “svn” command-line command or a GUI interface.
5. Though you may directly add or delete files or directories in your working copy, Subversion won’t recognize these changes unless you use the “svn add” and “svn delete” commands.
6. If more than one person is working on a project at the same time, periodically during the day you need to update your working copy with any changes others have made. Subversion automatically combines changes you’ve saved to the working copy with updates have saved to the repository by others. (In the unlikely event you’ve both just changed the same line of code, Subversion points this out and gives you the opportunity to fix it.) Similarly, periodically during the workday you should save your work to the repository, usually when you’ve completed a particular task within the project.
Though both getting others’ changes from the repository and saving your work to the repository are two kinds of updates, obviously you can’t refer to them the same way. So to update your working copy from the repository, you use the command “svn update”. To save your work, use “svn commit”.
7. Anything that changes a repository must be accompanied by a “commitment” message. The purpose of this message is to remind you (and coworkers on the project) what you just did and why. You have two ways to enter the message. First, if the environment variables EDITOR, VISUAL, or SVN_EDITOR are set to the name of a text editing program, it will be started up for you to enter your message. Save and quit, and the message is stored with the update. More often, I use the “-m” option, which overrides the EDITOR if that’s set. Somewhere in the svn command, include your comments about what you just did as follows:
-m “My comments contained within double quotes”
If you forget the -m option and no EDITOR variable is set, you get the following message:
svn: Could not use external editor to fetch log message; consider setting the $SVN_EDITOR environment variable or using the –message (-m) or –file (-F) options
svn: None of the environment variables SVN_EDITOR, VISUAL or EDITOR is set, and no ‘editor-cmd’ run-time configuration option was found
8. Some svn commands that affect the working copy require a commitment message and some don’t. Commands that don’t require the message aren’t carried out until you commit your changes to the repository, complete with message.
How to set up a repository
1. Figure out where you want the repository and how many directories and projects you want it to contain. Do you want a separate repository for each project? Be sure its name and path are short and easy to type.
2. Use the regular command line command “mkdir” to create the repository directory, then use the command “svnadmin create PATH” (substitute the complete path to the repository for PATH) to convert it into a repository. Now, all subsequent interaction with the repository must be done with the svn command (or GUI interface). Here’s a command line example:
cd ~
mkdir svn
svnadmin create /Users/jim/svn
3. Create the repository directory structure using “svn mkdir” commands. If this is a programming project, most people use the “trunk, branches, tags” structure within each project to help manage various revisions, versions and iterations of the program. So, for example, let’s assume that in the repository “/Users/jim/svn” I’m creating two projects, “apples” and “pears”:
svn mkdir file:///Users/jim/svn/apples -m "creating Apples project"
svn mkdir file:///Users/jim/svn/apples/trunk -m "creating Apples trunk"
svn mkdir file:///Users/jim/svn/apples/branches -m "creating Apples branches"
svn mkdir file:///Users/jim/svn/apples/tags -m "creating Apples tags"
svn mkdir file:///Users/jim/svn/pears -m "creating pears project"
svn mkdir file:///Users/jim/svn/pears/trunk -m "creating pears trunk"
svn mkdir file:///Users/jim/svn/pears/branches -m "creating pears branches"
svn mkdir file:///Users/jim/svn/pears/tags -m "creating pears tags"
You can shorten these commands, because “svn mkdir” allows you to create several new directories with one command. The first four of the above commands could be simplified to (the following must all go on one line):
svn mkdir file:///Users/jim/svn/apples file:///Users/jim/svn/apples/trunk
file:///Users/jim/svn/apples/branches file:///Users/jim/svn/apples/tags
-m "creating Apples directories"
Hidden gotchas in “svn mkdir”: First, you do not list the repository itself, only the complete URL for each directory you want to add (or, if adding directories to a working copy, the complete file path including the new directory). Second, any intermediate directories must exist or you must have just created them. So the following won’t work because you have to create the “apples” directory before the “trunk” subdirectory:
svn mkdir file:///Users/jim/svn apples/trunk file:///Users/jim/svn/apples -m "Apples directories"
4. Any time you want to see a listing of what your repository contains, use the “svn list” command. So, to see the contents of the Apples project, say:
svn list file:///Users/jim/svn/apples
You can shorten this to “svn ls”. Using the -R option recursively lists every file in every subdirectory:
svn ls -R file:///Users/jim/svn/apples
To remove a directory use “svn delete”, which can delete almost anything in a repository (using a URL) or working copy (using a file path).
5. Let’s say I’ve already started the Apples project but haven’t yet saved it to Subversion or started a working copy. Here’s how to do that. Navigate to the original Apple directory and then import into the repository. Note the original directory isn’t linked to Subversion: it’s not a working copy, and it’s hard to convert it to one. Instead, change the name of the original directory to something else (to save for safekeeping) and then check out a working copy from the repository:
cd ~/Rails/apples
svn import . file:///Users/jim/svn apples/trunk -m "Initial commit of Apple project"
cd ..
mv apples apples.old
svn checkout file:///Users/jim/svn apples/trunk apples
Checkout syntax is as follows:
svn checkout [repository URL] [path to new working copy]
Note that you can leave out the path to the new working copy; Subversion defaults to creating a directory in the current location with the same name as your repository directory. So if you type “svn checkout file:///Users/jim/svn apples/trunk”, your working copy will be put in a new directory called “trunk” within the current working directory.
Then, periodically I can submit changes from my working copy to the repository:
cd ~/Rails/apples
svn commit -m "Hooked up the revinator to the zorch"
Note that the pre-Subversion copy of apples (”apples.old”) can be tucked away for safekeeping. Or, once you’re sure everything is working properly, just delete it. You can always recreate it with “svn export”.
Working with Subversion projects
Once you have everything set up, the rest is easy.
The following commands assume you’ve CD’d to your directory tree of the working copy. To ensure you’re in the right place, do a “svn info”. If you get an intelligent response, you’re set.
svn propset svn:ignore "database.yml" config
svn propset svn:ignore "*" tmp
svn propset svn:ignore "*" tmp/cache
svn propset svn:ignore "*" tmp/pids
svn propset svn:ignore "*" tmp/sessions
svn propset svn:ignore "*" tmp/sockets
svn propset svn:ignore "*doc" doc
svn propset svn:ignore "schema.rb" db
svn propset svn:ignore "*" log/
svn: Unable to open an ra_local session to URL
svn: Unable to open repository 'file:///Users/jim/svn/applzz'
svn: '.' is not a working copy
svn: Can't open file '.svn/entries': No such file or directory
Resources and further reading
The official Subversion web site has lots of information. It’s the place to find Subversion downloads. (Instead I installed mine with MacPorts: “sudo port install subversion”)
Frankly I found the free Subversion Book not to be terribly useful. It’s vague about syntax, and I didn’t come away with an understanding of how to use the program. The official Subversion User Guide is a shorter clone of the Subversion Book and shares its deficiencies.
A good quick reference is to just type “svn help” to get a list of commands, and then “svn help <command>” to see details about a particular command. Alas, the help files are also vague about syntax, and they don’t list nearly enough examples.
The way I got up to speed was to get the Pragmatic Version Control using Subversion book from Amazon. It’s well written and a quick read.
Here are some web sites that walk you through setting up Rails projects in Subversion:
Zigzig is a handy Macintosh GUI, free to noncommercial users. I didn’t find it that useful until I understood what was going on in the first place.
Here’s an open-source Subversion repository browser written in Rails with plenty of AJAX. I haven’t tried it, but it looks slick.