Introduction

SVNSyncRouter is a program package, consisting of a server program and a client program. The server program handles the synchronization of paths in a slave repository with paths in a master repository. The client program is used to control the server. We will subsequently call both programs SVNSyncRouter as it will be clear from context if the server or client program is meant.

Synchronization by SVNSyncRouter is generally two-way, that is, changes in the master are reflected in the slave and vice-versa. Unlike svnsync, SVNSyncRouter does not attempt to synchronize the repositories in such a way that they are perfect replicas of each other. It might combine several revisions in one repository and commit them as a single revision to the other repository. Furthermore, user names are not preserved. Instead, a user must be created on the slave and master that will be used by SVNSyncRouter to access the repositories.

The server program is able to operate in either automatic or manual mode. In automatic mode, it continuously polls the repositories for new revisions and then transmits them to the other repository. In manual mode, a user must tell the server to transmit new revisions to the other repository.

The client program is invoked via the command line and takes several commands that are then passed to the server component via RMI. It uses a SHA-256-based challenge response mechanism to authenticate the client.

A file containing exclusion patterns (regular expressions) can be created to specify which files, directories and properties should be excluded from synchronization.

Getting started

The following explanations assume that you have checked out a copy of https://svn.vitalab.tuwien.ac.at/projects/SVNSyncRouter/sources/at.ac.tuwien.infosys.svnsync/svnsync. The content of the repository is organized as a maven project. So, to build and package the software, run maven package. However, the test cases only run on Unix/Linux systems, as they rely on various shell scripts. Therefore, the tests should be skipped on other systems via the -Dmaven.test.skip=true flag.

The result of the packaging is a jar file that contains both the server and client program. The main class is the main class of the client, so to run the server program, the main class of the server program (at.ac.tuwien.infosys.svnsync.server.Sync) must be explicitely specified.

A server instance of SVNSyncRouter can be configured by a properties file. An example configuration file named example.properties can be found in the directory <wc_root>/src/main/resources/config. The file contains detailed explanations of all configuration parameters.

Furthermore the server program uses an exclusions file that allows the specification of patterns to exclude certain paths and properties from synchronization. An example exclusions file named example.exclusions can be found in the directory <wc_root>/src/main/resources/config.

The client component of SVNSyncRouter uses no configuration files. It obtains all necessary parameters from command line arguments.

Invoking SVNSyncRouter

Since SVNSyncRouter is packaged in a jar file that has a manifest entry for the main class of the client program, the client program can be started via java -jar <jar_file> [<arguments> ...]. To start the server, the main class needs to be explicitely stated and the property java.rmi.server.codebase needs to be specified. So the invocation might look like the following: java -cp <jar_file> -Djava.rmi.server.codebase <jar_file> at.ac.tuwien.infosys.svnsync.server.Sync [<arguments> ...]. In the following text, the names <server> and <client> will be used to represent the invocation of the client and server program. The arguments are explicitely stated though.

Setting up the server

Setting up the server is largely about adapting the configuration file. Be sure to carefully read the explanations in example.properties to avoid misconfiguration of the server. Changes in the configuration file will not be visible to SVNSyncRouter during its execution. For the changes to take effect, you need to restart it.

Accordingly, changes in the exclusions file will not be visible to SVNSyncRouter until it is restarted. There is an important rule how the exclusions file is allowed to be modified during successive invocations. The modified exclusions file is only allowed to be more restrictive than the original. That is, it excludes all paths and properties that the original file excluded and excludes additional paths and properties.

Because SVNSyncRouter binds to an RMI registry, a registry must be started before the server program can be started:

$ rmiregistry

Once the configuration file is set up properly and a registry runs, the server program can be started:

$ <server> <path_to_prop_file> [debug]

Do not forget to set the property java.rmi.server.codebase, as otherwise the RMI registry will not be able to locate the class files for SVNSyncRouter. If SVNSyncRouter encounters a problem during the start up phase, it prints an error messages to stderr and exits. The argument debug can be appended after the path to the property file. This way SVNSyncRouter will not only print error messages in case of error, but will also print the exceptions that were thrown.

During the course of execution, the server program saves its state on disk after various significant events. This allows it to pick up where it left off after it was terminated (either deliberatly or erronously) and then started again. The file that contains the state can be set in the configuration file via the parameter state_file.

When the server is running, the first thing you need to do is transfer the data in the master to the slave, so the content of the slave is equal to the content of the master (except excluded paths and properties). The path in the slave that will receive the data must be empty. The client program is used to instruct the server to make the initial data transfer:

$ <server> initialtransfer <host> <name>

<host> is the hostname or IP address of the server that is running SVNSyncRouter and <name> is the name svnsrs is bound to in the RMI registry (as configured via the bind_to_name configuration parameter). When the client program promps you for a passphrase, you must enter the passphrase that was configured in the properties file of the server component.

Now the setup of the server is complete and committers can begin to commit to the master and slave repositories. The server program is now in manual mode. That means that changes in one repository will not be automatically relayed to the other repository. Read on to find out how you can use the client program to manually synchronize the master and slave repositories and how to switch to automatic mode.

Using the client program

The general syntax for the invocation of the client program is

<client> <command> [<host>] [<name>] [<argument>].

<host> ::= <hostname> | <IP address>

<name> is the name the server is bound to in the RMI registry (as set in the configuration file). We will now describe which commands are available and what they do.

Get a list of all commands available:

$ <client> help

Get a list of all names bound in a registry on a given host:

$ <client> list <host>

Get the status of an instance of SVNSyncRouter:

$ <client> status <host> <name>

The status command prints the mode that the server program is in (automatic or manual) and the state it is in (no_conflict or conflict; see the sections conflict situations and resolving conflict below)). It also prints if the initial transfer has already been performed.

Switch to automatic or manual mode:

$ <client> setmode <host> <name> (automatic | manual)

Send the revision ranges that have been created since the last transfer to the other repository (only in manual mode):

$ <client> sendchangesto <host> <name> (master | slave | remotebranch)

Tell svnsrs that a conflict has been resolved (see the next two sections for important notes):

$ <client> resolve <host> <name>

Send a test email to the configured email address to test the configuration:

$ <client> emailtest <host> <name>

Conflict situations

Consider the following situation: A user changes the file file_a in his local wc of the master and commits the change. At the same time, a user changes the same file in his wc of the slave and also commits the change. The next time the server program tries to synchronize the repositories (either automatically or manually via the sendchangesto command), it cannot apply the delta and aborts the commit. The server then changes to the state conflict and to the mode manual. Furthermore, it creates a remote branch on the master. This remote branch is a copy of the master at the last revision where the master and the slave were equal. Because of this property, new slave revisions can be applied to the remote branch.

By using the command <client> sendchangesto <host> <name> remotebranch, one can tell the server to transmit the new slave revisions to the remote branch.

Resolving conflicts

Since the master and the slave cannot be synchronized in a conflict state, you want to resolve the conflict as soon as possible. Furthermore, its easier to resolve a conflict when the master and slave do not differ much. You need to follow the following steps precisely for the conflict resolution to succeed. Most importantly, you must not call <server> sendchangesto <host> <name> remotebranch after you have completed step 3. The reason for this is that the data that you have merged into the wc in step 3 must be equal to the data in the remote branch when you call resolve in step 5. You should call resolve only as part of the five steps below and under no other circumstances.

  1. Bring the remote branch up-to-date:
    $ <client> sendchangesto <host> <name> remotebranch
  2. Checkout a wc of the master trunk (or update your existing one):
    $ svn co <URL_of_master_trunk>
  3. Merge the data in the remote branch into your wc of the master trunk. Assuming that your current working directory is the root of the wc, you would use:
    $ svn merge <URL_of_remote_branch>
  4. Commit the merged data:
    $ svn commit -m 'merged master trunk and remote branch'
  5. Tell SVNSyncRouter that you resolved the conflict:
    $ <server> resolve <host> <name>

    This is the point where the real work for SVNSyncRouter begins. It compares the remote branch to the master trunk and constructs deltas and combines them into a commit transaction that will be used to make the data in the slave equal to the master trunk. Should a new revision be committed to the slave after step 1, step 5 will not succeed. SVNSyncRouter will indicate this via an error message. You must then repeat the five steps. Commits to the master by other users do not cause problems at any step. If you want the server program to continue in automatic mode after the conflict has been resolved, you need to use the setmode command. It does not remember in which mode it where when the conflict occured.

Synchronizing more than one slave with a master

More than one slave can be synchronized with a master. Every instance of the server program manages the synchronization of one master with one slave. However, several instances can be configured in such a way that they use the same master repository URL but different slave repository URLs. Important in such a setup is that the remote branch URLs of all instances that use the same master repository need to be different.