Getting started with Subversion

The basics of the popular version control system.

Because the open source Subversion version control system lets you assign fairly arbitrary keywords to resources, I had some ideas a few months ago about combining Subversion with an RDF triple store to track resource metadata. I never learned Subversion properly, though, and recently decided to keep my to-do lists, address book, and notes files in Subversion to get better accustomed to its important commands. Many introductions to Subversion are available, but none were quite what I wanted, so I decided to write up the basics myself once I worked them out. I do recommend Garrett Rooney's "A Crash Course in Subversion" (part one, part two), because it's more detailed than mine, but it has too many details for a quick introduction, with digressions about how competing programs implement certain features and other things I wasn't interested in.

One problem I had with Subversion was my tendency at first to think of it as a version control system for files.

One problem I had with Subversion was my tendency at first to think of it as a version control system for files. Once I started thinking of it as a version control system for directories full of files, it was easier to understand its logic about certain things. For example, the commit command commits the changes to any files in the current working directory to the repository, and the update command updates a directory's collection of files with any more recent versions from the repository. (These are simplifications of the default behavior—of course these commands can do more.) Maybe this approach is more intuitive to others, but it took me a while to learn to think that way.

The most important tasks you do with Subversion are to:

Basic background

As far as I can tell, Subversion works identically on Linux and Windows. I've been keeping a repository on a thumb drive and using it to keep certain directories of a (Xubuntu) Linux machine and a Windows machine in sync. So far, this has worked well.

There are two command line programs you use for typical Subversion use: svn and svnadmin. When someone refers to the svn command foobar, they generally mean something that you enter like this:

$ svn foobar arg1 arg2 etc.

(I'm using the dollar sign to represent the command prompt—in later examples, lines with no dollar sign that follow these commands will show you the results of the command at the dollar sign.) Entering "help" after "svn" or "svnadmin" lists the available commands and the potential abbreviations of those commands. Entering a command name after "help" tells you more about that command. If svn included a foobar command, this would tell you more about it:

$ svn help foobar

Subversion commands use URLs to refer to repositories and resources within repositories. In the simplest form of this arrangement, in which your repository sits on locally accessible disk, this means adding "file:///" before the path name. Fancier Subversion server arrangements let you reference files using "http://" and other URL prefixes.

Creating a repository

The following creates an empty repository as a subdirectory of /media/disk:

$ svnadmin create /media/disk/testrepos

Now that it's created, commands will refer to the repository as file:///media/disk/testrepos. Because it's on a thumb drive, when I later move the thumb drive to a Windows machine that decides to call the thumb drive f:, my svn commands will refer to the repository as file:///f:/testrepos.

Preparing the repository for your files

The next step is to create a child of the repository root called "trunk". Eventually, you might create siblings of trunk called tags and branches to help organize alternate development branches and groups of files used together for a particular milestone such as a software release, but these are not issues for a quickstart guide. If you keep all of your directories full of files in descendants of trunk, you'll follow existing conventions and set yourself up to eventually move on to more sophisticated use of Subversion.

$ svn mkdir -m "creating the trunk dir" file:///media/disk/testrepos/trunk 

For certain commands, Subversion wants you to supply a comment, so if you don't include -m followed by a quoted string, it will try to start up an editor in which you enter the comment. I think it's easier to add the comment to the command line with the -m switch, and I don't alway include a comment—an empty string between the quotation marks works just fine.

Adding a directory of files to the repository

For demonstration purposes, I have a directory called /home/bob/samplewd with two files in it: myfile1.txt and myfile2.txt. The following puts this directory and its contents into version control by importing it into Subversion:

$ svn import -m "Adding first directory" /home/bob/samplewd file:///media/disk/testrepos/trunk/samplewd

The first argument after the -m comment is the directory to import and the second is the URL for the place in Subversion where I want to import it.

Listing files in the repository

The command to list the files in a directory in the repository is simple enough:

$ svn list file:///media/disk/testrepos/trunk/samplewd

Again, when you first learn Subversion, it's worth entering the help command for each new command that you try in order to learn more about it, like this:

$ svn help list

Creating a working copy of a directory from the repository

It would be nice if, after checking a directory of files into Subversion, you could then edit files in that directory and issue Subversion commands to pull the revised versions of those files into the repository, but that's not the way Subversion works. It tracks files in a "working directory", and it doesn't know that the directory that it just copied into the repository is a working directory. You must tell Subversion to create a working directory, and you can have it do this anywhere you like. Some people like to delete their original directory and then check out the Subversion copy in its place to be the working directory. If you're new to Subversion and doing this with files that matter to you, you're better off renaming the existing directory as a backup and then checking out a new copy. I did this from my /home/bob directory:

 mv samplewd samplewd.bkp    # or in Windows, rename instead of mv
 svn checkout file:///media/disk/testrepos/trunk/samplewd samplewd

The second command here tells Subversion to create a child of the current directory called samplewd as a working directory copy of the named directory in the Subversion repository.

Adding revised versions of files to the repository

The commit command tells Subversion to pull any revised files from the working directory into the repository. After I checked the samplewd directory and its contents out of the repository as shown above, I edited myfile1.txt and entered this command from within the samplewd directory to commit the changes:

$ svn commit -m "made first edits" 

This commits all the changes in the current working directory to the repository.

Finding out the status of your working directory files

The status command tells Subversion to list the status of files in the current directory. The default behavior is to list the status of files that have modifications that haven't been committed to the repository. After making a few edits to myfile1.txt and creating a new file called myfile3.txt, the status command gives me this:

$ svn status
?      myfile3.txt
M      myfile1.txt

This tells me that myfile3.txt is not under version control and that myfile1.txt has been Modified. ("svn help status" tells you about other codes that may appear at the beginning of each line.) myfile2.txt isn't listed because Subversion has nothing to say about it: it's in version control and hasn't been changed since the last time it was put there.

To put myfile3.txt under version control, I use the add command:

$ svn add myfile3.txt

(For filename arguments, svn usually accepts wildcard arguments such as my*3.html, which can save you some keystrokes. If you're really interested in saving keystrokes, "svn help" shows you the abbreviations you can use for many commands, such as ci instead of commit.) Instead of actually putting myfile3.txt in the repository, this command only marks it for addition to the repository the next time you commit. The same commit command that puts the edited version of myfile1.txt in will put myfile3.txt in as well, because when used with no arguments the commit command applies to everything in the current directory under source control:

$ svn commit -m "created new myfile3.txt file and edited myfile1.txt" 

Now, entering the following with no special parameters shows no output, which is always good to see when you're finishing an editing session, because it means that your working directory is in sync with the repository:

$ svn status

Extracting updated versions of files from the repository

The update command tells Subversion to update the current directory based on whatever's in the repository. Let's say I've revised several files on my Linux laptop and then committed the changes to the Subversion repository that I keep on a thumb drive. After I move the thumb drive to a Windows machine and make the corresponding directory my current working directory, I enter the following to update that directory from the repository copy:

c:\some\path\samplewd\>svn update

After I do that, the directory should have the same contents that the Linux samplewd directory did the last time I committed its contents to to the repository. If I edit or add to the files in the working directory on the Windows machine, I'll commit those changes to the repository on the thumb drive and then update the working directory in the Linux laptop's directory from the thumb drive the next time I use it.

The repository and renamed or deleted files

If I rename a file on my laptop, I want Subversion to know that I did, because the next time I update the corresponding directory on my Windows machine, I don't want to find copies of this file with both the old and new names. If I use Subversion to do the rename, I'll only have the renamed version in all updated versions of the directory:

$ svn rename myfile3.txt myfile3a.txt
A         myfile3a.txt
D         myfile3.txt

As the output shows, Subversion treats it as the addition of one file and the deletion of another, but this achieves what you want. Again, remember that the Subversion repository doesn't really know that you did this until you commit your most recent changes. Once you do, Subversion knows that your myfile3a.txt file used to be your myfile3.txt file, and at which revision of your working directory the rename took place, so you can still go back and get access to the old pre-rename version.

The same logic applies to deletion of files: have Subversion do it for you, and it will remove it from all corresponding working directories while keeping the older version in the repository if you need it:

$ svn delete myfile2.txt
D         myfile2.txt

Resolving conflicting versions of a file

What if Jane and Jack make different edits to the same file in two different working directories that are supposed to reflect the same directory in the repository? Jane can commit her edited version with no problem, because Subversion doesn't know that there's a problem yet. When Jack tries to commit his changes, Subversion knows that Jack was editing something from before Jane's round of committed edits, and it won't put Jack's version into the repository. Instead, it lets Jack know that there's a problem and gives him information to straighten out the problem.

To demonstrate what happens, I made one edit to a copy of myfile1.txt in one working directory and another to a copy in a second working directory. I committed the first one with no problem. An attempt to commit the second looked like this:

$ svn commit -m "testing conflicts part 2"
Sending        myfile1.txt
svn: Commit failed (details follow):
svn: Out of date: '/trunk/samplewd/myfile1.txt' in transaction '8-1'

It looks like there's a problem. The next step is to run Subversion's update command, which will help you sort the problem out.

$ svn update

This merges the two conflicting versions into a combined version in the current directory—if possible. In one of my tests, I added one new line at the beginning of one copy of the file and another new line at the end of another copy in a different working directory. After committing one, attempting to commit the other, and getting an error message similar to the one shown above, the update operation in the second directory created a version of the file with the two new lines in their appropriate places. (This still needs to be committed to the repository.) As it does so, the update command outputs the filename with a "G" status code for "merGed".

What if the edits can't be combined so easily and Subversion can't merge the two versions? It outputs the filename with a status code of "C" for "Conflict" and provides you with plenty of information to help you fix the problem. For example, let's say you checked out the file myfile1.txt from release 7 and then someone else committed a new version to the repository as release 8. When you unsuccessfully tried to commit your own revised version, Subversion would revise your myfile1.txt file to include some diff output (the utility that shows the differences between the versions) and it would create a few new files to help me fix the problem:

  • myfile1.txt.r7 The release 7 version of the file, which was checked out and edited to create the two different conflicting versions.

  • myfile1.txt.r8 The release 8 version of the file, which was checked in from the other working directory.

  • myfile1.txt.mine The version of the file in the current directory that conflicted with the repository.

Use these to edit myfile1.txt until it looks the way you really want it, and then enter the following command to tell Subversion "I've resolved the problem and the current version of myfile1.txt is the one that I want as the official copy in the repository":

$ svn resolved myfile1.txt
Resolved conflicted state of 'myfile1.txt'

Of course, it didn't really resolve the conflict; you must execute another commit command before that's complete. The resolved command also removes the extra files that the update command created (in this case, the .r7, .r8, and .mine files) instead of leaving them there to clutter up your directory.

Reverting your working copy back to an earlier version from the repository

The simplest case for reverting a file to an earlier version is to tell the repository to throw out your recent edits and replace a working copy with the most recent committed version from the repository. Let's say that one afternoon's round of edits led you down a blind alley and you want to go back to where you were at lunch time. You issue a status command, which shows you that myfile1.txt has been modified, and you tell Subversion to revert to the last committed version. You then enter another status command, which shows you that no files in your directory are out of sync with your repository versions:

$ svn status
M      myfile1.txt
$ svn revert myfile1.txt
Reverted 'myfile1.txt'
$ svn status
$ 

If you want to go back to an earlier version of a file in the repository, you'll use the same command that you used to get files out of the repository in the first place: update. Before you use it, remember to either commit your current version to the repository or revert as shown above— Subversion doesn't want you to lose any of your work, so if you pull an older version of a file out of the repository and Subversion sees uncommitted modifications in your file, it will create all the extra files that it creates to help you resolve any conflict between your existing working copy and the "official" one.

The following tells Subversion to replace the working directory's version of myfile1.txt with the one from revision 6:

$ svn update -r 6 myfile1.txt

(When referencing a particular version, the space after the "-r" is optional.) The online help for the update command lists some handy keywords that you can use instead of a number to specify a particular revision, such as "PREV" to get the version previous to the most recent committed version.

Looking at old versions of files without reverting to them

If you only want to look at an older version of a file without making it your working copy version, a simpler command is available. The following command tells Subversion to display the contents of myfile1.txt from release 3 of the committed working directory:

$ svn cat -r 3 myfile1.txt

The cat command can use the same keywords as update in place of specific version numbers.

Summary

Once you've put a directory of files (and potentially subdirectories) into a repository, then checked them out into a working directory, your typical procedure for a session of working with those files is to issue an an update command to ensure that the directory has the most recent versions, then make your edits, then issue a commit command to put your revised versions into the repository. (I almost wrote "to put your updated versions into the repository", and it wouldn't be the first time that re-use of Subversion vocabulary with slightly different semantics led me to a bit of confusion.) To reiterate one point I've already made several times, remember that many important Subversion commands have no effect until the next time you issue a commit command.

Subversion offers many more commands than we've seen here. You can specify files in your working directory for it to ignore, you can list the user names of who made which changes when, you can store repositories on remote systems, you can change the URL used to reference a particular repository if it got moved to a different server, you can fork off a development effort into multiple distinct branches... you can do all kinds of cool things. The Garrett Rooney two-part article is a great place to learn more about these features. Also, remember that I've only skimmed the surface of the commands that I did cover, so don't forget to enter "svn help command" for each of the above commands after you try them to learn about what else they can do.

For some more interesting thoughts on Subversion, see this recent blog posting by Norm Walsh and Joey Hess's Keeping Your Life in Subversion.

3 Comments

A great way to get started with subversion is to install Mercurial :-) I had a look at Mercurial after a friend suggested it and after you get used to the distributedness of it it is very pleasant. Subversion users will find commands familiar.


Peter Krantz wrote:
>A great way to get started with subversion is to install >Mercurial :-)

Oh yes, definitely! hg is IMHO a lot more simpler than svn. You can create a new "repo" in a second, create a new branch for free and it have real branching and merging capabilities.

You should really give it a try :-)


That's why I linked to Norm's recent posting. (Although, according to a more recent posting, he's been having some problems with Mercurial.)