nordisch by nature » das meer ruft

Laying out your repository

Tags:

If you come to #svn and ask about what layout to choose for a repository, you will most likely will get this answer:

How you setup your repository is really up to you. It’s a management question. See http://svnbook.red-bean.com/nightly/en/svn.reposadmin.projects.html#svn.reposadmin.projects.chooselayout for a discussion on what the Subversion team thinks is best practice.

But in most cases that factoid doesnt answer all questions that pop up. You instantly get questions like:

  1. (#q1) “Do you store multiple projects in a repository?”
  2. (#q2) “How do you store multiple projects?”
  3. (#q3) “Why do i need those branches and trunk dir? i will never do such fancy stuff.”

It seems layouting your repository is not as easy as it seems. Anyway lets see what we can do here.

what is a repository?

A repository is basically a versioned filesystem. This point seems to confuse many CVS users. Branches, tags and your mainline tree are only subdirectories in this filesystem. Getting used to this idea is very important.

CVS people keep in mind there is not a different layer/axis for branches/tags. Those are only subdirs somewhere in your repos.

This should answer the 3rd question. Whenever you need a branch for just testing stuff you will curse yourself, that you did not layout your repository properly. Of course svn allows you to fix the problem with some “svn mkdir” and “svn move”. But that complicates the diff/merge into with old revisions. As you already have taken the time to read this howto. Do yourself a favor and stick to the common subdirs. We will put in some more details as the discussion continues.

One repository to rule them all

In many cases this is a good decision. It works really well. Even at large scale:

I suggest to keep related projects inside one repository. This solution has multiple advantages. First of all you have all related code in one place. Furthermore you can merge between the projects if needed. This would not be possible with svn. (svk doesnt count here;)

Another point you should take into account is access control. Until subversion 1.2 only the Apache2 based solution allowed us to use finegrained access control. As of version 1.3 svnserve has that feature too. If you have to use an older subversion binary and Apache2 is not an option either, you might be forced to use seperate repositories just for the sake of access control for different subprojects. But dont worry. Once you can upgrade you can easily fix that1.

Layouting the repository

Single project repos.

This is easy. I suggest following suggestions from the svn book

1 Assuming you have all repositories for projectA in /srv/svn/projectA and your new repos is /srv/svn/projectA-all:

for repos in /srv/svn/projectA/* ; do
    REPOSNAME="$(basename $repos)" ;
    svn mkdir -m "- basedir for project $REPOSNAME" file:///srv/svn/projectA-all/$REPOSNAME ;
    svnadmin dump $repos | svnadmin load --parent-dir $REPOSNAME /srv/svn/projectA-all/ ;
done

Websites in svn

Tags:

For this little discussion i assume the standard layout as described here

Development phase

During the development you want to see your changes live instantly. That is pretty easily solvable with a hook script as described in this FAQ

A little sidenote to the FAQ. if your system ships with sudo, you can use that instead of the custom C binary above.

So far every thing was pretty simple.

Releasing

I personally prefer release branches. every time a new milestone goes live. I branch trunk to branches/release-1.2.x and keep that around for bugfixing. Trunk will be used for the ongoing development.

My production site always runs from a tag. That makes it easy to step backwards in the releases. Just a simple “svn switch url/tags/sometag”.

Conclusion

As you see versioning website is not that much different from “normal code”.

Authenticate with Apache against the svnserve passwd

Tags: , ,

About

mod_authn_svn.c allows sharing the password file between Apache 2.1 and svnserve. It reads the svnserve password files and authenticates users against it.

Do not mix this up with mod_authz_svn.c. mod_authz_svn.c does path based access control and not user authentication.

The current version is 0.1.0

It is licensed under the Apache License, Version 2.0

compile/install

You need Apache 2.1 and Subversion 1.2.

apxs -ci -I/path/to/include/subversion-1 mod_authn_svn.c

Usage

###
### load the needed module
###
### dependencies: mod_dav_svn, mod_auth_digest or mod_auth_basic
###
#
# LoadModule authn_svn_module modules/mod_authn_svn.so
#
###
### for HTTP BasicAuth:
###
#
# AuthBasicProvider svn
#
###
### for HTTP DigestAuth:
###
#
# AuthDigestProvider svn
#
### To specify the password file you can use:
### AuthnSVNUserFile [svnserve|path]
###
### - svnserve only works inside svn locations
### - path works every but is meanly meant for non svn locations.
###   like viewcvs
#
# AuthnSVNUserFile svnserve
#

ToDo

  • add caching similar to mod_auhtz_svn

If you use this module commercially, or appreciate my work, please consider making a donation via PayPal to darix@web.de .

Subversion and permissions

Tags:

Quite often I hear people complaining “BDB sucks, it breaks here twice a day! I will use FSFS now. BDB just sucks”. This seems true at first. But in many cases, it’s an administration error. :)

As the svn book and FAQ point out in many places, you need to take care of the permissions. The first important link with BDB and permissions might be the FAQ entry “Why do read-only operations still need repository write access?”. It explains why BDB requires write permission to the repository directory even for readonly operations. At this point, FSFS seems distinctly better than BDB, because FSFS only requires read permissions for readonly operations.

(1) For our checklist we keep in mind that a user needs write permissions for reading the repos.

Now we have to sort out the question “Who needs access to our repos?” I will discuss some common setups here.

The basic setup

$ mkdir -p /srv/svn/repositories
$ svnadmin create /srv/svn/repositories/project01
$ cd /srv/svn/repositories/project01
$ find . -ls
31320846    0 drwxr-xr-x   7 root     root           90 Apr  9 00:14 .
2272808    0 drwxr-xr-x   2 root     root            6 Apr  9 00:14 ./dav
7178361    0 drwxr-xr-x   2 root     root           39 Apr  9 00:14 ./locks
7178364    4 -rw-r--r--   1 root     root          460 Apr  9 00:14 ./locks/db.lock
7178366    4 -rw-r--r--   1 root     root          295 Apr  9 00:14 ./locks/db-logs.lock
8854953    4 drwxr-xr-x   2 root     root         4096 Apr  9 00:14 ./hooks
11228932    4 -rw-r--r--   1 root     root         2137 Apr  9 00:14 ./hooks/start-commit.tmpl
11228937    4 -rw-r--r--   1 root     root         2944 Apr  9 00:14 ./hooks/pre-commit.tmpl
11228939    4 -rw-r--r--   1 root     root         2764 Apr  9 00:14 ./hooks/pre-revprop-change.tmpl
11228940    4 -rw-r--r--   1 root     root         2016 Apr  9 00:14 ./hooks/pre-lock.tmpl
11228943    4 -rw-r--r--   1 root     root         1998 Apr  9 00:14 ./hooks/pre-unlock.tmpl
11228944    4 -rw-r--r--   1 root     root         2015 Apr  9 00:14 ./hooks/post-commit.tmpl
11228946    4 -rw-r--r--   1 root     root         1637 Apr  9 00:14 ./hooks/post-lock.tmpl
11231620    4 -rw-r--r--   1 root     root         1564 Apr  9 00:14 ./hooks/post-unlock.tmpl
11237184    4 -rw-r--r--   1 root     root         2255 Apr  9 00:14 ./hooks/post-revprop-change.tmpl
13642827    0 drwxr-xr-x   2 root     root           39 Apr  9 00:14 ./conf
13642830    4 -rw-r--r--   1 root     root         1078 Apr  9 00:14 ./conf/svnserve.conf
13642831    4 -rw-r--r--   1 root     root          311 Apr  9 00:14 ./conf/passwd
31320848    4 -rw-r--r--   1 root     root          379 Apr  9 00:14 ./README.txt
31320850    4 -r--r--r--   1 root     root            2 Apr  9 00:14 ./format
<strong>18259246    0 <u>drwxr-sr-x</u>   5 root     root          120 Apr  9 00:14 ./db</strong>
18272163    4 -rw-r--r--   1 root     root            5 Apr  9 00:14 ./db/fs-type
23169557    0 drwxr-sr-x   2 root     root           14 Apr  9 00:14 ./db/revs
23169558    4 -rw-r--r--   1 root     root          115 Apr  9 00:14 ./db/revs/0
27193648    0 drwxr-sr-x   2 root     root           14 Apr  9 00:14 ./db/revprops
27193649    4 -rw-r--r--   1 root     root           50 Apr  9 00:14 ./db/revprops/0
31320852    0 drwxr-sr-x   2 root     root            6 Apr  9 00:14 ./db/transactions
18272185    4 -rw-r--r--   1 root     root            6 Apr  9 00:14 ./db/current
18272212    0 -rw-r--r--   1 root     root            0 Apr  9 00:14 ./db/write-lock
18273153    4 -rw-r--r--   1 root     root           37 Apr  9 00:14 ./db/uuid
18273164    4 -r--r--r--   1 root     root            2 Apr  9 00:14 ./db/format
         

(2) In the output of “find” we can already see one important point: newer svn releases set the ”<a href=”http://www.gnu.org/software/coreutils/manual/html_chapter/coreutils_26.html#SEC159”>set group id” flag on the db directory. This flag makes sure that all files created in the “db” directory are owned by the same group as the “db” directory.

Basic Concept

Note that the setgid bit doesn’t suffice to ensure correct permissions: we can only enforce the group ownership, but not enforce the permissions the new file will get—the umask of the user influences those. A typical umask value is “022”; if you create a file with this umask it will get the following permissions:

$ umask
022
$ touch file
$ ls -l file
-rw-r--r--  1 darix users 0 2005-04-09 00:41 file
         

As we see the group can only read the file. This is fine for FSFS, but for BDB we would need write permissions.

$ umask 007
$ touch file
$ ls -l file
-rw-rw----  1 darix users 0 2005-04-09 00:44 file
         

Setting the umask to “007” gives the group write permissions and removes all permissions for others. a more liberal umask would be “002” which would allow the others at least read permissions. A paranoid user would set “077” which takes permissions away from all other users (note that this umask would wreak havoc with either flavor of subversion repository). The complete magic is explained in man umask.

FSFS takes care of this itself. If FSFS backend creates a new file it uses the permissions of the previous revision. This permission inheritance makes sure that the group can always write the files.

(3) For the checklist we keep “The umask can be important!”

Apache2/Svnserve Only

These are for sure the easiest cases. You should run them as a non-root user!
I assume you run Apache2 as “apache:apache” and svnserve as “svn:svn”:

$ chown -R apache: /srv/svn/repositories/project01/db
$ find . -ls
31320846    0 drwxr-xr-x   7 root     root           90 Apr  9 00:14 .
2272808    0 drwxr-xr-x   2 root     root            6 Apr  9 00:14 ./dav
7178361    0 drwxr-xr-x   2 root     root           39 Apr  9 00:14 ./locks
7178364    4 -rw-r--r--   1 root     root          460 Apr  9 00:14 ./locks/db.lock
7178366    4 -rw-r--r--   1 root     root          295 Apr  9 00:14 ./locks/db-logs.lock
8854953    4 drwxr-xr-x   2 root     root         4096 Apr  9 00:14 ./hooks
11228932    4 -rw-r--r--   1 root     root         2137 Apr  9 00:14 ./hooks/start-commit.tmpl
11228937    4 -rw-r--r--   1 root     root         2944 Apr  9 00:14 ./hooks/pre-commit.tmpl
11228939    4 -rw-r--r--   1 root     root         2764 Apr  9 00:14 ./hooks/pre-revprop-change.tmpl
11228940    4 -rw-r--r--   1 root     root         2016 Apr  9 00:14 ./hooks/pre-lock.tmpl
11228943    4 -rw-r--r--   1 root     root         1998 Apr  9 00:14 ./hooks/pre-unlock.tmpl
11228944    4 -rw-r--r--   1 root     root         2015 Apr  9 00:14 ./hooks/post-commit.tmpl
11228946    4 -rw-r--r--   1 root     root         1637 Apr  9 00:14 ./hooks/post-lock.tmpl
11231620    4 -rw-r--r--   1 root     root         1564 Apr  9 00:14 ./hooks/post-unlock.tmpl
11237184    4 -rw-r--r--   1 root     root         2255 Apr  9 00:14 ./hooks/post-revprop-change.tmpl
13642827    0 drwxr-xr-x   2 root     root           39 Apr  9 00:14 ./conf
13642830    4 -rw-r--r--   1 root     root         1078 Apr  9 00:14 ./conf/svnserve.conf
13642831    4 -rw-r--r--   1 root     root          311 Apr  9 00:14 ./conf/passwd
31320848    4 -rw-r--r--   1 root     root          379 Apr  9 00:14 ./README.txt
31320850    4 -r--r--r--   1 root     root            2 Apr  9 00:14 ./format
18259246    0 drwxr-sr-x   5 apache   apache        120 Apr  9 00:14 ./db
18272163    4 -rw-r--r--   1 apache   apache          5 Apr  9 00:14 ./db/fs-type
23169557    0 drwxr-sr-x   2 apache   apache         14 Apr  9 00:14 ./db/revs
23169558    4 -rw-r--r--   1 apache   apache        115 Apr  9 00:14 ./db/revs/0
27193648    0 drwxr-sr-x   2 apache   apache         14 Apr  9 00:14 ./db/revprops
27193649    4 -rw-r--r--   1 apache   apache         50 Apr  9 00:14 ./db/revprops/0
31320852    0 drwxr-sr-x   2 apache   apache          6 Apr  9 00:14 ./db/transactions
18272185    4 -rw-r--r--   1 apache   apache          6 Apr  9 00:14 ./db/current
18272212    0 -rw-r--r--   1 apache   apache          0 Apr  9 00:14 ./db/write-lock
18273153    4 -rw-r--r--   1 apache   apache         37 Apr  9 00:14 ./db/uuid
18273164    4 -r--r--r--   1 apache   apache          2 Apr  9 00:14 ./db/format
         

For svnserve, of course, you’d instead do chown -R svn: /srv/svn/repositories/project01/db.

You might argue that one should further restrict the permissions, so that others can neither read nor write the “db” directory. You’d be right. My paranoid setup looks like this:

$ chmod -R o= /srv/svn/repositories/project01
$ chgrp -R apache /srv/svn/repositories/project01
$ find . -ls
31320846    0 drwxr-x---   7 root     apache            90 Apr  9 00:14 .
2272808    0 drwxr-x---   2 root     apache             6 Apr  9 00:14 ./dav
7178361    0 drwxr-x---   2 root     apache            39 Apr  9 00:14 ./locks
7178364    4 -rw-r-----   1 root     apache           460 Apr  9 00:14 ./locks/db.lock
7178366    4 -rw-r-----   1 root     apache           295 Apr  9 00:14 ./locks/db-logs.lock
8854953    4 drwxr-x---   2 root     apache          4096 Apr  9 00:14 ./hooks
11228932    4 -rw-r-----   1 root     apache          2137 Apr  9 00:14 ./hooks/start-commit.tmpl
11228937    4 -rw-r-----   1 root     apache          2944 Apr  9 00:14 ./hooks/pre-commit.tmpl
11228939    4 -rw-r-----   1 root     apache          2764 Apr  9 00:14 ./hooks/pre-revprop-change.tmpl
11228940    4 -rw-r-----   1 root     apache          2016 Apr  9 00:14 ./hooks/pre-lock.tmpl
11228943    4 -rw-r-----   1 root     apache          1998 Apr  9 00:14 ./hooks/pre-unlock.tmpl
11228944    4 -rw-r-----   1 root     apache          2015 Apr  9 00:14 ./hooks/post-commit.tmpl
11228946    4 -rw-r-----   1 root     apache          1637 Apr  9 00:14 ./hooks/post-lock.tmpl
11231620    4 -rw-r-----   1 root     apache          1564 Apr  9 00:14 ./hooks/post-unlock.tmpl
11237184    4 -rw-r-----   1 root     apache          2255 Apr  9 00:14 ./hooks/post-revprop-change.tmpl
13642827    0 drwxr-x---   2 root     apache            39 Apr  9 00:14 ./conf
13642830    4 -rw-r-----   1 root     apache          1078 Apr  9 00:14 ./conf/svnserve.conf
13642831    4 -rw-r-----   1 root     apache           311 Apr  9 00:14 ./conf/passwd
31320848    4 -rw-r-----   1 root     apache           379 Apr  9 00:14 ./README.txt
31320850    4 -r--r-----   1 root     apache             2 Apr  9 00:14 ./format
18259246    0 drwxr-s---   5 apache   apache           120 Apr  9 00:14 ./db
18272163    4 -rw-r-----   1 apache   apache             5 Apr  9 00:14 ./db/fs-type
23169557    0 drwxr-s---   2 apache   apache            14 Apr  9 00:14 ./db/revs
23169558    4 -rw-r-----   1 apache   apache           115 Apr  9 00:14 ./db/revs/0
27193648    0 drwxr-s---   2 apache   apache            14 Apr  9 00:14 ./db/revprops
27193649    4 -rw-r-----   1 apache   apache            50 Apr  9 00:14 ./db/revprops/0
31320852    0 drwxr-s---   2 apache   apache             6 Apr  9 00:14 ./db/transactions
18272185    4 -rw-r-----   1 apache   apache             6 Apr  9 00:14 ./db/current
18272212    0 -rw-r-----   1 apache   apache             0 Apr  9 00:14 ./db/write-lock
18273153    4 -rw-r-----   1 apache   apache            37 Apr  9 00:14 ./db/uuid
18273164    4 -r--r-----   1 apache   apache             2 Apr  9 00:14 ./db/format
         

This setup will allow apache to read all files in the repository directory but only write in the “db” directory. Other users than root or apache don’t have any access.

Allowing Svnserve and Apache2 to access the same repository

Now we get to the interesting problems: two daemons accessing the same repository. We have two possibile solutions:

  1. running both daemons as apache
  2. both daemons in a common group

running both daemons as apache

This is the easier of the two solutions. The permissions from above stay the same as above. We just need to start the svnserve as apache. On distributions with startproc it will look like:

$ startproc -u apache -g apache -e /usr/bin/svnserve -d -r /srv/svn/repositories

On systems with start-stop-daemon:

$ start-stop-daemon --start --chuid apache --group apache --exec /usr/bin/svnserve -- -d -r /srv/svn/repositories

For all other systems please refer to the manual of your os/distributon.

both daemons in a common group

For this approach we use point #2 from our checklist. First we need to decide which group we use. We can add the svn user to the group apache, or conversely we can add the apache user to the svn group. The latter has fewer possible security implications, so i will use this for my example:

# suse
$ /usr/sbin/groupmod --add-user apache svn
# freebsd
$ pw groupmod -n svn -m apache
        

We can set our new permissions now. Based on the paranoid example from above

$ chgrp -R svn /srv/svn/repositories/project01

Now we have to make sure both daemons run with a proper umask. The umask is usually inherited from the startup environment. Usually this can cause trouble, as root usually has “022” as its umask. If you want to make sure that the umask of the daemon is always correct, add “umask 007” in your startscript. Et voila—svnserve and apache can both access the repos just fine. :)

At this moment we don’t care anymore if your apache is running mod_dav_svn or viewcvs or any other repository viewer. They all work just fine and don’t wedge the repository. (At least not for permissions reasons.)

Adding file:// and svn+ssh:// to the mix

svn+ssh:// and file:// both access the repository the same way. In general we have the same problems as in the last block. We need to make sure that all accessing users have a common group and a proper umask. The easiest solution for file and svn+ssh are wrapper scripts.

file:// – access

Our wrapper script for file:// access:

#!/bin/sh
# wrapper script for svn
umask 007
exec /usr/bin/svn "$@" 
         

You have to put this script in the path before your actual svn binary (”/usr/bin/svn”), to ensure that your users run your script. First we need to check the $PATH variable.

$ env | grep -e "^PATH=" 
PATH=/home/darix/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/games:/opt/gnome/bin:/opt/kde3/bin
         

On my box, ”/usr/local/bin” is in the path before ”/usr/bin/”. I save the wrapper script as ”/usr/local/bin/svn”. Any invocations of “svn” will then invoke ”/usr/local/bin/svn”. You should look at gui clients/scripts which invoke svn: sometimes they use a full path, hence bypassing our script. You’d need to adjust them, too. (This doesn’t refer to scripts like websvn—it is run in the webserver context and should have the proper umask.)

svn+ssh://

The solution for svn+ssh:// follows the same path. We have a small wrapper script again:
#!/bin/sh
# wrapper script for svnserve
umask 007
exec /usr/bin/svnserve "$@" 
         

To find the value of $PATH from a ssh session we do:

$ ssh localhost env | grep -e "^PATH=" 
PATH=/home/darix/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/games:/opt/gnome/bin:/opt/kde3/bin
         

Again, ”/usr/local/bin” seems to be a nice place for the wrapper script. We save the script as ”/usr/local/bin/svnserve”.

A note for BSD users: ”/usr/local/” is the default prefix for svn on a BSD, but put your wrapper scripts into ”/usr/bin” instead, because (at least, on my BSD box) ”/usr/bin” is in the path before ”/usr/local/bin”.

Conclusion

As shown in this short howto it is quite easy to have a proper setup with BDB without any permissions problems. So next time before you blame BDB … check your setup. From my day- by-day experience on #svn I can say 99% of the BDB wedges are permissions problems.

Sources

CVS modules in svn.

Tags:

many old cvs user like the cvs module feature.

you can emulate that feature with svn:externals

let us assume the following layout:

$ svn ls svn://localhost/repos/
trunk/
tags/
branches/

First let us create the base directory for all modules:

$ svn mkdir svn://localhost/repos/modules

We create the base dir for the documentation module and check it out.

$ svn mkdir svn://localhost/repos/modules/documentation
$ svn co svn://localhost/repos/modules/documentation project-documentation

We add the externals to the documentation directory.

$ svn propedit svn:externals project-documentation

In your open editor add the following lines:

help svn://localhost/repos/trunk/help
src-help svn://localhost/repos/trunk/src/help
docs svn://localhost/repos/trunk/docs

save the file and svn will commit the properties change

now you can update with "svn update project-documentation" and get only the documentation of your project.

Commit the property change to make it permanent.

Refactoring the Authorization API in Apache 2.1

Tags:

A point people dislike is the integration of mod_authz_svn into the Apache API.
At the moment you are restricted to the file format of mod_authz_svn. This makes it hard to integrate Subversion with existing authorization setups.

The problem

Currently mod_authz_svn is required for path/group based authorisation.
Other authorization modules are circumvent by the svn special paths "PROPFIND /svn/path/!svn/vcc/default HTTP/1.1". Furthermore mod_authz_svn takes care of the various WebDAV methods and it combinations.
You could argue that apache can handle this methods too, but mod_authz_svn is more convienent on configuring this.

Compare

[groups]
somegroup = user1, user2

[/]
* = r
somegroup = rw

with

&lt;LimitExcept GET PROPFIND OPTIONS REPORT&gt;
    Require valid-user
    Require group somegroup
&lt;/LimitExcept&gt;

Solution

Justin Erenkrantz refactored the Authentication API for Apache 2.1 to allow different backends for HTTP Basic Auth and HTTP Digest Auth.
I used this new API for my modauthnsvn.

I discussed a better integration with Justin. We came to the conclusion that the best solution would be to refactor the Authorization API along the way of the Authentication API.

The solution would allow us:

  1. use different backends for the storage sql/ldap/db
  2. reuse the backends for different authorization modules.

What does this mean for mod_authz_svn?

  1. the group managment goes into the available mod_authz_groups
  2. path managment can be done by other modules too.
  3. mod_authz_svn mainly remains for taking care of the WebDAV methods handling. (compare req_check_access() in mod_authz_svn.c)

Path managment

As already mentioned in the problem description mod_authz_svn is a convinient way to describe access control to paths. IMHO this is much more readable than the Apache ´<Limit* /> directives. It would be a nice idea to still use a similar way, but allow storage in other backends. Furthermore i would like to see mod_authz_svn working outside of svn locations. But this is a minor bugfix. :)

Article TODO:

  • mention that authz_svn needs to resolve the paths for underlying authz modules
  • doing this patch would allow authz_Svn to enforce ssl inside of parent paths. might be nice for hosters.

svnmirror.sh

Tags: ,

svnmirror.sh.txt v0.0.6

this script syncs changes from a developer repository to a READONLY public repository.
it supports pushing or pulling the changes via ssh and svn tools. its intended to be run manually or from cron.

things you have to take care of:
  1. you need write access to the directory structure on both boxes for more see the warning at:
    http://svnbook.red-bean.com/svnbook/ch06s03.html#svn-ch-6-sidebar-1.
    this warning only applies to bdb based repositories. if your repos is fsfs you only need read access on the source side.
  2. for running it from cron i suggest the use of the ssh agent e.g via keychain in general read “man ssh-agent” and “man ssh-keygen”.
  3. don’t run it from post commit scripts it can lead to broken public repositories!
  4. you do not have to be afraid of the public repos. if the pipe is broken nothing will be committed to it.
    21:40:47 <@sussman> tberman: welcome to atomic commits.
  5. for local syncing use “svnadmin hotcopy” see: “svnadmin help hotcopy
License:

the same as svn itself. for latest version check: http://svn.collab.net/repos/svn/trunk/subversion/LICENSE

thanks to the subversion team for their great work

Links:

if you do not like my solution check:

svnpush
http://svn.collab.net/repos/svn/trunk/contrib/client-side/svn-push/svn-push.c
svn replicate
http://open.datacore.ch/wikipage/SVNreplicate
SVN::Mirror and SVN::Web
http://svn.elixus.org/repos/member/clkao/
http://svn.elixus.org/svnweb/repos/browse/member/clkao/
svk
a nice user interface for SVN::Mirror is svk. it can even mirror changes from arch now. See: http://svk.elixus.org/