[ Prev ] [ Index ] [ Next ]

Subversion

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).

1. Client mapping

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


==== 1.A Sync a specific revision ====

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


==== 1.B Protocols and content transfer ====

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


===== 2. 3rd party svn tools =====

There are a heap of svn tools, ranging from command line to ui's integrated into exploader and into IDE's like Exclipse:

* TorquiseSVN - Repo integration within the windoze exploader
* Subclipse - Repo integration within Eclipse
* Subcommander - Gnome based ui for svn interaction
* RapidSVN - SVN ui

===== 3. Changlelists =====

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.

===== 4. SVN and the shell environment =====

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''.

==== 4.1 SVN_EDITOR ====

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



==== 4.2 SVN_MERGE ====

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.


===== 5. Labelling (Tagging) =====

{{../.images/stock/info.png}} **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


==== 5.2 Sync'ing to a tag ====

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



===== 6. SVN and the windoze desktop =====

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)


===== 7. Integration/Merge =====

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 .

7.B Date-based diff

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"} .

8. Detecting modifications

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')
do
    svn status $f | grep '^?'
done

9. External links and documentation

10. SVN Server

The subversion server process is called svnserve and can be integrated into inetd or started as a daemon process from the command line.

10.1 Starting an svn server instance

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.

10.2 Basic init.d script for svnserve

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

10.3 Creating an svn repostory

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

10.4 Creating svn users

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

10.5 Importing a project

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)

11. SVN Properties

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

12. Building SVN from source

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

13. SVN http via proxy

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


Backlinks: cvs port-forward-cvs oracle