Created Tuesday 21 Oct 2008
This document is an overview of the Subversion (svn) source code management (SCM). Subersion (subversion.tigris.org) is an opensource SCM providing transactional commits. The Wikipedia's comparison of revision control software compares svn favourably with other tools. However, svn is not client-server like p4 and does not provide natural integration operations from branching. However, like p4 svn provides the usual features like labelling (called tagging in svn) as well as 3-way diff and conflict resolution. Most notably however, svn does not provide strong analysis nor investigative tools. Most obviously lacking is the inability to determine (from the basic svn client) what versions of which files make up a particular revision. This type of query is provided by 3rd party tools like ViewVC, an OpenSource web-based svn browser or Fisheye, which is Non-Commerical.
Todo: Need a section on TorquiseSVN (installation, operation, integration with Exploader).
A any portion of the repository can be mapped to any location on the client. For example, to map root path ''' somesvnroot ''' /someproject/trunk to the local client as someproject:
bash $ svn co svn://somehost/somesvnroot/someproject/trunk someproject
In svn, all commits are virtual labels (tags). The --revision (-r) option can be used to syn the client to a specific revision. E.g.,
bash $ svn co -r 101 svn://somehost/somesvnroot/someproject/trunk someproject-version_101
Subversion supports client-server depots, with file transfer between the subversion client and the subversion server performed using a variety of protocols. The svn checkout in #1, above, uses subversion's own svn: prototol, which uses port 3690. Another option is to access the repository using http (port 80):
bash $ svn co http://svn.foo.com/some/module
There are a heap of svn tools, ranging from command line to ui's integrated into exploader and into IDE's like Exclipse:
SVN has limited changelist capability. To create a changelist use the changelist (or cl) command. Any file can be associated with a changelist. In the following example, two file path1/a.txt and path2/b.txt are associated with a change list called text_files. The first changelist command creates the changelist (assuming it didn't previously exist).
bash $ svn changelist text_files path1/a.txt path2/b.txt
Additional files can be associated with the changelist in the same way. E.g., the following attaches file path1/c.txt with existing changelist text_files
bash $ svn changelist text_files path1/c.txt
SVN commands can be directed to affect files in changelists. Any command which includes the -cl (or --changelist) option will provide this facilty.
Files can be moved between changelists by simply invoking the svn changelist command again, with the new changelist name.
The best way to view opened changlists is via TorquiseSVN. This will show the opened changelists and which files are associated with each. Files not associated with any change list appear as belonging to the default changelist. The TorquiseSVN also permits files to be easily moved around from changelist to changelist and provides a quick interactive diff facility.
1 Open Microsoft Explorer and navigate to top of working copy repository 2 Right-click then from exploader's popup context menu choose TorquiseSVN->Check for modifications. This opens a panel with the named changelists sorted alphabetically, with each associated file underneath.
To move files to different changelists, create new changelists or diff conent, use the right-click context menu after first selecting a target file.
Subversion uses a number of environment variables to configure operational behaviour and for forwarding operations onto 3rd party applications. The simplest example of this is the $SVN_EDITOR, which allows the user to configure which editor is invoked for operations that require text-editing (like changelist text on commit). SVN integration with merge tools is also possible by providing the pathname of a merge tools for $SVN_MERGE.
This variable controls which editor is invoked by subversion should the svn client need it. The default editor is the ex(1), which is a little awkward to use at the best of times. Set the value to either an absolute pathname to a preferred editor or simply specify the application name if the preferred app is in the path. E.g.,
bash $ export SVN_EDITOR=vim
The subversion conflict resolution process falls back to SVN_EDITOR if no merge tool is specified in SVN_MERGE. The merge tool must support 3-way diff and is fired up during svn resolve or svn update operations.
Note: A label can be created from any revision in the repository. Use the -r nnnn. E.g.,
bash $ svn copy -r 1001 -m "TEST" svn://path/trunk svn://path/tags/tagname
SVN Provides for p4-like labels, which it calls tags. A tag is actually a copy of depot location (not created from the client). Unfortunately because the tag is a copy this allows developers to check the label content out, modifiy and check it back in (which means the branch content is not updated.
To create a tag, issue svn's tag command, providing a repository source location and a repository target location. Typically, the source location will be eithe the trunk or a branch location and the latter will be a tag name under the tags directory. The tags directory must exist, so use svn's mkdir command to create it if it doesn't. The following example creates a tag called csde01-d3-p2 from a branch location called csde01-d3:
bash $ svn copy -m "Label csde01-d3-p2" \
svn://somesvnhost/somesvnroot/someproject/branches/csde01-d3 \
svn://somesvnhost/somesvnroot/someproject/tags/csde01-d3-p2
Committed revision 1141.
Another example: This label is created for the trunk revision of a project submitted at revision 1481.
bash $ svn copy -r 1481 -m "Foo release 1.0" \
svn://somesvnhost/projects/foo/trunk \
svn://somesvnhost/projects/foo/tags/Release1.0
It is always posible to simply sync a tag from the tags directory of a project. However, it is important that a tag not be updated (even though svn permits this operation). Tag are labels, which in svn speak are revisions. A revision is exactly that: a snapshot at a point in time and it is non-sensical to update a revision, given that two distinct clients with the same revision could have potentially different content. Quite simply: don't do it.
The better approach for update is to create a branch from a tag and then update the branch and then create new tags based on that branch (integrating the changes back to trunk, as required).
To check out a trunk or branch revision at given tag, it is first necessary to identify which revision was used to create the tag. The revision information of any file, including a tag can be determined using the info command.
bash $ svn info svn://somesvnhost/somesvnroot/poseidon/tags/csadapter-aleph-4 Path: csadapter-aleph-4 URL: svn://somesvnhost/somesvnroot/poseidon/tags/csadapter-aleph-4
Repository Root: svn://somesvnhost/somesvnroot Repository UUID: bd131156-1c2c-c9c8-d769-b6d48f3e4578 Revision: 3347 Node Kind: directory Last Changed Author: foo Last Changed Rev: 3310 Last Changed Date: 2009-05-11 19:21:57 +1000 (Mon, 11 May 2009)
Once the revision is identified, which is 3347 in the previous example, then the -r option of the checkout command can be used to sync the revision.
bash $ svn co -r 3347 svn://somesvnhost/somesvnroot/poseidon/trunk poseidon-csadapter-aleph-4
There are a heap of tools that provide SVN integration into the O/S desktop. A good example is TortoiseSVN, which provides enhanced SVN operations like repo browsing, merge and diff as well as basic svn operations like update and commit and filelog (info in svn-speak)
Merging (integrating) an SVN branch or tag revision is quite simple. The usful analysis commands are "info" (to find the revision to integrate), diff to check what's going to integrated, merge to do the copy/integrate and "resolved" to mark and conflicts as resolved. In doing an integration the working copy should be the destination. For example, if merging from branch back to trunk, then the current SVN working copy (WCOPY) should be an up-to-date revision of the trunk. The following scenario illustrates merging a branch revision called 'csde01-d3' of a project on server somesvnhost back to the trunk
SVN resvision numbers in the repository are implicit changelists. Once a revision number has been identified as containing changes to integrate, then the process is quite straight forward. The following example illustrates an integration from a branch called 'csde01-d3' back onto the trunk (note, the integration can also be done from a label (or tag in svn-speak). The revision being integrated is 1128 back to the head revision of the trunk.
The first step is to ensure that the SVN working copy (WCOPY) contains an up-to-date revision of the trunk.
bash $ cd svn/some.project.trunk bash $ svn update
The next step (optional) is to diff with the prior revision. This yeilds the patch (or differences) between the revision being integrated and the trunk. The svn diff command takes a revision specification, which is always given in the form -r S:D where S is the source (left side) and D is the destination (right side). The numeric arguments to -r are the commited revisions in the repository or the keyword HEAD. In our example we will diff 1128 with the prior revision 1127
bash $ svn diff -r 1127:1128 svn://somesvnhost/somesvnroot/someproject/branches/csde01-d3
After the diff, we can merge the differences. The svn merge command takes the same revision specification as the diff command but also requires the source svn repository url be given. Continuing the example, we are merging the csde01-d3 branch revision , so our source url is svn://somesvnhost/''' somesvnroot ''' /someproject/branches/csde01-d3. The --dry-run option can be given to svn so that the commands are only printed to stdout, not executed:
bash $ svn merge -r 1127:1128 --dry-run svn://somesvnhost/somesvnroot/someproject/branches/csde01-d3
Then do the merge if the dry run all looks good:
bash $ svn merge -r 1127:1128 svn://somesvnhost/somesvnroot/someproject/branches/csde01-d3
Tip: For windows, the TorquiseSVN provides a simple ui for conflict resolution. First use the check for modifications context-menu option in Explorer, then for each file listed as a conflict, open the 3-way diff/merge tool by choosing the edit conflict option from the modifications view context menu.
If there are any conflicts during the merge process, then these can be either resolved (edited) interactively by selecting e or postponed by choosing p as each conflict is listed. Postponed conflicts can be resolved in a number of ways, but must be marked as resolved when the conflicts are merged satisfactorily. Unless a decent 3-way mergetool is configured for svn, it's probably better to postpone and using something like TorquiseSVN to do the merge.
bash $ svn resolved file.txt
The final steps require that the integrated and resolved content be committed to the repository. A diff should be done immediately prior to the commit just to verify the changes being checked in to the trunk:
bash $ svn diff . bash $ svn commit .
The svn diff command can be given a date range or revision number. E.g., to diff content between 1 July 09 and 31 Aug 09:
bash $ svn diff -r{"2009-07-01"}:{"2009-08-17"} .
Tip: Use find(1), egrep(1) and svn status to establish the list of modified files.
Subversion is not like p4 in that there is no checkout for edit (etc) option. Instead, files are sync'd from the repository and can then be modified. This makes it a little difficult to determine which files are modified (added, updated, deleted). The svn status command can be used to establish the list of files to submit back to the repository.
The simplest way to execute the svn status is to use find(1) and then pump the results into svn. However, subversion uses the local filesystem to track state, which are held in the directory .svn, which is present for each file and directory known to the repository. All files located within all .svn directories (of which there are many) should be eliminated from the candidate list.
Executing svn status somefile results in an attribute being returned for the file in question. There are 9 response codes for status:
Table 1: SVN response codes to the status
The following scriptlet performs a status for each non-metadata file (i.e., not in .svn) from the current working directory. The find(1) used in the scriptlet looks for files only (-type f) because otherwise the status results will be duplicated if status is ussued both on the directory containing a given file and the on the file itself.
bash $ for f in $(find . -type f | egrep -v '.svn' | wc -l)
do
svn status $f
done
Using Table-1 above, it is easy to modify this scriptlet to look for (e.g.,) file not known to svn:
bash $ for f in $(find . -type f | egrep -v '.svn' | wc -l)
do
svn status $f | grep '^?'
done
The subversion server process is called svnserve and can be integrated into inetd or started as a daemon process from the command line.
Tip: See Red book:SVN Server
Configuring an svn server on a host is pretty straight forward. The svnserve executable is the server instance and can be configured as daemon (-d option) or started via inetd. To start a subversion in instance listening in daemon mode on the default port 3690, with repository access restricted to /da01/svn-repo:
bash # svnserve -d -r /da01/svn-repo
A simple verification for the server instance can be performed by telnet'ing to the svn host on the port that the instance was started on. In the above case, this is the default port, which is 3690:
bash $ telnet localhost 3690 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. ( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline svndiff1 absent-entries ) ) ) Connection closed by foreign host
The sever can be configured for client access in a number of different ways, including svn serve (unsecured), svn server via ssh, sasl and so on.
A typical init.d script for starting and stopping svnserve could be:
#!/bin/bash
SVN_REPO=${SVN_REPO:-/da01/svn-repo}
SVN_SERVER=${SVN_SERVER:=/usr/bin/svnserve}
action() {
svnpid() {
ps -elf | egrep "svnserve" | egrep -v "grep|$$" | awk '{ print $4 }'
}
start() {
echo "Starting svn server ${SVN_SERVER} in daemon mode for repository ${SVN_REPO}" 1>&2
${SVN_SERVER} -d -r ${SVN_REPO}
pid=`svnpid`
[ -n "$pid" ] && { echo "Started svn server daemon on pid $pid"; rt=0; } || { echo "Failed to start svn server daemon"; rt=1; }
}
stop() {
pid=`svnpid`
if [ -n "$pid" ]; then
echo "Stopping svn server daemon on pid=$pid..." 1>&2
kill $pid;
pid=`svnpid`
[ -z "$pid" ] && { echo "Stopped svn server successfully"; rt=0; } || { echo "Failed to stop svn server"; rt=1; }
else
echo "No svn server process found" 1>&2
fi
}
status() {
pid=`svnpid`
[ ! -z "$pid" ] && { echo "Found svn server running on pid $pid" 1>&2; } || { echo "No svn server process found" 1>&2; }
}
${1}
}
rt=0
case $1 in
start) action start ;;
stop) action stop ;;
status) action status ;;
*) echo arg1 must be either start, stop or status ;;
esac
exit $rt
Tip: See Read book:SVN Repository
Once the server is running, it will service svn client requests. However, a repository is needed for clients to add/check out files. Creating a repository is incredibly easy and is done with the svnadmin create command. The following example creates an svn repository under /da01/svn-repo. The ownership of the pathname for the repository must be the same as the process owner of the svnserve daemon.
bash # svnadmin create /da01/svn-repo
Note: Be sure that the password-db = passwd entry in the svnserve.conf is uncommented
Creating an svn repository also creates a configuration file for the svn daemon instance controlling that repository. The file <repo>/conf/svnserve.conf is used to control authentication and authorization to files in the repository. Using the server instance in 10.1, <repo> is /da01/svn-repo.
The default configuration allows read access for anonymous users but requires authentication for write access. User authentication is specified, by default in svnserve.conf in the file passwd (same directory). To create a user called foo with a password of foo, simply create the passwd file (if it doesn't already exist) and add the new user. The following assu,es the file doesn't exist:
bash # cd /da01/svn/conf bash # printf '[users]\nfoo = foo\n' > passwd bash # cat passwd [users] foo=bar
Once the repository is configured, content can be imported into the repository once it is configured and a server instance is running. The svn import command is used to import a directory structure into the repository. In the following example, the directory footest is imported by user foo:
Note: The import pathname and target svn repo location can be different
bash $ svn import footest --username foo --password foo svn://localhost/footest
Any files imported into the repository are now available for checkout by svn clients (see #1)
Note: The svn propset command is used to set property values for files in the repository
Many attributes for repository files are controlled by svn properties. A good example of this is the execution bit which controls whether or not a file will have the execution bit set on checkout (only works for unix platforms). The execution bit is controlled by the property svn:execution:
bash $ svn propset svn:execution \* foo.sh
Be sure to escape the asterisk at the shell
The svn tarball is trivial to build. However, if compiling the shared libraries, be sure to configure the ld.so.conf so that the shared libraries are found. The default install location from tarball configure is /usr/local/lib, and in this case, the simplest approach is to add a file in /etc/ld.so.conf.d as follows:
bash # echo '/usr/local/lib' > /etc/ld.so.conf.d/svn.conf
If the shared library location is not found, then some commands which require plugins (like svn over http) will fail with messages similar to svn: Unrecognized URL scheme. Also if using svn over http, then be sure that the rp_dav svn module is compiled and installed to the shared libary location.
The svn repository access for non svn schemes (e.g., http and https) require the ra_neon (used to be ra_dav) library. The configure script will compile this as a shared libary, so long as the neon headers can be found. For Fedora Linux, ensure the following rpm's are installed:
bash # yum install neon-devel.i386 neon
To access an svn server via http through a proxy, first make sure the SVN Neon repository access module is compiled into the svn client being used (See #12). If rp_neon support is compiled in then svn over http is supported and accessing a repository via a proxy only requires that the proxy authentication credentials be provided. The proxy credentials can be specified either per user in ~/.subversion/servers or globally within /etc/subversion/server. In either case, simply provide the proxy credentials, setting properties http-proxy-host, http-proxy-port, http-proxy-username and http-proxy-password
The servers file provides for proxy credentials to be specified for a subset of repository requests or for all svn repository accesses. In the following example, a server group called foosvn is created and ensurse that specific proxy credentials are used when accessing an svn repository with the text 'foo.svn.com' in the svn uri.
[groups] foosvn = *.foo.svn.com.* [foosvn] http-proxy-host = corporate-proxy http-proxy-port = 8080 http-proxy-username = someuid http-proxy-password = somepwd http-timeout = 60
It is obvious from the preceeding that it is trivial to setup multiple proxy credentials matched on different hostnames. E.g., it might be required to go through port 9999 when accessing svn.bar.com but 8888 when accessing svn.foo.com. If a global proxy proxy setting is preferred then simply include the proxy property values in the global section of subversion servers config (i.e., not within a group tag)
The http reposity access is then directed through the proxy. Completing the priror example, accessing a project bar on a server foo.svn.com becomes
bash $ svn co http://svn.foo.com/bar bar
Stuart Moorfoot 21 Oct 2008 foo@bund.com.au