SVN notes

Jump to navigationJump to search

Why use Subversion?

As near as I can figure these are the reasons that people use Subversion for Version Control:

  1. We fear change.
  2. Was familiar with CVS and knew they had to advance beyond the dark ages.
  3. Nobody really values Version Control
  4. Heard that moving from CVS to SVN was supposed to require the least amount of effort. Version Control does matter to anyone so why go out of your way?


SVN tags are a complete failure. Which is to say they decided that tags are equivalent to a copy, so you should just use that. This causes frequent tagging to be so expensive that it isn't practical. Of course, you could just keep a file that you update with a Revsion ID and an associated tag name. Then if you need to restore to a tag you can just do something like this, svn co -r ${REV_FROM_TAG} ${PROJECT_URL}.

get the SVNROOT from a local checkout directory

This is handy in scripts that need to get the SVNROOT of the current working copy.

svn info . | grep "Repository Root: " 2>&1 | cut --delimiter=' ' --fields=3

Subversion remote access

After playing with WEBDAV and SVN Server (http:// and svn://) I settled on using svn+ssh://. It is secure and it requires little or no setup or service to run on the host (besides sshd). I don't know why anyone would use anything else. With a few SSH keys it's as easy to use locally as it is to use remotely. It's dead simple and reliable. The only disadvantage of svn+ssh is that you cannot use fine-grained, per-directory authorization to restrict access to files for certain users. You might encounter this problem if you want to allow a consultant to access only one part of a repository.

authz authorization with svnserve

Start svnserve with the root pointing to the directory ABOVE your repository files. For example, if I had a project repository under "/home/svn/project_a" I would use the following to test svnserve:

# svnserve -d --foreground --root /home/svn

If I wanted to allow employees access to everything, but restrict a consultant named "sid" to a branch but allow read access to the trunk I would create an authz file something like this:

employees = bob, sue, jim, ted

@employees = rw

sid = r

sid = rw

password caching

The svn client will cache passwords under "~/.subversion/auth/svn.simple/". The big downside of this is that if you enter the wrong password when you first login you will forever be shut out until you delete a file under "~/.subversion/auth/svn.simple/". You will need to delete the file associated with the svn server that you are trying to login to. Cat each file to find the right one... Yes, this does seem like a bug. If svn can't login with the password you gave it then why should it cache that password and use it again later without asking. Unfortunately, the "--no-auth-cache" option only prevents svn from caching the password in the first place. It doesn't prevent it from using a cached password that already exists.

svnserve init.d script

This works on both RedHat and Ubuntu systems.

<include svncat src="file:///home/svn/src/shell/svnserve" highlight="sh" />

Subversion setup for team development (svn+ssh)

The easiest way to setup a repository for distributed developers is with the svn+ssh method. The connection is secure over SSH. The downside is that you loose fine-grained control over which files and directories a given user can see. You can limit access to the entire repository, but not to directories inside a repository.

If you want to share a repository among several developers you will need to configure a few trivial things.

Create a user "svn" and create the repository under "svn". This assumes the "svn" user will have a home directory of /home/svn. Here are the exact steps starting as root user:

# adduser svn
# su - svn
$ pwd
$ svnadmin create /home/svn/src

Make the svn repository group writable. This allows developers in the svn group to commit changes. In this example the repository is called 'src':

chmod -R g+w /home/svn/src

Add each developer to the svn group (usermod or edit /etc/group). Your repository may use a different group, such as "apache" or "svnadmin".

usermod -a -G svn username

Now each developer in the svn group can checkout a project using an svn+ssh URL to the repository:

svn co svn+ssh://

Hook scripts for protecting repository

This prevents anyone who had a merge conflict from simply called "svn resolved" and then committing the file with conflict markers. This looks for patterns in the source that look sort of like this:


Put the following in a script somewhere and then modify your 'pre-commit' script to call your script. Your pre-commit script will be in a directory somewhat like this: /home/svn/my_repository/hooks/rec-commit.

# The pre-commit hook is invoked before a Subversion txn is
# committed.  Subversion runs this hook with the following ordered arguments:
#   [1] REPOS-PATH   (the path to this repository)
#   [2] TXN-NAME     (the name of the txn about to be committed)


# Scan through the transaction diff looking for things that look
# like conflict markers. If we find one, we abort the commit.
SUSPICIOUS=$($SVNLOOK diff -t "$TXN" "$REPOS" | grep -E '^\+(<{7} \.|={7}$|>{7} \.)' | wc -l)
if [ $SUSPICIOUS -ne 0 ]; then
    echo "Some parts of your checkin look suspiciously like merge conflict markers." >&2
    echo "Please check your diff and try again." >&2
    exit 1

# python /home/svn/vinyl/hooks/ -v "$TXN" "$REPOS" >&2
# if [ $EXIT_STATUS -ne 0 ]; then
#     echo "Delete locks before you checkin." >&2
#     exit 1
# fi


Subversion merging is painful. It's confusing; it's not easy to visualize what is happening; and you have to manually keep track of the revision numbers between the sections you want to merge. But no one said that merging was supposed to be easy. Subversion is free, so less complaining and more work!


Branching is simply a "copy" operation. For example say you want to create a new branch from a project on the trunk:

svn copy svn+ssh://

Now if you go into your working copy branch and run "svn up" you should see "my_project_branch_B" get added to your working copy.

Normally you would have to keep track of the revision number that the branch was copied from. This is crazy. Instead see


Before you do anything make sure your branch working copy is up to date and committed (`svn up` then `svn ci`).

Merging always happens to your working copy. You might merge between remote URL A and remote URL B, but the results of that merge is stored in your local working copy. This tends to confuse people at first. This is why you want to make sure you working copy is up to date and committed; otherwise, you will get a three way merge which can be even more confusing.

   A      B
    \    /
     \  /
 working copy

Merging Enhancements with

The standard Subversion distribution comes with a handy script called that keeps track each time you merge in either direction. This allows you to incrementally merge changes without having to manually keep track of revision numbers. Don't merge without it.

By default it expects to be run in a branch working copy and to merge from the trunk down to the branch. init

But it can run in a trunk working copy and merge from any branch up to the trunk. init BRANCH_URL

What revision was a branch created from?

Merging requires that you know at what revision a branch was branched from. When you did the "svn copy" the new branched copy got a logged with a revision number. This is tricky for a beginner because intuitively you expect Subversion to merge from one branch to another without requiring the user to keep track of branch revision numbers.

The easiest way to get the starting revision of a branch is this command:

svn log --verbose --stop-on-copy svn+ssh://

How to merge to or from the trunk

Use the trick above to get the revision you branched from. Assume this gives you a revision number 17.

merge FROM branch TO trunk

Do this if you want to merge changes from the branch to the trunk.

In your trunk working copy run this command:

svn merge -r 17:HEAD svn+ssh://

merge FROM trunk TO branch

Do this if you want to merge changes from the trunk to the branch.

In your branch working copy run this command:

svn merge -r 17:HEAD svn+ssh://

The tricky thing to remember is that now your branch gets the latest Revision number. The next time you want to do an update from the trunk you need to use this new revision number! Yikes! Save yourself some trouble and use

SVN Revert -- Merge back to previous version

Sometimes you need to roll-back to a previous version after you have committed changes. For this you use merge. You merge the changes from a previous version into your working copy. Then you must commit your merged working copy if you want to make the roll-back visisble on the SVN server. In the example below PREV and HEAD are aliases that svn interprets, so you may not even have to know the exact revision numbers you are merging from and to:

svn merge -r PREV:HEAD my_file.txt
svn ci my_file.txt

Mass resolve

This does a resolve all. This will very likely result in a bunch of garbled files. All of the conflicted files will be mangled. But I found this useful from time to time when I know what I'm doing or if I am just going to "svn rm" a whole directory anyway and massive conflicts must be resolved before svn will allow the "rm" command.

svn status | grep ^C | awk '{print $2}' | xargs svn resolved

Date ranges for revision log messages

Good news: revision ranges can take dates instead of a revision number. Dates put between {curly braces}. Unfortunately, the SVN docs are unclear on how to read revision logs by date. You almost certainly want to use a date range. You can use a single date, but it would have to exactly match one in the log down to the second. That's not very useful, so a date range makes more sense. For example, the following shows the logs for a six month range:

svn log -r {2006-12-25}:{2007-6-25}

What is svn going to do to my files when I update?

It's annoying that "svn status" does not do this. The status command only looks at your local revisions. Even if you add '-u' and do "svn status -u" you will still not tell you if there will be any conflicts or automatic merging. It will only show "M" or "*" (newer on server), which is misleading because you might think that you can Update or Commit without conflicts. Here svn does not follow the principle of least surprise.

The solution is to use the merge command. The "svn up" command is just a special form of merging. You can use merge to do an update. The key is that "svn merge" supports the "--dry-run" option, so you can see what files would get thrashed before they actually get thrashed.

svn merge --dry-run -r BASE:HEAD .

This would be a lot simpler if "svn up" supported "--dry-run" or if "svn status" did what you think it should do.

Note that "svn merge" isn't exactly like "svn up", but for the purposes of detecting merges and conflicts before they happen it's close enough.

CR vs. CR/LF -- set text file line endings

This will recursively set all text files (will ignore binary files) so that svn will change text file line endings on checkout.

svn propset -R svn:eol-style native *

Set to always CR (UNIX):

svn propset -R svn:eol-style CR *

Set to always CRLF (Windows):

svn propset -R svn:eol-style CRLF *


This sets keyword expansion for the Id keyword.

svn propset svn:keywords Id *

Put $Id$ in your code and it will be expanded to something like:

$Id: calc.c 148 2002-07-28 21:30:43Z sally $

Execute permissions

This sets execute permission on "":

svn propset svn:executable

Note that you cannot set read and write permission or ownership.

Create and checkout a directory in place of an existing directory

You want to add an existing directory to svn but you don't want to do "svn import ..."; delete the import directory; then checkout the directory back in-place where you had it to begin with. For example, say you want to add your /etc directory to svn. For this example you should be in the directory you want to add. That is, your current working directory should be the directory you want to add. Here we add /etc to an SVN repository.

cd /etc
svn mkdir file://home/svn/root/trunk/etc -m"Created /etc"
svn checkout file://home/svn/root/trunk/etc/. .
svn add *
svn add .*
svn ci

Status codes

Subversion prints letter codes next to the filenames to indicate their status. You will see status when you do an update, merge, or status:

svn status:

   This means that you have no changes to your working copy, but 
   the svn server has new changes that will update your working copy.
   This means that you have changes to your working copy and 
   the svn server has new changes that will be merged automatically 
   into your working copy. BEWARE! SVN is writing code for you.
   I never trust this.
   This means that you have changes to your working copy and
   the svn server has new changes that conflict with your changes.
   These changes cannot be merged automatically.
   Your working copy will now contain conflict markers (<<<<<<<), so
   it will be broken until you edit and fix the conflicts.
   You will also have to tell Subversion that the conflict is resolved
   ("svn resolved myfile.txt").

Error: Cannot replace a directory from within

If you see this it probably means that your project directory was moved. If you have checked out the directory above then usually going back to the root and running "svn up" will fix this (it will rename your project directory). But probably you did not checkout every directory above this project, so you can't go up and run 'svn up'. Don't panic.

svn switch

You almost always need the relocate switch:

svn switch --relocate

Error: The URL 'FOO' has a different repository root than its parent

If you see this when trying to merge it probably means that you are using a different user or address for the merge URL than the URL used to check out your working copy. The URL to the repository portion has to be exactly the same. Sometimes this happens if I check out a working copy with my own username but then try to merge to the repository using the username of the SVN user.

Error: Error replacing text-base of ...

This seems to only be a problem on Windows clients (usually Tortoise). It was always fixed after the user does an 'svn cleanup' on the project and then commits again. I am not sure of the cause, but I think it is something to do with eol-style. I am not sure if this is due to the svn:eol-style being absent or if it is set to 'native'. I suspect it is due to the eol-style property being absent. I always set it to 'native' for text files.

Error: svn: Failed to add file 'foo': object of the same name already exists

This happens when someone committed two files with the same name to the repository; the filenames differ only by capitalization; and you are trying to checkout onto a filesystem that is "case preserving" but not "case sensitive". You see this case problem on Mac OSX and Windows NTFS. For example, these two files will not checkout on OSX or Windows "README" and "readme", but they coexist fine on a UNIX filesystem.

This will also happen with directory names:

svn: Failed to add directory 'foo': object of the same name already exists