4.2. Subversion (SVN)

Table of Contents

4.2.1. Terminology
4.2.2. Configuration
4.2.3. Setup
4.2.4. Updating your workspace
4.2.5. Restoring a Previous Version
4.2.6. Branches and Merges
4.2.7. Tags
4.2.8. Publishing a project
4.2.9. References
Subversion (SVN) is an open-source tool for source code maintenance.

4.2.1. Terminology

repository
Location where SVN keeps master copies of all files and directories it knows about, along with histories for those files.
project
Collection of files and directories that belong together, e.g. a program or library.
checkout
Make a local copy of the files to allow you to make and test changes locally (without affecting what others see).
commit
Save your changes back to the repository, where they are available to other users of the repository (also allows you to roll back to a previous version).
revert
Throw away your changes and reset the state of a file to what is currently in the repository.

4.2.2. Configuration

SVN configuration is saved in a file named ~/.subversion/config. The most useful part of this file is the ability to let SVN set properties automatically. These properties allow you to configure things like $Id: svn.xml 43 2006-06-07 15:12:28Z adalton $ expanding to a version string, doing end-of-line translation, etc. Below is a sample of the appropriate section of the config file:

### Automatic properties are defined in the section 'auto-props'.
enable-auto-props = yes

### Section for configuring automatic properties.
[auto-props]
### The format of the entries is:
###   file-name-pattern = propname[=value][;propname[=value]...]
### The file-name-pattern can contain wildcards (such as '*' and
### '?').  All entries which match will be applied to the file.
### Note that auto-props functionality must be enabled, which
### is typically done by setting the 'enable-auto-props' option.
# Scripts
*.sh         = svn:eol-style=native;svn:executable,svn:keywords=Id
*.bash       = svn:eol-style=native;svn:executable,svn:keywords=Id

# Text files, source files
Makefile     = svn:eol-style=native;svn:keywords=Id
*.xml        = svn:eol-style=native;svn:keywords=Id
*.c          = svn:eol-style=native;svn:keywords=Id
*.nc         = svn:eol-style=native;svn:keywords=Id
*.cpp        = svn:eol-style=native;svn:keywords=Id
*.cxx        = svn:eol-style=native;svn:keywords=Id
*.h          = svn:eol-style=native;svn:keywords=Id
*.sql        = svn:eol-style=native;svn:keywords=Id
*.java       = svn:eol-style=native;svn:keywords=Id
*.properties = svn:eol-style=native;svn:keywords=Id
*.shtml      = svn:eol-style=native;svn:keywords=Id
*.html       = svn:eol-style=native;svn:keywords=Id
*.htm        = svn:eol-style=native;svn:keywords=Id
*.css        = svn:eol-style=native;svn:keywords=Id
*.jsp        = svn:eol-style=native;svn:keywords=Id
.htaccess    = svn:eol-style=native;svn:keywords=Id
*.txt        = svn:eol-style=native;svn:keywords=Id
*.tex        = svn:eol-style=native;svn:keywords=Id
*.dsp        = svn:eol-style=CRLF;svn:keywords=Id
*.dsw        = svn:eol-style=CRLF;svn:keywords=Id

# Images
*.gif        = svn:mime-type=image/gif
*.png        = svn:mime-type=image/png
*.jpg        = svn:mime-type=image/jpeg
*.jpeg       = svn:mime-type=image/jpeg

# Binary
*.jar        = svn:mime-type=application/octet-stream
*.pdf        = svn:mime-type=application/pdf

4.2.3. Setup

First, create the repository:

$ svnadmin create /home/userid/svn
$ ls /home/userid/svn
README.txt  conf/  dav/  db/  format  hooks/  locks/
Next, create the initial directory structure for the repository. The authors of SVN suggest there be one directory per project, and that directory be subdivided into three subdirectories, branches, tags, and trunk. We will discuss the purpose of each of these subdirectories later in this document.
$ cd /tmp
$ mkdir -p temp/project/trunk/project temp/project/tags temp/project/branches
Now we are ready to import the initial directory structure for the project:
$ svn import /tmp/temp file:///home/userid/svn -m "Importing initial project structure"
Adding         /tmp/temp/project
Adding         /tmp/temp/project/trunk
Adding         /tmp/temp/project/trunk/project
Adding         /tmp/temp/project/branches
Adding         /tmp/temp/project/tags

Committed revision 1.
You may now safely delete/move the temp project directory structure.
$ rm -rf /tmp/temp

Note: You may import an unrestricted number of projects into a repository.

Next, we will check out a copy of our empty project from SVN and add project files to it:

$ cd /home/userid
$ svn checkout svn checkout file:///home/userid/svn/project/trunk/project
Checked out revision 1.
$ cd project
$ cp -r /home/userid/src/project/* .
$ svn add *
A         lab1
A         lab1/Makefile
A         lab2
$ svn commit -m "Initial code added"
Adding         lab1
Adding         lab1/Makefile
Adding         lab2
Transmitting file data .
Committed revision 2.

Now that our project has been checked out, we are free to make changes to it. Once you have made your changes, you must commit them to the repository. Each time you commit to the repository, SVN will prompt you to enter a log message that describes the changes. You may supply the message using the -m flag; if you do not supply a message at the command line, SVN will open an instance of $EDITOR.

To see what would happen if you were to commit:

$ echo "# This is a comment" >> /home/userid/project/lab1/Makefile
$ svn status
M      lab1/Makefile

To see what has changed since last you committed:

$ svn diff
Index: lab1/Makefile
===================================================================
--- lab1/Makefile       (revision 2)
+++ lab1/Makefile       (working copy)
@@ -0,0 +1 @@
+# This is a comment

To get detailed information about a file or directory in the repository:

$ svn info lab1/Makefile
Path: lab1/Makefile
Name: Makefile
URL: file:///home/userid/svn/project/trunk/project/lab1/Makefile
Repository Root: file:///home/userid/svn
Revision: 2
Node Kind: file
Schedule: normal
Last Changed Author: userid
Last Changed Rev: 2
Last Changed Date: 2006-06-07 03:10:38 -0400 (Wed, 07 Jun 2006)
Text Last Updated: 2006-06-07 03:10:09 -0400 (Wed, 07 Jun 2006)
Properties Last Updated: 2006-06-07 03:10:24 -0400 (Wed, 07 Jun 2006)
Checksum: d41d8cd98f00b204e9800998ecf8427e

To view the revision log for a file or directory in the repository:

$ svn log lab1/Makefile
------------------------------------------------------------------------
r2 | userid | 2006-06-07 03:10:38 -0400 (Wed, 07 Jun 2006) | 1 line

Initial code added
------------------------------------------------------------------------

To actually commit the changes we have made:

$ svn commit -m "Added comment to Makefile"
Sending        lab1/Makefile
Transmitting file data .
Committed revision 3.

Eventually, you will want to add more files to your project:

$ cd /home/userid/project/lab1
$ touch draw.c
$ svn add draw.c
A         draw.c
$ svn commit -m "Added draw.c"
Adding         lab1/draw.c
Transmitting file data .
Committed revision 4.
You will likely want to remove files as well:
$ cd /home/userid/project/lab1
$ svn remove draw.c
D         draw.c
$ svn commit -m "Removed draw.c"
Deleting       lab1/draw.c

Committed revision 5.
You might even want to move (or rename) something:
$ cd /home/userid/project/
$ svn mv ./lab1/Makefile ./lab2
A         lab2/Makefile
D         lab1/Makefile
$ svn commit -m "Moved Makefile to lab2"
svn commit -m "Moved Makefile to lab2"
Deleting       lab1/Makefile
Adding         lab2/Makefile

Committed revision 6.

4.2.4. Updating your workspace

Before you commit your changes, you'll need to have the latest version of the project. If other users are simultaneously updating your project, you must keep your local version in sync with the repository. To update your workspace:

$ cd /home/userid/project
$ svn update
A    lab1/hello.c
Updated to revision 7.
Here we see that another user has added the file hello.c to the lab1 directory since the time we checked out the project.

4.2.5. Restoring a Previous Version

To restore a project to its state at a given revision number in the past, use the --revision flag:

$ cd /home/userid/tmp
$ svn log file:///home/userid/svn/project/trunk/project
------------------------------------------------------------------------
r7 | userid | 2006-06-07 03:14:59 -0400 (Wed, 07 Jun 2006) | 1 line

Added hello.c
------------------------------------------------------------------------
r6 | userid | 2006-06-07 03:14:07 -0400 (Wed, 07 Jun 2006) | 1 line

Moved Makefile to lab2
------------------------------------------------------------------------
r5 | userid | 2006-06-07 03:13:42 -0400 (Wed, 07 Jun 2006) | 1 line

Removed draw.c
------------------------------------------------------------------------
r4 | userid | 2006-06-07 03:13:19 -0400 (Wed, 07 Jun 2006) | 1 line

Added draw.c
------------------------------------------------------------------------
r3 | userid | 2006-06-07 03:12:36 -0400 (Wed, 07 Jun 2006) | 1 line

Added comment to Makefile
------------------------------------------------------------------------
r2 | userid | 2006-06-07 03:10:38 -0400 (Wed, 07 Jun 2006) | 1 line

Initial code added
------------------------------------------------------------------------
r1 | userid | 2006-06-07 03:07:45 -0400 (Wed, 07 Jun 2006) | 1 line

Importing initial project structure
------------------------------------------------------------------------
$ svn checkout --revision 5 file:///home/userid/svn/project/trunk/project
A    project/lab1
A    project/lab1/Makefile
A    project/lab2
Checked out revision 5.
We could also use a date and time as a revision:
$ svn checkout --revision "{2006-06-07 03:14:07 -0400}" file:///home/userid/svn/project/trunk/project
A    project/lab1
A    project/lab1/Makefile
A    project/lab2
Checked out revision 5.

4.2.6. Branches and Merges

Branches allow you to maintain separate versions of a projects, e.g. a development version, a testing version, and an unstable version. This is where the branches subdirectory comes into play. To create a branch, simply create a copy of what you want to branch from to the branches directory:

$ svn copy file:///home/userid/svn/project/trunk file:///home/userid/svn/project/branches/new_feature_dev \
-m "Create development branch for new feature"

Committed revision 12.
After this copy has been created, you can check out a copy of that branch for development:
$ cd /home/userid
$ mkdir development
$ cd development
$ svn checkout file:///home/userid/svn/project/branches/new_feature_dev/project
A    project/lab1
A    project/lab1/hello.c
A    project/lab2
A    project/lab2/Makefile
Checked out revision 12.
Now we might add a new directory and a new source file:
$ cd project
$ mkdir lab3
$ touch lab3/shape.c
$ svn add lab3
A         lab3
A         lab3/shape.c
$ svn commit -m "Added lab3"
Adding         lab3
Adding         lab3/shape.c
Transmitting file data .
Committed revision 13.

Eventually, you'll be finished with your development effort and will want to merge your changes back to the trunk. The problem is another user may have already merged changes to the trunk. First, we'll merge any changes from trunk to our development branch (make sure everything is okay), then merge our changes to trunk.

First, find out what was the revision was before the branch:

$ svn log file:///home/userid/svn/project/branches/new_feature_dev
------------------------------------------------------------------------
r13 | userid | 2006-06-07 03:29:17 -0400 (Wed, 07 Jun 2006) | 1 line

Added lab3
------------------------------------------------------------------------
r12 | userid | 2006-06-07 03:20:57 -0400 (Wed, 07 Jun 2006) | 1 line

Create development branch for new feature
------------------------------------------------------------------------
r7 | userid | 2006-06-07 03:14:59 -0400 (Wed, 07 Jun 2006) | 1 line

Added hello.c
------------------------------------------------------------------------
r6 | userid | 2006-06-07 03:14:07 -0400 (Wed, 07 Jun 2006) | 1 line

Moved Makefile to lab2
------------------------------------------------------------------------
r5 | userid | 2006-06-07 03:13:42 -0400 (Wed, 07 Jun 2006) | 1 line

Removed draw.c
------------------------------------------------------------------------
r4 | userid | 2006-06-07 03:13:19 -0400 (Wed, 07 Jun 2006) | 1 line

Added draw.c
------------------------------------------------------------------------
r3 | userid | 2006-06-07 03:12:36 -0400 (Wed, 07 Jun 2006) | 1 line

Added comment to Makefile
------------------------------------------------------------------------
r2 | userid | 2006-06-07 03:10:38 -0400 (Wed, 07 Jun 2006) | 1 line

Initial code added
------------------------------------------------------------------------
r1 | userid | 2006-06-07 03:07:45 -0400 (Wed, 07 Jun 2006) | 1 line

Importing initial project structure
------------------------------------------------------------------------
Here, we see that revision 7 was the revision prior to the creation of our branch.

Next, we'll check for any changes to trunk since that revision:

$ svn diff -r 7:HEAD file:///home/userid/svn/project/trunk/project
Index: lab4/io.c
===================================================================

Property changes on: lab4/io.c
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native
Here, we see there have been changes we'll need to merge. Next, we'll merge those changes from trunk to our development branch:
$ cd /home/userid/development
$ svn merge -r 7:HEAD file:///home/userid/svn/project/trunk/project
A    lab4
A    lab4/io.c
$ svn commit -m "Back-merge of revisions 7:HEAD of trunk to development branch"
Adding         lab4
Adding         lab4/io.c

Committed revision 15.

Now, we check to make sure that our code still works after we've merged the changes from trunk. If everything is OK, we'll merge our changes back to trunk:

$ cd /home/userid/project
$ svn update
At revision 15.
$ svn merge -r 12:13 file:///home/userid/svn/project/branches/new_feature_dev/project
A    lab3
A    lab3/shape.c

And finally, we commit our changes:

$ svn commit -m "Merge changes 12:13 from branch 'new_feature_dev' to trunk"
Adding         lab3
Adding         lab3/shape.c

Committed revision 16.

4.2.7. Tags

Sometimes you might want to take a snapshot of your project at a given point in time. This might be especially useful if you release multiple versions of your software, to take a snapshot of those versions. To do this with SVN, you use tags.

Creating tags with SVN is exactly like creating branches; however, by convention you don't modify code in a tag copy. The tag copy is only for checking out the snapshot.

To create a tag:

$ svn copy file:///home/userid/svn/project/trunk file:///home/userid/svn/project/tags/0.9.4 \
-m "Tag version 0.9.4 of the project"

Committed revision 17.

4.2.8. Publishing a project

To do its job, SVN adds many auxiliary files to your workspace. To obtain a copy of the project without these added files:

$ cd /home/userid/public_html
$ svn export file:///home/userid/svn/project/tags/0.9.4/project
A    project
A    project/lab1
A    project/lab1/hello.c
A    project/lab2
A    project/lab2/Makefile
A    project/lab3
A    project/lab3/shape.c
A    project/lab4
A    project/lab4/io.c
Exported revision 17.

4.2.9. References

http://svnbook.red-bean.com/nightly/en/index.html