svnmailer - documentation

Perlig > Projects > svnmailer > Documentation (1.1-dev)


Table Of Contents

  1. General Comments
  2. Installation
  3. Config File Design
  4. Global Configuration Options
  5. [maps] Configuration Section
  6. [general] Configuration Section
  7. Group Configuration Sections
  8. Configuring the Repository
  9. The svnmailer Command Line

General Comments

The svnmailer is an extensible subversion commit notification tool. Its purpose in the first place is to create human readable commit mails and correctly encoded other notifications. In order to accomplish this, the content may be recoded to fit the requirements of the supported standards like MIME or XML 1.0. Note that this does not change the meaning of the actual content. It's more like encoding a < character as &lt; in an HTML or XML document.

That means that you always get valid and readable mails, but you SHOULD NOT expect to be able to simply copy the diffs from the generated mails and apply them with the patch program. Use svn diff or ViewCVS diffs for such tasks. The svnmailer is able to generate the proper ViewCVS urls and place them quite handy near the diffs in the notification mails.

Nevertheless, there are always people doing weird things, so the svnmailer provides the possibility to generate multipart mails, which contain the opaque diff data. Note however, that the using the diffs directly from the multipart tracking mails has a number of disadvantages, including the following:

  1. The mails may be badly modified by gateways, because they are not protected by a proper transfer encoding
  2. The mails may not contain the whole change. There are options to omit diffs for special actions (often diffs for deletions are turned off). Further diffs of binary files are always omitted.
  3. Keyword and newline expansion in subversion happens client side, so the mail diff may be another one than the stuff svn diff generates.
  4. Last but not least email is an unreliable medium. The typical case to lose tracking mails is that the mailing list rejects mails that are greater than a certain limit. So how would you apply a diff, which you don't even have received? Sure, one could truncate the mails properly, but then issue 2 applies...


Before installing the svnmailer package make sure, that you meet the following requirements:

The svnmailer is a pure python package, which is packed using the standard distutils. So the installation on your system is fairly simple. First download the package and make sure you've checked the integrity of the downloaded file. There are a detached GPG signature (*.asc) and signed hash files (*.md5 and *.sha1), which can be used for this task.

After unpacking the archive file change into the svnmailer-1.1.0-dev-r1373 directory and follow the typical python procedure:

$ bzip2 -cd svnmailer-1.1.0-dev-r1373.tar.bz2 | tar -xf -
$ cd svnmailer-1.1.0-dev-r1373
$ python install

Now there should be two things installed, the svn-mailer command line script and svnmailer package itself. The package is copied to the "proper" location, where python finds it by default. The location of the script depends on the OS and the python installation. For example, on linux it typically installs into /usr/bin or /usr/local/bin. For customizations please refer to the related python documentation.

The next step is to create a configuration file. After you've done that, you can configure your repository to let the svnmailer do its work.

Config File Design

In short: the configuration file controls, who gets the notifications for which path in which repository. Further it defines the basic script parameters like how to send the notification mail, which diff program to use (if any) and so on. It is supposed to be compatible to the config of the original script.

Syntactical Elements

The file is a plain text file in an INI like format as defined by the standard python ConfigParser module. Basically it consists of several sections, that are started with a line containing:

[section name]

and finished at the next [section] or the end of the file. Values are defined that way:

name = value
# or
name: value

As you might have guessed, comments are preceded by the hash character (#). Lines starting with a semicolon (;) are also recognized as comment lines. Empty lines (or lines containing only whitespaces) are ignored. Note that section headings, comments and value definitions have to start at the first column, because the configuration parser treats (non-empty) lines starting with spaces or tabs as continuations of the previous line (like in mail headers).

There is a subtle difference compared to the original script. The svnmailer doesn't utilize the ConfigParser module, which cuts values after the first occurence of a semicolon which is preceeded by a whitespace character (that way this sequence starts a comment "after" the value). As this is not desired (since the sequence is not escapable) the svnmailer uses an own module, which doesn't modify the values. In fact, this special behaviour is not even documented, so removing it shouldn't hurt much.


The sections in your config file are processed by the svnmailer as follows: The [general] section contains the basic script parameters. The [defaults] section contains default values for the group sections. All values that are not defined in a selected notification group are taken from the defaults. [defaults] is optional. Further you can have a [maps] section, which specifies value mapping tables (which may define further sections, too). All sections not recognized otherwise define notification groups. The names of these sections don't care (except for debugging purposes). They just have to be unique within the config and may not be named defaults, general or maps, of course. If there is no separate group configuration, the defaults will be applied. So a minimal config is:


If you call the svnmailer with this config, it will generate diffs for every possible action at any path for any supplied repository and write a notification message to stdout.

Furthermore, there are options, which don't fit into any section at all, namely config_charset and include_config. They are defined in the global context, i.e. before the first section in the main config file. Before version 1.1 of the svnmailer both options were defined in the [general] section, so there is a compatibility behaviour built in: If there are no global options defined, they are looked up in the [general] section. But you cannot place, say, config_charset into the global context and include_config into [general].

Group Selection

When the svnmailer is called, it reads the config file and selects all groups, which should be notified of that particular event (commit or revprop change). Which groups are selected, is determined by the for_repos, for_paths, exclude_paths and ignore_if_other_matches options. Since the svnmailer tries to minimize the number of notification mails, each of these selected groups gets one mail at maximum per event (except they are configured for huge mail splitting). Furthermore, if the notifications generated for different selected groups are detected to be equal, these groups are merged and just one mail is sent to all of those groups. Currently this detection compares the list of modified paths (that were matched by each group) and several configuration options, which are in particular: show_nonmatching_paths, commit_subject_template, propchange_subject_template, lock_subject_template, unlock_subject_template, commit_subject_prefix, propchange_subject_prefix, lock_subject_prefix, unlock_subject_prefix, max_subject_length, reply_to_addr, diff_command, generate_diffs, browser_base_url, revision_url, diff_add_url, diff_copy_url, diff_delete_url, diff_modify_url, viewcvs_base_url, extract_x509_author, long_mail_action, long_news_action, mail_transfer_encoding, news_transfer_encoding, mail_type, apply_charset_property, show_applied_charset.

Here's another subtle difference to the original script. If there are group configurations defined, the svnmailer will never consider the [defaults] section as an additional group to be notified.

Global Configuration Options

The possible global configuration options
Option Name Type Description
config_charset string The character encoding of the config file
include_config List A list of config files to include

These options don't fit into any particular section type.


The config_charset option defines, how the svnmailer should interpret the (bytes read as) option values in your config files. It defaults to us-ascii, which means, normally you can leave it just out. It is very useful if you want to define, say, French subject prefixes or the like. Besides the exceptions described below, the svnmailer will die with an UnicodeError if there are characters in the config file which are not valid for the given config_charset.

Some options, namely of type "quoted literal" (like smtp_user) are not charset decoded, but taken literally (though possibly unquoted). Furthermore, the first item of command line option values (the command itself) may be interpreted literally, too. This depends on the OS and the --path-encoding option. See there for details.

There's a limitation: config_charset should be an US-ASCII based encoding (for example, UTF-16 is a bad choice).

# Example
# =======
config_charset = iso-8859-1


The include_config option specifies further config files which are appended to the current one. The config files can be defined using either relative or absolute paths in the filesystem. Relative paths are taken relative to the config file which includes them. The option value is parsed like a command line -- it is split on whitespaces, except when they are quoted. Have a look at the description of diff_command for a detailed explanation of the rules.

One typical use of include_config is to exclude mappings from the main config for maintenance and/or security reasons.

# Example
# =======
include_config = passwords.txt

smtp_user = smtpuser
smtp_pass = smtppass

smtp_user = [smtp]
smtp_pass = [smtp]

And the following could be the passwords.txt file:

# Example
# =======
smtpuser = "someuser"
smtppass = "secret password"

[maps] Configuration Section

The [maps] section allows you to specify mapping tables for values that you can't create directly by substitutions or just want to abbreviate via a nick name. For example, a typical configuration of from_addr is:

from_addr = %(author)

This is fine as long all authors really do have a mail address at, which local part matches the authentication user. However, a more complex mapping from author to mail address is not possible with this method. In this case, maps can provide a solution. Different map types are planned; for now, the svnmailer only supports plain config maps, which are described in the next section.

Note that not all options are mappable. The exceptions are all options that specify boolean or integer options in addition to the following list: generate_diffs, mail_transfer_encoding, news_transfer_encoding, config_charset, include_config.

Plain Config Maps

All you need is a translation table, which maps the authenticated author to his real address. Something like this:

[author table]
john =
foo =

Further you need to associate the [author table] with the revision author and the from_addr option:

from_addr = [author table]

from_addr = %(author)s

In the above config the [maps] section defines, that the resulting values of from_addr should be mapped using the [author table] section. Now the svnmailer takes the specified from_addr (%(author)s), substitutes the author with, say, john and maps john to If it doesn't find the value (john) in the mapping table, it is passed through unchanged.

On list options like to_addr, every item is mapped separately. For example:

from_addr = [author table]
to_addr = [author table]

from_addr = %(author)s
to_addr = %(author)s

This excerpt sends all commits to the author itself and an archive account.

[general] Configuration Section

The possible configuration options in [general]
Option Name Type Description
sendmail_command command line The sendmail compatible command line template
smtp_host string The SMTP host[:port] to use
smtp_user quoted literal The user used for SMTP authentication
smtp_pass quoted literal The password used for SMTP authentication
nntp_host string The NNTP host[:port] to use
nntp_user quoted literal The user used for NNTP authentication
nntp_pass quoted literal The password used for NNTP authentication
debug_all_mails_to mail addresses Mails should go only to these fixed addresses
cia_rpc_server string CIA XML-RPC tracking server
tempdir string The directory to use for temporary files
config_charset (deprecated) string (Use the global config_charset option)
include_config (deprecated) List (Use the global include_config option)
diff_command (deprecated) command line (Use the diff_command option defined in group sections)

Though the [general] section defines such basic parameters, it may be finally empty, because there are more or less useful defaults given for each option. In order to make sure, that you didn't forget it, the svnmailer requires at least the section heading to be present in the config.

The different options play together as follows:


The sendmail_command option defines the command line template of the program that should be called for sending a mail. The program should expect the mail body on stdin. The stdout channel of the program is closed by the svnmailer, but stderr is passed through the caller of the svnmailer.

In contrast to diff_command there are no substitutions made on the arguments. Instead the final command line is constructed as follows:

  1. The command template is split into its arguments following the rules described at diff_command.
  2. To specifiy the mail sender the arguments -f and the sender address is attached to the argument list
  3. The argument list is further extended with all recipient addresses.

This calling convention is compatible to, for example, sendmail or qmail's sendmail wrapper, hence the name sendmail_command.

As with diff_command no shell metacharacters are interpreted.

For compatibility reasons the sendmail_command option can also be written as mail_command.

# Example
# =======
sendmail_command = /usr/sbin/sendmail


The smtp_host option defines the SMTP server to connect in order to send a mail. This option is ignored if sendmail_command is defined and not empty. The option value is a hostname, optionally followed by a colon and a port. If the server supports authentication, you can supply the required credentials via the smtp_user and smtp_pass options.

# Example
# =======
smtp_host =
# or with port
smtp_host =

smtp_user and smtp_pass

These two options are only used if the smtp_host is used by the svnmailer. They define the credentials to be used in the SMTP session when attemting to send mail. If you supply smtp_user, you have to define smtp_pass as well. However, svnmailer just checks for the presence of smtp_user to know, if any credentials should be used.

The utilized smtp library supports the CRAM-MD5, PLAIN and LOGIN authentication mechanisms.

Because of the nature of those two options, the values are not considered to be charset encoded. They are sent literally to the SMTP server. In order to make sure, that spaces and other possibly weird characters are taken literally, you can enclose the actual string in double quotes ("). For double quotes and backslashes inside the quoted string apply the same rules as for command line arguments. Have a look at the description of diff_command for details. Of course, the surrounding quotes and backslash escape characters are stripped before submitting the string to the SMTP server.

# Example
# =======
smtp_host =
smtp_user = mysmtpuser
smtp_pass = mysmtppass


The nntp_host option defines the NNTP server to connect in order to post the notification as a news article. The option value is a hostname, optionally followed by a colon and a port. If the server requires authentication, you can supply the credentials via the nntp_user and nntp_pass options.

# Example
# =======
nntp_host =
# or with port
nntp_host =

nntp_user and nntp_pass

These two options are used if a news notification is submitted via an nntp_host. They define the credentials to be used in the NNTP session when attemting to post the article. If you supply nntp_user, you have to define nntp_pass as well.

Similar to smtp_user and smtp_pass the values are not considered to be charset encoded. They are sent literally to the NNTP server. See there for a further explanation of the argument format.

# Example
# =======
nntp_host =
nntp_user = mynntpuser
nntp_pass = mynntppass


This one is a real debugging option. It specifies a fixed list of mail addresses, where all notification mails should be sent to -- regardless of the to_addr templates of the selected groups. The addresses of the overridden recipients are sent along with the mail using the X-Supposed-Recipients header.

# Example
# =======
debug_all_mails_to =


The cia_rpc_server option defines a CIA XML-RPC handler, where your commits or some of them) should be tracked in real time. If this option is defined and not empty, the cia_xmlrpc notifier is considered to be run, but it will be activated only if the selected notification group defines a cia_project_name option. Note that it's possible for every group to run more than one notifier (e.g., mail and cia_xmlrpc) per commit.

The cia_rpc_server option takes a http or https URL. Note that the trailing slash is important. If there's no URL path given (i.e., just http://server), the svnmailer (or better, the utilized XML-RPC library) assumes it to be /RPC2.

The CIA notifier will not send any information if it runs in the post-revprop-change hook. By default there's no RPC server defined.

# Example
# =======
# (note the missing trailing slash!)
cia_rpc_server =


The tempdir option defines a directory to use for temporary files. By default or if the specified directory is empty this temp directory is chosen by python's tempfile module.

# Example
# =======
tempdir = /space/svnmailer-tmp


Defining this option inside [general] is deprecated. Use the global context instead.


Defining this option inside [general] is deprecated. Use the global context instead.


The diff_command option in [general] is deprecated. diff_command options defined in group sections are taken in preference of the one defined in [general].

Group Configuration Sections

The possible configuration options in group sections and [defaults]
Option Name Type Description
for_repos regex Matches the repository file path
for_paths regex Matches the virtual path inside a/the repository
exclude_paths regex Excludes paths that might be matched with for_paths
ignore_if_other_matches boolean Determines, whether the group should be ignored, if any other group matches this path
show_nonmatching_paths token Defines how to deal with changed paths that don't belong to the group
commit_subject_template template The mail subject template for normal commits
propchange_subject_template template The mail subject template for revprop change notifications
lock_subject_template template The mail subject template for lock notifications
unlock_subject_template template The mail subject template for unlock notifications
commit_subject_prefix string The mail subject prefix for normal commits
propchange_subject_prefix string The mail subject prefix for revision property notifications
lock_subject_prefix string The mail subject prefix for lock notifications
unlock_subject_prefix string The mail subject prefix for unlock notifications
max_subject_length number The maximum subject length
from_addr template The sender addresses
to_addr template The receiver addresses
reply_to_addr template The reply-to address
to_newsgroup template The newsgroups to post to
diff_command command line The diff command to use
generate_diffs token list The list of actions, which generate diffs
browser_base_url template Base URL and type of the repository browser installation
revision_url template URL format for revision overviews
diff_add_url template URL format for added files
diff_copy_url template URL format for copied (and modified) files
diff_delete_url template URL format for deleted files
diff_modify_url template URL format for modified files
viewcvs_base_url string (DEPRECATED) Base URL of the ViewCVS installation
long_mail_action tuple Action to take on overlong notification mails
long_news_action tuple Action to take on overlong notification news postings
mail_transfer_encoding token The content transfer encoding used for mails
news_transfer_encoding token The content transfer encoding used for news postings
mail_type token How to construct the mail (multipart/single)
apply_charset_property boolean Resolves the content charsets from svnmailer:content-charset properties.
show_applied_charset token Specifies whether the content charset (configured or default) of the should be written into the diff header.
custom_header tuple Name and Value format string for a custom header, which is included in the mail
extract_x509_author boolean Treats the author as x509 subject string and tries to extract the author's real name and email address
cia_project_name template The project name submitted to the CIA tracker
cia_project_module template The project module submitted to the CIA tracker
cia_project_branch template The project branch submitted to the CIA tracker
cia_project_submodule template The project submodule submitted to the CIA tracker
cia_project_path template The project path stripped from the absolute file paths before submitting to the CIA tracker

The options described here are all valid both in group configurations and in the [defaults] section. If a option in a normal group configuration is missing, its value is taken from [defaults]. If there is nothing defined, a hardcoded default is applied.


The for_repos option defines a regular expression, which is used to match against the file path of repository, for example /var/svn/my-repository. The file-path, which is matched against is guaranteed to not have a directory separator at the end (slash or backslash). Note that the regular expression always matches from the beginning of the path, so your regex typically will begin with .*. This is, because the svnmailer uses the re.match function - see the python docs for further information.

If the for_repos option is not defined or empty, the particular group matches for any repository (which is the default). Named matches of this group are stored for later substitutions.

In the following example the group "sample group" will be selected only if the script is called for the "public" repository (e.g. /var/svn/repositories/public):

# Example
# =======
[sample group]
for_repos = .*/public$


The for_paths option defines a regular expression, which is used to match against one of the modified paths stored in the repository, but only if the group was preselected by repository (see for_repos). If the path matched against is a directory, it is guaranteed to end with a slash, so that matching by directory paths results in more simple regular expressions. As with for_repos, the match always starts at the beginning of the path, but without a leading slash.

If the for_paths option is not defined or empty, the particular group matches for any path inside the repository (which is the default). Named matches of this group are stored for later substitutions.

In the following example the group "sample group" will be selected only if the script is called for the "public" repository (e.g. /var/svn/repositories/public) and everything under the /site/ directory (e.g. /site/images/foo.gif, but not for /site-tools/

# Example
# =======
[sample group]
for_repos = .*/public$
for_paths = site/


Since regular expressions usually match positive, it's from time to time helpful (and better readable) to exclude substrings with a separate match. The exclude_paths option exists for that purpose. It matches exactly like for_paths, but the group is selected only if the supplied regex does not match (and has been preselected by for_repos and for_paths).

If the exclude_paths option is not defined or empty, nothing will be excluded (which is the default). Of course, named groups of the match will not be stored for substitution, because the group is not selected, if there is a match of exclude_paths.

In the following example the group "sample group 1" will be selected only if the script is called for the "public" repository (e.g. /var/svn/repositories/public) and everything under the /site/ directory (e.g. /site/images/foo.gif), but not for stuff under /site/tools/. For every change under the site/tools/ directory the group "sample group 2" will be notified:

# Example
# =======
for_repos = .*/public$

[sample group 1]
for_paths = site/
exclude_paths = site/tools/

[sample group 2]
for_paths = site/tools/

Note that if the exclude_paths option was not given, every change under site/tools/ would generate a notification for both groups.


Consider a main project, which consists of several subprojects. Every subproject has its own notification group:

# Example
# =======
for_repos = .*/public$

[main project]
# consists of main1/ .. mainn/ and sub1/ ... subn/
# sub1 ... n get their own notification, the main project should
# be notified only for stuff other than sub?/
for_paths = project/
exclude_paths = project/(sub1|sub2|...|subn)/

[sub 1]
for_paths = project/sub1/
# :
[sub 10]
for_paths = project/sub10/

The exclude_paths option could be matched easier, if the sub projects really would be named subdigit. But usually this is not the case. As you see, maintaining the exclude_paths regex grows to a nightmare the more projects are added. The ignore_if_other_matches option is supposed to help out of this ugly situation. If set to a positive value (e.g. yes), the group will not be selected for the matched path if there are any other groups that match the same path / repository. The above config could be rewritten as:

# Example
# =======
for_repos = .*/public$

[main project]
for_paths = project/
ignore_if_other_matches = yes

[sub 1]
for_paths = project/sub1/
# :
[sub 10]
for_paths = project/sub10/

Note that there is a border case. If you use this feature for more than one group, it can happen, that finally the list of selected groups per path consists only of more than one ignorable groups. Theoretically these would unselect each other. Practically all those groups are selected, so that the notification is not lost.

The "boolean" values accepted by this option are yes, on, true and 1 for the "true" case and no, off, false, 0, none and the empty string for the "false" case. The default is false.

For compatibility reasons, convenience and better readability this option can also be written as suppress_if_match or fallback.


The show_nonmatching_paths option specifies how the svnmailer should deal with situations where the paths matched by for_paths are only a subset of all paths affected by the commit. For instance, consider the following changeset:


Further consider a notification group that matches for all paths beginning with foo/:

[some group]
for_paths = foo/

The show_nonmatching_paths option provides for three values, which solve the conflict differently:

The additional changes are included in the notification (but after the ones belonging to the group). CIA notices for this group also include all changed paths then. If you have different customers that may not see each other's projects, be careful with this solution.
It will be stated in the notification, that there are additional changes (after the path list), but neither the paths nor their diffs are included
The additional paths will be just ignored. In the notification there will be no sign of changes not belonging to the group.

If show_nonmatching_paths is unset or empty, it defaults to no. Note that this default differs from the less safe yes default used in the subversion 1.2 script.

For convenience reasons and better readability this option can also be written as nonmatching_paths, nongroup_paths and show_nongroup_paths.

# Example
# =======
[some group]
show_nonmatching_paths = yes

commit_subject_template, propchange_subject_template, lock_subject_template and unlock_subject_template

These options define the subject templates to be used for the particular notification type (commit, revprop change, lock, unlock). In addition to the normal substitution record the following substitutions are available:

Additional subject template substitutions
Name Value Description
prefix This is the subject prefix as configured.
part If mails are split, this contains the description of the current part ([x/y])
files* This contains the paths affected by the event. Despite the name files this also contains affected directories (consider them as special files).
dirs* Well, this only contains the directories affected by the event (in contrast to file).
files/dirs* The content of files/dirs is determined dynamically. It chooses the value of files by default. If the subject gets too long then, it takes dirs. The length parameter is max_subject_length or 255 if max_subject_length is unset.

* All items are space separated. Further if the path items have a common prefix, it is extracted and the paths shortened respectively. It looks about "in /prefix: foo bar/baz" then.

After the template was filled in, all whitespaces are normalized, that is, leading and trailing spaces are stripped and multiple adjacent whitespaces of any favor are compressed to one space. So you don't have to worry about strange-looking subjects, because one of the substitutions is empty (part is a good candidate).

The svnmailer defines the following default templates in case of unset or empty options:

The default subject templates
commit %(prefix)s r%(revision)s %(part)s - %(files/dirs)s
revprop change %(prefix)s r%(revision)s - %(property)s
lock %(prefix)s %(files/dirs)s
unlock %(prefix)s %(files/dirs)s

A typical use case of a customized subject template is a mailing list, where the svn authors are not allowed to post, but just one mail address, which represents the notification mailer itself. Or you don't want to expose the mail addresses of the committers. However, it would be still desirable to get the author of the commit in the mail client overview. Just put into the subject:

# Example
# =======
commit_subject_template = %(author)s: %(revision)s - %(files)s

commit_subject_prefix, propchange_subject_prefix, lock_subject_prefix and unlock_subject_prefix

These options define the subject prefix of the generated mails depending on the described event. If a string is supplied, it's provided as prefix substitution in the subject template. commit_subject_prefix defines the prefix for normal subversion commits (files, directories and versioned properties). propchange_subject_prefix defines the subject prefix for unversioned property change notifications. lock_subject_prefix defines the subject prefix for lock notifications (SVN 1.2 and later) and unlock_subject_prefix for unlock notifications (you knew that, huh?).

The default prefixes are empty.

# Example
# =======
commit_subject_prefix = svn commit:
propchange_subject_prefix = svn revpropchange:
lock_subject_prefix = svn lock:
unlock_subject_prefix = svn unlock:


The max_subject_length option specifies the maximum length of the generated mail or news subject line. If the generated subject is longer than the defined limit, it is cut and three dots are appended (hence the minimum subject length is 3). If the max_subject_length option is not specified, empty or defines 0, no limit is applied.

For compatibility reasons and convenience the max_subject_length option can also be written as truncate_subject or subject_length.

# Example
# =======
max_subject_length = 127

from_addr, to_addr and reply_to_addr

from_addr, to_addr and reply_to_addr define address templates for the mails to be sent. Both from_addr and to_addr accept space or tab separated lists of address templates, while reply_to_addr takes just one address. The semantics should be quite clear: from_addr defines the sender addresses (but usually just one), to_addr the recipient addresses and reply_to_addr the address, where answers to the commit mail should be sent to. If groups are merged during the selection process, there can be any number of senders, receivers and even reply-to addresses in the mail (which conforms to RFC 2822, if you care about such things). Duplicates in the address lists are filtered away. In the case of more than one final sender address, the svnmailer generates an additional Sender: header with the first item of the sender address list (which is more or less random, but they should be all valid, right?).

If there are no sender addresses given, it uses the string no_author (as the original script does). That may lead to an error while mail sending, so the best is to supply a valid from_addr in the [defaults] section.

If there are no recipients, the svnmailer simply doesn't send the mail (this is useful, if there are more notifier types like news or XML-RPC active). If this is not, what you want, you have to supply functioning to_addr options.

All those addresses may contain substitution patterns in the form %(name)s. The list of values to substitute is determined for each notification group dynamically using the for_repos and for_paths regular expressions. The substitution names author and group are always defined. If not overridden by one of the regular expressions, author contains the author of the change (or the string no_author if no author could be determined) and group the name of the notification group (the section heading without the braces).

All address templates described here are empty by default. For compatibility reasons the reply_to_addr option can also be written as reply_to.

# Example
# =======
for_repos = .*/public$
from_addr = %(author)

for_paths = projects/(?P<PROJECT>[^/]+)/
to_addr = commits@%(PROJECT)
reply_to_addr = dev@%(PROJECT)

[home repositories]
for_paths = home/(?P<OWNER>[^/]+)/
to_addr = %(OWNER)

[everything else]
for_paths =
fallback = yes
to_addr =


The to_newsgroup option specifies a space or tab separated list of newsgroups where the notification should be posted to. This parameter is a substitution template, so you can extract information from the commit information.

Note that you need to define the nntp_host option (in [general]) in order to submit news postings.

# Example
# =======
nntp_host =

to_newsgroup = org.example.commits


The diff_command option defines, that you want to use an external diff program (instead of python's difflib module), where to find and how to call it. The option value is a template for the command line to call. The program has to write the diff information to stdout. Stdout and stderr are caught by the svnmailer and dumped into the mail.

As said, the value describes a template. There is a fixed number of substitutions defined for the diff_command option, in particular: label_from, label_to, from and to. from and to define the actual files to process (typically some scrambled temporary file names). label_from and label_to define, how the files should be labeled by the diff program. These substitutions are written as %(name)s and replaced by the svnmailer script. If you want a literal percent character somewhere in the command line, you have to duplicate it (i.e., write %% instead).

The command line template is split on spaces or tabs to separate the different arguments. If you want to have an argument contain such space characters, you have to enclose it in double quotes ("). Further if you want such a quoted argument to contain a double quote character, you have to escape it with a backslash (i.e., write \" instead). To complete the escaping mechanism, you have to duplicate backslashes inside a quoted argument. The surrounding quotes and the backslash escape characters are stripped by the svnmailer to get the final argument string.

Note that on POSIX systems no shell is called to execute the program, so shell metacharacters are not interpreted. However, on Win32 the shell (cmd.exe & Co.) may be called, but the arguments are properly escaped then.

For compatibility and convenience the diff_command option can also be written as diff.

# Example for GNU diff
# ====================
# (note that the following is all on one line)
diff_command = /usr/bin/diff -u -L %(label_from)s -L %(label_to)s %(from)s %(to)s


The generate_diffs option defines which actions diffs are generated for. It takes a space or tab separated list of one or more of the following tokens: add, modify, copy, delete, propchange and none.

If the add token is given and a new file is added to the repository, the svnmailer generates a diff between an empty file and the newly added one. If the modify token is given and the content of an already existing file is changed, a diff between the old revision and the new revision of that file is generated. The copy token only worries about files, that are copied and modified during one commit. The delete token generates a diff between the previous revision of the file and an empty file, if a file was deleted.

If the propchange token is given, the svnmailer also takes care of changes in versioned properties. Whether it should actually generate diffs for the property change action depends on the other tokens of the generate_diffs list. The same rules as for files apply, except that the svnmailer never generates property diffs for deleted files. For example:

# Example
# =======
generate_diffs = add copy modify propchange

Now svnmailer generates diffs, if:

If a file or property is deleted, it's written as action information into the mail, but no content diff is generated. The default value for generate_diffs contains all possible actions. Mistyped tokens are ignored. If the resulting token list is empty, svnmailer falls back to the default. If you really don't want diffs to be generated, configure explicitly an empty generate_diff option or use:

[some group]
generate_diffs = none


If the browser_base_url option is defined and not empty, the svnmailer generates URLs for the specified repository browser. One URL for the whole revision is placed on top and for every generated file diff a conrete URL is written before the actual diff output.

The option takes either one or two parameters. In case of one parameter, it has to be generic. In this case the svnmailer gets URL templates from the revision_url, diff_add_url, diff_copy_url, diff_delete_url and diff_modify_url options.

If you supply two parameters, the first one specifies the browser type. The second parameter determines the base url of the browser installation. The base url is interpreted by the semantics specified by the type, which (treated case insensitive) can be one of:

The base url specifies the root URL of the ViewCVS installation. The svnmailer tries to keep most query parameters you provide (such as root). The following query parameters are always overridden: view, rev, p1, p2, r1 and r2.
The base url specifies the repository's WebSVN root listing URL. Depending on the WebSVN configuration this looks different (see the examples below) and produces different resulting URLs. The svnmailer tries to autodetect the installation type on the basis of the supplied URL. If it contains a query parameter called repname, a non-PATH_INFO installation is assumed. In this case the svnmailer may choke if the last path element ends with a slash (i.e. does not represent a file). This is just a sanity check, because the file will be replaced by the svnmailer, when constructing the final URLs.

As you might have guessed, if the URL doesn't contain this repname query parameter, a PATH_INFO installation is assumed. However, the best way to get the proper base URL is to open the repository root directory in your browser and just copy the URL and paste it into the svnmailer config.

The base url specifies the repository's SVN::Web root listing URL. The svnmailer appends the desired action (like checkout, browse, or diff) to this path and touches the following query parameters: rev, rev1 and rev2. Since SVN::Web currently does not support diffs between files on different locations (such as copied and modified files), the svnmailer just provides a checkout link for the new file.
The base url specifies the repository's Chora root listing URL. The svnmailer appends (or replaces the non-directory portion like index.php with) the desired action php script names (like co.php or diff.php) to this path and touches the following query parameters: r, r1 and r2. Further it removes any Horde session id, so you could just copy the URL from your browser and paste it into your config. The Chora browser has some limitations: Currently it does not support diffs between files on different locations (such as copied and modified files), the svnmailer just provides a checkout link for the new file in that case. It also doesn't seem to support revision overviews; a link to the base directory is supplied instead.
The base url specifies your Trac browser url. The actions (changeset, file) are properly inserted and the changed path appended. The only query parameter touched is rev. Trac (as of version 0.8.2) cannot display single file diffs where one could point to directly, so the svnmailer provides normal file view URLs of the particular revision.

If you don't specify browser_base_url, it defaults to generic. This is compatible behaviour to the original script. If you specify browser_base_url empty, URL generation is turned off.

# Example
# =======
# simple base url:
browser_base_url = viewcvs

# base url with parameters:
browser_base_url = viewcvs

[some group using websvn]
browser_base_url = websvn

# or websvn is configured using PATH_INFO
# (it's called multiviews in their docs)
browser_base_url = websvn

[SVN::Web group]
browser_base_url = SVN::Web

[Chora using group]
browser_base_url = Chora

[Using Trac]
browser_base_url = Trac

[Using something completely different]
browser_base_url = Generic
revision_url =

And here is an output of a change in a sample repository (revision 5):

Author: nd
Date: Thu Jan 6 00:10:04 2005
New Revision: 5

copied a file

    foo/eggs   (contents, props changed)
      - copied, changed from r4, foo/spam

Copied: foo/eggs (from r4, foo/spam)
--- foo/spam (original)
+++ foo/eggs Thu Jan 6 00:10:04 2005
@@ -1,1 +1,1 @@
-This is spam.
+These are eggs.

Propchange: foo/eggs
    color = white

revision_url, diff_add_url, diff_copy_url, diff_delete_url and diff_modify_url

These options can be used to specify URL templates, if the built-in URL generators provided by the browser_base_url option are not sufficient. In order to let the svnmailer use the templates you may not specify browser_base_url at all or set it explicitly to generic. The latter variant is preferred, however.

The revision_url option should specify an URL that gives an overview of the particular revision (log entry, list of changed files). The other options should be configured with URLs that can placed above the accompanying diffs (see generate_diffs). You don't need to specify each option. The ones you leave out, just won't be provided in the mail. By default all options are empty.

In addition to the normal substitution record the following substitutions are available:

Additional substitutions
NameValue Description
path The path of the changed file
base_path The path of the changed file in the previous revision
base_revision The previous revision of the file
base_rev A compatibility alias for base_revision
rev A compatibility alias for revision

Note that all paths are given without a leading slash. Furthermore all substitution values are first UTF-8 encoded and then URL escaped (so André becomes Andr%C3%A9).

# Example
# =======
# (Linebreaks after equal signs are not intended)

browser_base_url = Generic
revision_url =
diff_add_url =
diff_copy_url =
diff_delete_url =
diff_modify_url =


The viewcvs_base_url option is deprecated. Use the browser_base_url option instead.


From time to time it happens, that commits are huge and so are the accompanying notification mails. They may hit limits of mailing lists or mailboxes they are sent to or simply crash some mail client. The long_mail_action option specifies, what "huge" means (in number of bytes) and what action the svnmailer should take to prevent such mails.

long_mail_action takes at least two space or tab separated parameters. The first one defines the number of bytes, one notification mail body should contain at maximum. Note that this number is applied, before the content is MIME encoded, so the actual mail may be slightly greater than the number of bytes specified here. The second parameter defines the action to take. It can be one of the following tokens:

This advises the svnmailer to truncate the notification mail starting with the first line reaching the limit. It will add a note about the number of lines truncated.
If you have a browser_base_url configured and you hit the limit, the svnmailer will just leave out all the diffs and supply the browser URLs only. If you have no browser_base_url configured, you will get neither diffs nor the URLs. If the final mail still gets too long, the showurls action doesn't care about, but showurls/truncate does.
This action token behaves mostly like showurls, except that if the single mail still gets too long, it is truncated after reaching the limit. A note about the number of truncated lines is appended then.
This action splits the notification mails into several ones. The mail subject will contain an enumeration string ([1/3], [2/3] etc) so you can read the mails in the proper order later and know if one is missing. In order to keep the mails readable the svnmailer never splits in the middle of a particular diff, but between whole diff blocks (including their meta information). That means, certain mails of the generated sequence may be still too long (containing one single huge diff). The split action doesn't care about this. If you want to truncate such huge single mails, use the split/truncate token instead.

Experience shows, that if the number of mails grows (huge imports easily lead to 30, 40 or more mails), less people actually read those mails. In order to not waste bandwidth and people's good humor, you may want to limit the number of mails per commit. This is done by extending the token by a slash and the maximum number of mails. If this limit is exceeded then, only one mail will be sent, containg just a short summary and an explanation.

This action token behaves mostly like split, except that overlong single mails (containing one huge diff) are truncated after reaching the limit. A note about the number of truncated lines is appended then.

The optional final parameters determine, whether the configured behaviour should be applied to revision property changes and/or locks, too. The possible tokens are revprop-changes and locks. Now if the second parameter specifies a truncating action, the speecified notifications are truncated if they would execeed the limit, otherwise they are not touched (They are never split). However, overlong revision property change or lock notifications should happen very rarely.

By default no special action is taken to prevent huge mails. But it is highly recommended to configure one.

# Example
# =======
long_mail_action = 100000 split/truncate

[some group]
# truncate revprop changes as well
long_mail_action = 100000 split/truncate revprop-changes

[second group]
# truncate revprop changes and locks as well
long_mail_action = 100000 split/truncate revprop-changes locks

[other group]
# limit to 3 mails per commit at max
long_mail_action = 100000 split/truncate/3


The long_news_action option defines, how the svnmailer should deal with huge commit notification news articles. It takes exactly the same parameters like the long_mail_action option. Please have a look there for detailed information.

# Example
# =======
long_news_action = 100000 showurls/truncate


The mail_transfer_encoding option specifies, which transfer encoding should be used when sending mails. It takes one of the following tokens:

quoted-printable (default)
The mail will be encoded using quoted-printable. This has the advantage, that no physical line will be longer than 76 bytes (some mail servers cut or reject lines with more than 1000 byte). Disadvantage: The final size depends on the content.
The mail will be encoded using base64. Lines are also broken after 76 bytes and the final size is always 4/3 of the original size. Disadvantage: The encoded content is not human readable.
The mail won't be transfer encoded. This means, that huge lines may be cut or mails containing weird bytes (e.g. \0) may be misinterpreted or rejected.

Note that the mail_transfer_encoding is not applied to multipart mails.

# Example
# =======
mail_transfer_encoding = base64


The news_transfer_encoding option defines the transfer encoding to use for news postings. It takes the value as mail_transfer_encoding.

# Example
# =======
news_transfer_encoding = 8bit


The mail_type option defines how the notification mail should be constructed. If you set it to multipart, the mail will be of type multipart/mixed, otherwise it's a single text mail.

The generation of multipart mails is discouraged, because is has unclear semantics (the diffs are more or less opaque data) and the client support is not as well as it could be. Further the mail_transfer_encoding option is not applied to multipart mails. The diff parts are designated as Content-Transfer-Encoding: binary. Further note, that regardless of the charset, paths are always encoded as UTF-8.

With svnmailer 1.0.4 and later you can also refine the multimail type with options. These options have the form name=value and just have to be appended to the mail_type command. The following options are possible:

The type option allows to specify the content type of the diff parts. A reasonable value could be, for instance, text/x-diff. Several mail clients are known to provide syntax highlighting for the diff then. If the option is not specified it defaults to text/plain.
The disposition option determines how the diff parts appear in the mail. Possible values are inline and attachment. If the option is not specified it defaults to inline.

# Example
# =======
mail_type = single

[some group requested multipart mails]
mail_type = multipart

[multipart mails with options]
mail_type = multipart type=text/x-diff


The apply_charset_property option determines, whether the svnmailer should retrieve the content charset of the modified files from svn:mime-type and svnmailer:content-charset properties. This charset is used to recode the files, when generating the diff, so you get readable notifications, even if you change files with different charsets during one commit.

At first the svn:mime-type property of the particular file (in the particular revision) is evaluated. If it's set and contains a charset attribute (like text/plain; charset=iso-8859-2), the attribute value (iso-8859-2) is chosen as file encoding. Otherwise the svnmailer continues the encoding lookup by evaluating svnmailer:content-charset properties.

The svnmailer:content-charset property can be set either for the file itself or for one or more of its parent directories (while more specific definitions override less specific ones). If set for a particular file, the property just contains the charset name:

# Example: setting a specific file charset
$ svn pset svnmailer:content-charset koi8-r

If the svnmailer doesn't find the property set for the changed file, it looks for the svnmailer:content-charset property in the parent directories up to /. These directory properties are the way to specify file charsets in a more general manner. If specified, they are expected to contain shell-style glob/charset definitions, one per line. Such a line looks like:

glob = charset

Leading and trailing spaces, empty lines, lines starting with # and lines without a = character are ignored. Those directory properties can be easily set using the svn propedit command:

# (note the trailing dot)
$ svn pedit svnmailer:content-charset .

Now an editor should be opened and you can add the desired definitions. A sample property could look like:

# German files
* = iso-8859-1
* = iso-8859-1

# Russian files
* = koi8-r
* = koi8-r

Note that the globs apply to the whole file path including the leading slash. They are interpreted case sensitive by python's fnmatch module. The content of the property is expected to be UTF-8 encoded.

If apply_charset_property is false or there's no svnmailer:content-charset property defined or the charset cannot be found in python's codec collection, the svnmailer falls back to iso-8859-1, which translates literally to unicode and so just represents the byte stream in character form. If the file content is miscoded (according to the chosen charset), uninterpretable byte sequences are represented by the replace character (U+FFFD). Note that asian encodings are not present in python 2.3. If you want to use such encodings, you need to install the CJKCodecs (which are included in python 2.4 and later).

The "boolean" values accepted by this option are yes, on, true and 1 for the "true" case and no, off, false, 0, none and the empty string for the "false" case. The default is false.

For convenience the apply_charset_property option can also be written as charset_property.

# Example
# =======
apply_charset_property = yes


The apply_charset_property option specifies whether the svnmailer should write the (either assumed or configured) charset of a file into the diff header. This looks like this:

Modified: foo/
--- foo/ [koi8-r] (original)
+++ foo/ [koi8-r] Sat May 21 18:16:10 2005
@@ -1,4 +1,5 @@

The value in the square brackets represents the content charset before it is recoded to UTF-8. That way you have all information to recode it back if you need to. The following configurations are possible:

Always show the applied charset
Never show the applied charset
Show the applied charset only if it was explicitly configured

If the option is unset or empty the svnmailer picks nondefault as default ;-).

# Example
# =======
show_applied_charset = yes


The custom_header option defines a custom header line that is attached to each mail sent for the particular group (or all groups, if you make it default). This allows for better filtering of the mails than just by subject. If the mails are sent to a mailing list, you probably don't need a custom header, because most mailing list software defines its own (and probably better) headers.

The supplied option value consists of two space or tab separated parts -- the header name and the header value template. The header name is always prepended with X- and invalid characters (according to RFC 2822) are stripped. The header value is a substitution template. Note that if the final header value contains characters, that are not contained by the us-ascii character set, it is encoded according to RFC 2047. If you supply the header name only, the header will be attached empty.

If groups are merged during the selection process and multiple custom headers are found, they are all attached to the mail. If multiple values for the same header name are found, they are merged with a comma and a space between. By default custom_header is empty.

# Example
# =======
for_repos = .*/(?P<REPOS>[^/]+)$
custom_header = SVN-Repository %(REPOS)s

[some group]
for_repos = .*/public$
# ...

[group without custom header]
custom_header =

In the above example the svnmailer sends all mails for the group some group with an additional header named X-SVN-Repository containing the value public.


The extract_x509_author option is useful, if you're using SSL client certificates in conjunction with SSL Options +FakeBasicAuth for repository authentication. If set to a positive value (e.g. yes), the svnmailer tries to extract the author's real name and email address from the supplied x509 Subject Distinguished Name (DN). In case of successful extraction it adds the following values to the substitution parameters:

The contents of the Common Name field (which is assumed to be UTF-8 encoded). This value also will be taken by the message generator for the meta data on top of the message.
The contents of the email address field
A string consisting of CN and email address usable as full email address. It has the form Common Name <email address>, where the Common Name is encoded according to RFC 2047.

The "boolean" values accepted by this option are yes, on, true and 1 for the "true" case and no, off, false, 0, none and the empty string for the "false" case. The default is false.

For convenience the extract_x509_author option can also be written as x509_author.

# Example
# =======
extract_x509_author = yes
from_addr = %(x509_address)s


The cia_project_name specifies the project name under which the changes are tracked in a CIA real time tracker. In order to track your projects using CIA notifiations you need to define the cia_rpc_server option properly at least and a cia_project_name for the notification groups that should be tracked. However, you should consider to set cia_project_path to a reasonable value.

The cia_project_name option value is a substitution template, so it is possible to extract the project name from the path or the like. By default there's no project name defined.

# Example
# =======
cia_rpc_server =

[some group]
cia_project_name = Example Project

cia_project_module, cia_project_branch and cia_project_submodule

These options further refine the project description submitted to the CIA tracker. Note that you still need to specify a project name to submit the notification to the CIA server at all.

Like cia_project_name the option values are substitution templates, so it is possible to extract some information from the path. By default all of the descriptions are empty.

# Example
# =======
cia_rpc_server =

for_paths = [^/]+/(?P<MOD>[^/]+)/(?:branches/(?P<BRA>[^/]+)/)?
cia_project_module = %(MOD)s
cia_project_branch = %(BRA)s

[some group]
cia_project_name = Example Project


The cia_project_path option defines the path in the repository that should be stripped from the beginning of the paths changed when submitting the file list to the CIA tracker. This lets tracked projects look more self containing. A real life example is the Apache HTTP Server. Its position in the ASF repository is /httpd/httpd/{trunk,branches,tags}. A reasonable cia_project_path value would be httpd/httpd/. Note that the specified path is normalized before being stripped. That means, any leading slash is removed and a trailing slash is appended if needed.

Like cia_project_name the option value is a substitution template, so it is possible to extract this information from the path. By default no path is stripped.

# Example
# =======
cia_rpc_server =

for_paths = (?P<PATH>[^/]+)/(?P<MOD>[^/]+)/(?:branches/(?P<BRA>[^/]+)/)?
cia_project_module = %(MOD)s
cia_project_branch = %(BRA)s
cia_project_path = %(PATH)s

[Example Project]
cia_project_name = %(group)s


Substitutions are a powerful feature that can simplify some configurations very much. They base on named python format strings. The format strings are similar to the ones you may already know from the printf() function of C or perl, except that the particular formats are not determined by order, but by name. To give a particular format a name, you just write %(name)s instead of %s (for a string). The name can be any sequence of characters.

As usual, when dealing with such format strings, if you want to express a literal %, you need to duplicate it (%%). Note that you should limit your formats to strings (i.e. the s format), because that's what the svnmailer always supplies. Here is a real life example:

from_addr = %(author)

Well, where are the format names and values taken from? Depending on the option they are either fixed or determined dynamically. The former -- simpler -- variant is used by the diff_command option. For this option the svnmailer defines, which format names and values are used (label_from, label_to, from and to). For the actual meaning of these format names have a look at the diff_command description.

The dynamic definition of format names and values is a bit more complex. This is used by the address templates, the the subject templates, the CIA project descriptions and the custom_header option. There the list of format names (and values) is taken from previously matched regular expressions (default for_repos, default for_paths, group for_repos and group for_paths -- in that order, later definitions override earlier ones). This relies on another python feature: named matching groups. These are like normal storing parentheses, but you can give them a name. Instead of (to-match), you write (?P<name>to-match) in your regular expression. Such a name has to look like a valid python identifier. After a matching regular expression is executed, the svnmailer stores such named groups for later use separatly for each notification group. A typical use case of this feature is:

[some group]
for_paths = projects/(?P<PROJECT>[^/]+)/
to_addr = %(PROJECT)

Besides the regular expression matches the substitution value record initially contains some fixed values: revision, property, author and group. revision contains the revision number if there is one available (e.g. locks are not tied to a particular revision). In the case of revision property change notifications property contains the name of the propery modified. author contains the author of the particular event (or no_author if no author could be determined). group contains the name of the notification group (that is the section heading without the braces). Further the record may contain the values described at the extract_x509_author option. All initial values can be overridden by one of the regular expressions described above. Additionally the subject templates provide more predefined values, but have a look there for details.

Configuring the Repository

Now, after you've created a config file, you can hook the svnmailer into the repository. Subversion uses so called hook scripts to perform customized actions at certain stages of a change event. (Note that the following descriptions don't apply directly to Windows.)

Configuring For Commit Messages

In order to run the svnmailer for normal commits you need to call it at the post-commit stage. If your post-commit hook isn't customized already, change into the repository/hooks directory and copy the post-commit.tmpl template to post-commit It's generally a good idea, to keep the template file for later reference.

# change into the hooks directory
# (/var/svn/public is the repository in question)
$ cd /var/svn/public/hooks

# create the actual hook script from template
$ cp post-commit.tmpl post-commit

# edit the file
$ vi post-commit

# make it executable
$ chmod 755 post-commit

After you've copied the template open the newly created hook script with your favorite editor. You will see a lot of comments describing the purpose of the hook and giving some hints about file system permissions etc. After you've read these comments, there follows a small shell script. Typcially there are sample scripts activated, you may not want to run. If so, comment them out or delete the entries. Finally to activate the svnmailer, add the following:

# the location of svn-mailer may be customized,
# use the real location.
/usr/bin/svn-mailer --commit --config /path/to/your/config \
--repository "${REPOS}" --revision "${REV}" &

Of course, the REPOS and REV variables are only available if you left the definitions below the comments. Anyway, that's it. After you saved the file and closed your editor, you only have to make it executable and subversion will execute it after every commit. It is, however, a good idea to test the hook script as the user who runs it, before doing the next commit (That is only possible, if there are already revision stored in the repository):

# in this example wwwrun is the user the httpd runs as
$ sudo -u wwwrun ./post-commit /var/svn/public 1

Now you should receive a notification. If not, you should get a descriptive error message, what went wrong. By the way: a typical error is a non-readable configuration file.

Configuring For Revision Property Changes

Configuring for a revprop change message is mostly equal to commit messages. If you do not allow to modify revision properties, you don't need to care and can stop here. If you want to allow for revision property changes, you should read the related subversion documentation regarding the pre-revprop-change and post-revprop-change hooks first.

You need to hook the svnmailer into post-revprop-change to get a notification containing the new property value.

# change into the hooks directory
# (/var/svn/public is the repository in question)
$ cd /var/svn/public/hooks

# create the actual hook script from template
$ cp post-revprop-change.tmpl post-revprop-change

# edit the file
$ vi post-revprop-change

# make it executable
$ chmod 755 post-revprop-change

After opening the hook script the same warnings as for post-commits apply: remove all stuff, you don't want there. After that you can add the svnmailer command to the script. Depending on the subversion release, the command line may differ. Starting with subversion 1.2, the action taken on the property and the old property value (via STDIN) are supplied to the hook script.

# the location of svn-mailer may be customized,
# use the real location.

# command line for SVN < 1.2
/usr/bin/svn-mailer --propchange --config /path/to/your/config \
--repository "${REPOS}" --revision "${REV}" \
--author "${USER}" --propname "${PROPNAME}" &
# command line for SVN >= 1.2
/usr/bin/svn-mailer --propchange --config /path/to/your/config \
--repository "${REPOS}" --revision "${REV}" \
--author "${USER}" --propname "${PROPNAME}" \
--action "${ACTION}" &

The REPOS, REV, USER, PROPNAME and ACTION variables should be still defined, of course. Close the file, make it executable and test it:

# in this example wwwrun is the user the httpd runs as
# SVN < 1.2
$ sudo -u wwwrun ./post-revprop-change /var/svn/public 1 nd svn:log

# SVN >= 1.2
$ echo -n | sudo -u wwwrun \
./post-revprop-change /var/svn/public 1 nd svn:log A

Configuring For Lock Notifications

Locking notifications can be sent out when paths are either locked or unlocked. Locking is a new feature of subversion 1.2. In order to activate those notifications you need to hook the svnmailer into the post-lock hook and the post-unlock hook respectively. Similar to the other hooks there should be templates in your hooks/ directory. However, if you've just upgraded your subversion installation to 1.2, they may be not there, because the templates are generated only when you're creating a new repository. So if you don't find them, just create files in your hook directory named post-lock and post-unlock.

Well, when you're editing the template copies, make sure there's nothing called you do not want. And that's what you have to put into the hook scripts to let the svnmailer do its work:

# the location of svn-mailer may be customized,
# use the real location.

# post-lock hook:
/usr/bin/svn-mailer --lock --config /path/to/your/config \
--repository "${REPOS}" --author "${USER}" &
# post-unlock hook:
/usr/bin/svn-mailer --unlock --config /path/to/your/config \
--repository "${REPOS}" --author "${USER}" &

The REPOS and USER variables should be still defined. If you've created the files from the scratch, the variable definitions are as follows:


When you're done, close the file, make it exectuable and test it:

# in this example wwwrun is the user the httpd runs as
$ echo "/some/existing/file/in/your/repos" | \
sudo -u wwwrun ./post-lock /var/svn/public nd

$ echo "/some/existing/file/in/your/repos" | \
sudo -u wwwrun ./post-unlock /var/svn/public nd

Congratulations, you're done. Happy Hacking!

The svnmailer Command Line

The command line parameters
Parameter Name Type Description
--version action Shows the version and exits
--help action Shows a short help and exits
--debug flag Turns on debugging mode
--background flag Does the work from the background
--path-encoding (-e) string The encoding used for file and path names
--config (-f) filepath The location of the main config file
--revision (-r) int The revision number to process
--repository (-d) filepath The repository to process
--commit (-c) flag Turns on the commit mode
--propchange (-p) flag Turns on the revpropchange mode
--lock (-p) flag Turns on the lock mode. SVN 1.2 and later
--unlock (-p) flag Turns on the unlock mode. SVN 1.2 and later
--author (-a) string The author of the change (usually for revprop changes only)
--propname (-n) string The name of the modified property (for revprop changes only)
--action (-o) character The revprop change action (A, D or M). SVN 1.2 and later

The svnmailer is usually invoked via a small script called svn-mailer. It's located, whereever python installs it (have a look at the installation procedure for details). On my box it was installed to /usr/bin/svn-mailer. The script needs a number of command line parameters, which control its behaviour.

svn-mailer supports two command line styles: the "old-style" command line, which is compatible to the original script and its own "new-style" command line, which takes named parameters only. The latter is recommended, however.

The "old-style" variant is derived from svn itself. It starts with a subcommand (either commit or propchange) and continues with the necessary parameters. Further it is fixed. No further options are allowed. Here are the old-style variants (optional parameters are enclosed in square brackets):

svn-mailer commit repos revision [config]
svn-mailer propchange repos revision author propname [config]
# (optionally) for SVN >= 1.2:
svn-mailer propchange2 repos revision author propname action [config]
svn-mailer lock repos author [config]
svn-mailer unlock repos author [config]

These lines, translated into the new style, look about:

svn-mailer [--commit] [--config=config] \
--repository=repos --revision=revision

svn-mailer --propchange [--config=config] \
--repository=repos --revision=revision \
--author=author --propname=propname

# (optionally) for SVN >= 1.2:
svn-mailer --propchange [--config=config] \
--repository=repos --revision=revision \
--author=author --propname=propname \

svn-mailer --lock [--config=config] \
--repository=repos --author=author

svn-mailer --unlock [--config=config] \
--repository=repos --author=author

The following sections describe all available "new-style" command line parameters in detail.


If you supply the --version parameter, the svnmailer writes its own name and version plus the version of the subversion bindings it uses to stdout and exits immediately.

$ svn-mailer --version
with svn 1.1.3 (r12730)


If you supply the --help parameter, the svnmailer writes a short usage information to stdout and exits immediately.

$ svn-mailer --help
usage: svn-mailer options
... description of the parameters ...


The --debug option turns the svnmailer into debug mode. This means, that no mails are sent, but written to stdout, so you can test your installation. Additionally a header called X-Config-Groups is attached, which contains the selected notification groups.

$ svn-mailer --debug --repository=testrepos --revision=1
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
X-Mailer: svnmailer-1.1.0-dev-r1373
Date: Sat, 15 Jan 2005 21:03:58 -0000
Subject: r1 - foo
X-Config-Groups: [defaults]

Author: nd
Date: Wed Jan  5 00:23:43 2005
New Revision: 1

added directory foo


--background (-b)

The --background option lets the svnmailer do its work detached from the hook script. From the view of the hook script it exits immediately (with success). From the view of the mailer a child process is started, which inherits STDIN, STDOUT, STDERR and the command line and the actual work is done asynchronously. In order to catch mailer errors it is a good idea to log the STDERR channel to a file:

$ svnmailer [options] --background 2>>/path/to/svnmailer-errors.log

The idea behind the --background option is to speed up the hook script. If the mailer is started synchronously the committer may get the feeling that the commit hangs for a while, especially if the svnmailer does a lot of file system access operations.

If your platform does not support starting a detached child process, or an error occurs the --background option writes a warning to STDERR and does its work synchronously instead. Platforms supporting the fork(2) and setsid(2) system calls are supported at all. On Windows it depends. When running Python 2.3 you need to have the pywin32 package installed. Fortunately ActivePython already bundles the pywin32 package. Python versions greater or equal 2.4 provide the necessary interfaces out of the box.

Warning: The --background option is experimental on windows platforms.

--path-encoding (-e)

The --path-encoding option can be used to override the assumptions regarding the path name encodings the svnmailer makes based on the locale environment. This affects the paths given both via command line and config. If you're using file and path names consisting of ASCII characters only, you can (and probably should) safely skip the following paragraphs.

Normally this option should not be necessary. However, the POSIX filesystem doesn't know anything about encodings at all, so it can easily happen, that the bytes that happen to represent the file name are not interpretable as characters in the locale used. --path-encoding provides a workaround for this kind of problem (at least it tries to). You can specify the encoding of filenames independet from the locale. It just should be one of the codecs recognized by python.

Unfortunately there may be still a problem. The function to open the repository of underlying subversion bindings expects an unicode path and recodes it back to the locale representation (possibly using a slightly different algortithm than python). That way, the svnmailer has to recognize the encoding of the path given in the command line. The conversion back to the locale (inside svn) is not influencable by the --path-encoding option. If you get a crash caused by the subversion library, that describes a recoding error, you can try setting the LC_CTYPE environment variable to some ISO-8859-1 locale (like en_US.iso-8859-1), and just treat the octets of the file names as characters (the reason for this is, that the codepoints of ISO-8859-1 are compatible to unicode, so it should be all recoded smoothly) -- or use --path-encoding to supply it in a readable form to the svnmailer.

Anyway, the best solution is either to use just ASCII names or to use consistent encodings for file names and locales. UTF-8 is a good choice.

--config (-f)

The --config option is used to pass the path and name of a configuration file to the svnmailer. If you omit this option, the svnmailer looks at some default locations for a config, which are in order: repos/conf/mailer.conf, a file called mailer.conf in the same directory the svn-mailer script is located in and /etc/svn-mailer.conf. The file found first wins. If you didn't supply a --config option and no file is found at the default locations, the svnmailer exits with an error. It is recommended, to supply always the full path to the config file, so that no misunderstandings can happen.

If the supplied config file is a single dash (-), the config is read from STDIN. Note that this interferes with the property value supplied via stdin on revision property changes in subversion 1.2 and later.

For convenience the --config option can also be written as -f.

--revision (-r)

The --revision parameter defines the revision to process. It has to be a number and a valid revision of the supplied repository, otherwise the svnmailer will exit with an error. Typically you don't need to worry about this, since the proper revision number is passed to the hook script by subversion itself.

For convenience the --revision parameter can also be written as -r.

--repository (-d)

The --repository parameter specifies the full filesystem location of the repository. This is the path matched against by the for_repos configuration option. If it does not point to a subversion repository, the svnmailer will exit with an error. Typically you don't need to worry about this, since the proper repository path is passed to the hook script by subversion itself.

For convenience the --repository parameter can also be written as -d.

--commit (-c), --propchange (-p), --lock (-l) and --unlock (-u)

These options tell the svnmailer whether it is running in the post-commit, the post-revprop-change, the post-lock or the post-unlock hook. They are mutually exclusive (in fact, if you supply more of one, the most right option wins). The --commit option is default and can be omitted. The --lock and --unlock switches are only available if you're using subversion 1.2 or later. Have a look at the repository configuration section for further information.

The options also can be abbreviated, of course. --commit can be written as -c, --propchange as -p. --lock as -l and --unlock as -u.

--author (-a)

The --author option defines the author of the change. This is typically used in the post-revprop-change hook, where the author maybe different from the original revision author. The change author is passed to the hook script by subversion.

It is also possible to pass the --author option from within the post-commit hook. In that case it overrides the revision author noted in the repository. This is useful, if there is no revision author stored.

For convenience the --author parameter can also be written as -a.

--propname (-p)

The --propname parameter is only used for revprop-change notifications. It specifies the name of the property being modified. The name is passed to the hook script by subversion.

For convenience the --propname parameter can also be written as -n.

--action (-o)

The --action parameter is only used for revprop-change notifications. It specifies the action that was taken on the specified property. It contains one of the letters A (for "added"), M (for "modified") or D (for "deleted"). The proper value is passed to the hook script by subversion 1.2 and later. If the svnmailer is in propchange mode and this parameter is specified and valid, the svnmailer reads the old property value from STDIN and generates a diff between the old and the new value.

For convenience the --action parameter can also be written as -o (shortcut for "operation" :-).