1. About project
1.1. What is it?
git-as-svn (https://github.com/git-as-svn/git-as-svn) emulates Subversion repository on top of Git repository.
It allows you to work with Git repositories using any tool compatible with Subversion 1.8+:
console svn, TortoiseSVN, SvnKit, SmartSVN, etc.
1.2. Features
This implementation allows the majority of Subversion-users to work without thinking about what they actually use Git-repository.
- 
Nearly all Subversion commands:
- 
svn checkout, update, switch, diff
 - 
svn commit
 - 
svn copy, move [1]
 - 
svn cat, ls
 - 
svn lock, unlock
 - 
svnsync
 
 - 
 - 
Transparent mapping of .gitattributes/.gitignore to Subversion properties
 - 
Git LFS, including locks
 - 
Git submodules [2]
 
1.3. What is project goal?
The project is designed to allow you to work with the same repository as Git, Subversion and style.
- Git style
 - 
The basic idea is that the developer works in the local branch. His changes do not affect the work of other developers, but nonetheless they can be tested on CI farm, review by another developer and etc.
This allows each developer to work independently, as best he can. He can change and saving intermediate versions of documents, taking full advantage of the version control system (including access to the change history) even without network connection to the server.
Unfortunately, this approach does not work with not mergeable documents (for example, binary files).
 - Subversion style
 - 
The use of a centralized version control system is more convenient in the case of documents do not support the merge (for example, with binary files) due to the presence of the locking mechanism and a simpler and shorter publication cycle changes.
 
The need to combine Git and Subversion style work with one repository arises from the fact that different employees in the same project are working from fundamentally different data. If you overdo, you Git programmers, and artists like Subversion.
1.4. Why do we need it?
This project was born out of division teams working on another project into two camps:
- 
People who have tasted the Git and do not want to use Subversion (eg programmers);
 - 
People who do not get from Git practical use and do not want to work with him, but love Subversion (eg designers).
 
To divide the project into two repository desire was not for various reasons.
At this point, saw the project http://git.q42.co.uk/git_svn_server.git with Proof-of-concept implementation svn server for git repository. After this realization svn server on top of git and didn’t seem completely crazy idea (now it’s just a crazy idea) and started this project.
2. Installation
| Subversion versions prior to 1.8 are not supported because git-as-svn relies on inherited properties Subversion feature. | 
2.1. .gitattributes
By default, Git uses native line ending for text files and determines whether file is text or not using heuristics that do not match Subversion behavior.
In order to fix this discrepancy, add the following to your .gitattributes file:
* -text
This will force Git to store files as-is unless end-of-line conversion is explicitly configured for them. See gitattributes documentation for additional info.
2.2. Installation on Debian/Ubuntu
You can install git-as-svn on Debian/Ubuntu using the following commands:
# Set up repository
curl -1sLf 'https://dl.cloudsmith.io/public/git-as-svn/git-as-svn/setup.deb.sh' | sudo -E bash
# Install git-as-svn
sudo apt-get install git-as-svn
# You only need this if you plan to use git-as-svn builtin Git-LFS server
sudo apt-get install git-as-svn-lfs
2.2.1. git-as-svn package
This package contains the git-as-svn.
After you install git-as-svn is run in daemon mode and is available on the svn-protocol on port 3690. The daemon runs as git user.
To access the server, you can use the user:
Login: test
Password: test
You can check configuration with command like:
svn ls --username test --password test svn://localhost/example/master
Used directories
This package by default is configured to use the following directories:
- /etc/git-as-svn
 - 
This directory contains git-as-svn configuration files.
 - /usr/share/doc/git-as-svn
 - 
This directory contains git-as-svn documentation.
 - /var/git/lfs
 - 
This directory contains Git Large File Storage files.
It must be writable by user
git. - /var/git/repositories
 - 
This directory is used by default to store the Git-repositories.
It must be writable by user
git. - /var/log/git-as-svn
 - 
This directory is used to record log files.
It must be writable by user
git.See logging documentation on log configuration.
 - /var/cache/git-as-svn
 - 
This directory is used to store the git-as-svn cache.
It must be writable by user
git.The loss of the contents of this directory is not critical for operation and does not entail the loss of user data.
 
2.2.2. git-as-svn-lfs package
This package contains the git-lfs-authenticate script required for git-as-svn builtin LFS server
2.3. Manual download
To try git-as-svn you need:
- 
Install Java 21 or later;
 - 
Download archive from site https://github.com/git-as-svn/git-as-svn/releases/latest;
 - 
After unpacking the archive change working path to the uncompressed directory and run the command:
bin/git-as-svn -c doc/examples/config.yml
 
This will start git-as-svn server with following configuration:
- 
The server is accessible via svn-protocol on port 3690.
You can check server with command like:
svn ls svn://localhost/example/master
 - 
To access the server, you can use the user:
Login: test
Password: test
 - 
Cache and repository will be created in
builddirectory:- 
example.git— repository directory, accessible via svn-protocol; - 
git-as-svn.mapdb*— cache files for expensive computed data. 
 - 
 
3. Command-line parameters
git-as-svn supports the following command-line parameters:
-?|-h|--help- 
print help for command-line parameters.
 -c <file>|--config <file>- 
use an alternative configuration file instead of a default file.
 -t- 
test the configuration file: git-as-svn checks the configuration for correct syntax.
 
-t doesn’t perform full git-as-svn initialization, so it is still possible that git-as-svn will fail to startup due to invalid configuration even though -t passed successfully.
 | 
-T- 
same as
-t, but additionally dump configuration files to standard output. -v|--version- 
print git-as-svn version.
 
4. GitLab integration
git-as-svn supports integration with GitLab >= 10.2.
This includes:
- 
User authentication against GitLab
 - 
Access control depending on user permissions in GitLab
 - 
Usage of GitLab LFS server for transparent handling of LFS files for svn users
 - 
Automatic discovery of new repositories created in GitLab
 - 
Running GitLab repository hooks if any installed
 
4.1. Configuration
This chapter assumes that GitLab is installed using standard Omnibus installation to /opt/gitlab.
 | 
git-as-svn uses direct file access to Git repositories, so it needs to run from the same user as GitLab (normally, git).
If you’re installing both git-as-svn and Gitlab from Debian packages, no additional actions are required.
- 
Create GitLab Personal Access token for git-as-svn. Token needs to have following scopes:
api,sudo,read_repository,read_user,write_repository. Do not addread_apiscope to token. - 
Change
userDBto!gitlabUsers. This will tell git-as-svn to authenticate users against GitLab server:userDB: !gitlabUsers { # Users can either authenticate using their GitLab login+password or login+access token # Possible values: Password, AccessToken # Default: Password # # authentication: Password } - 
Configure builtin git-as-svn webserver:
shared: - !web # git-as-svn base url. Leave empty for autodetect. # Default: empty # # baseUrl: http://localhost:8123/ listen: - !http # The network interface where git-as-svn web server binds to as an IP address or a hostname. If 0.0.0.0, then bind to all interfaces. # Default: localhost # # host: localhost # Port where git-as-svn web server listens on. # Default: 8123 # # port: 8123 # HTTP idle timeout milliseconds. If not a single byte is sent or received over HTTP connection, git-as-svn closes it. # -1 = Use Jetty default # 0 = Disable timeout # Default: -1 # # idleTimeout: -1 # Tells git-as-svn to handle X-Forwarded-* headers. # Enable this if git-as-svn web server is running behind reverse HTTP proxy (like nginx) # Default: false # # forwarded: false - 
Configure GitLab URL and token:
shared: - !gitlab # GitLab base URL. This must match GitLab EXTERNAL_URL. # Default: http://localhost/ # url: <GitLab URL> # GitLab access token. Note that git-as-svn requires sudo access. token: <GitLab Access Token> - 
Configure git-as-svn to use GitLab as repository list source:
repositoryMapping: !gitlabMapping # Filesystem location where GitLab stores repositories # Note that git-as-svn requires write access # Default: /var/opt/gitlab/git-data/repositories/ # # path: /var/opt/gitlab/git-data/repositories/ # Common settings for all repositories exposed to svn:// # template: pusher: !pushEmbedded # This tells git-as-svn where GitLab commit hooks are located hooksPath: /opt/gitlab/embedded/service/gitaly-ruby/git-hooks - 
Restart git-as-svn after changing its config. If your OS uses Systemd, this can be done via
sudo systemctl restart git-as-svn. - 
Add
git-as-svn:<branch>topics to whatever repositories you want to add to git-as-svn via "Settings → General → Topics" in GitLab project settings. For example, addgit-as-svn:masterto exposemasterbranch. If you want to expose more than one branch, add multiplegit-as-svn:<branch>topics separated by commas. 
4.2. Supported Git LFS modes
- 
git-as-svn uses GitLab LFS API for write operations and direct disk access for read operations. This is recommended option.
lfsMode: !fileLfs # Directory where GitLab stores LFS ojects path: /var/opt/gitlab/gitlab-rails/shared/lfs-objects - 
git-as-svn uses GitLab LFS API for all LFS operations. This mode is slower than
!fileLfs.lfsMode: !httpLfs {} - 
git-as-svn doesn’t use LFS at all
lfsMode: null 
4.3. Full configuration file example
/etc/git-as-svn/git-as-svn.conf
!config:
# Specifies IP to listen to for svn:// connections
# Default: 0.0.0.0
#
# host: 0.0.0.0
# Specifies port number to listen to for svn:// connections
# Default: 3690
#
# port: 3690
# Subversion realm name. Subversion uses this for credentials caching
# Default: git-as-svn realm
#
# realm: git-as-svn realm
# Traffic compression level. Supported values: LZ4, Zlib, None
# Default: LZ4
#
# compressionLevel: LZ4
# If enabled, git-as-svn indexed repositories in parallel during startup
# This results in higher memory usage so may require adjustments to JVM memory options
# Default: true
#
# parallelIndexing: true
# Sets cache location
cacheConfig: !persistentCache
  path: /var/cache/git-as-svn/git-as-svn.mapdb
# Tells git-as-svn to use GitLab API for repository list
repositoryMapping: !gitlabMapping
  # Filesystem location where GitLab stores repositories
  # Note that git-as-svn requires write access
  # You normally do not need to change it
  # Default: /var/opt/gitlab/git-data/repositories/
  #
  # path: /var/opt/gitlab/git-data/repositories/
  # Common settings for all repositories exposed to svn://
  #
  template:
    # renameDetection: true
    # emptyDirs: Disabled
    # format: Latest
    pusher: !pushEmbedded
      # This tells git-as-svn where GitLab commit hooks are located
      hooksPath: /opt/gitlab/embedded/service/gitaly-ruby/git-hooks
# Tells git-as-svn to authenticate users against GitLab
userDB: !gitlabUsers {
  # Users can either authenticate using their GitLab login+password or login+access token
  # Possible values: Password, AccessToken
  # Default: Password
  #
  # authentication: Password
}
shared:
  # git-as-svn builtin web server
  # It is used for GitLab system hook for repository creation/deletion notifications
  # Also, git-as-svn builtin LFS server is served through it
  - !web
    # git-as-svn base url. Leave empty for autodetect.
    # Default: empty
    #
    # baseUrl: http://localhost:8123/
    listen:
      - !http {
        # The network interface where git-as-svn web server binds to as an IP address or a hostname.  If 0.0.0.0, then bind to all interfaces.
        # Default: localhost
        #
        # host: localhost
        # Port where git-as-svn web server listens on.
        # Default: 8123
        #
        # port: 8123
        # HTTP idle timeout milliseconds. If not a single byte is sent or received over HTTP connection, git-as-svn closes it.
        # -1 = Use Jetty default
        # 0 = Disable timeout
        # Default: -1
        #
        # idleTimeout: -1
        # Tells git-as-svn to handle X-Forwarded-* headers.
        # Enable this if git-as-svn web server is running behind reverse HTTP proxy (like nginx)
        # Default: false
        #
        # forwarded: false
      }
  # Configures GitLab access for git-as-svn
  - !gitlab
    # GitLab base URL
    # Default: http://localhost/
    #
    # url: http://localhost/
    # Tells git-as-svn to use GitLab for LFS objects and file locking
    # Default: !httpLfs {}
    #
    lfsMode: !fileLfs
      # Directory where GitLab stores LFS ojects
      path: /var/opt/gitlab/gitlab-rails/shared/lfs-objects
    # GitLab access token
    # Note that git-as-svn requires sudo access
    token: <GitLab Access Token>
    # Path to Gitaly socket file
    # You normally do not need to change it
    # Default: /var/opt/gitlab/gitaly/gitaly.socket
    #
    # gitalySocket: /var/opt/gitlab/gitaly/gitaly.socket
    # Gitaly secret token
    # This must match gitaly_token in /etc/gitlab/gitlab.rb
    # You normally do not need to change it
    # Default: secret token
    #
    # gitalyToken: secret token
    # Path to Gitaly binaries dir
    # You normally do not need to change it
    # Default: /opt/gitlab/embedded/bin
    #
    # gitalyBinDir: /opt/gitlab/embedded/bin
    # Value for GL_PROTOCOL hooks environment variable
    # See https://docs.gitlab.com/ee/administration/server_hooks.html#environment-variables-available-to-server-hooks
    # Possible values: HTTP, SSH, Web
    # Default: Web
    #
    # glProtocol: Web
5. Gitea integration
git-as-svn supports integration with Gitea >= v1.7.2.
This includes:
- 
User authentication against Gitea
 - 
Access control depending on user permissions in Gitea
 - 
Usage of Gitea LFS server for transparent handling of LFS files for svn users
 - 
Automatic discovery of new repositories created in Gitea
 - 
Running Gitea repository hooks if any installed
 
| git-as-svn requires Sudo Gitea token | 
| git-as-svn uses direct file access to Git repositories, so it needs to run from the same user as Gitea | 
Configuration file example:
!config:
# Specifies IP to listen to for svn:// connections
# Default: 0.0.0.0
#
# host: 0.0.0.0
# Specifies port number to listen to for svn:// connections
# Default: 3690
#
# port: 3690
# Subversion realm name. Subversion uses this for credentials caching
# Default: git-as-svn realm
#
# realm: git-as-svn realm
# Traffic compression level. Supported values: LZ4, Zlib, None
# Default: LZ4
#
# compressionLevel: LZ4
# If enabled, git-as-svn indexed repositories in parallel during startup
# This results in higher memory usage so may require adjustments to JVM memory options
# Default: true
#
# parallelIndexing: true
# Sets cache location
cacheConfig: !persistentCache
  path: /var/cache/git-as-svn/git-as-svn.mapdb
# Tells git-as-svn to use Gitea API for repository list
repositoryMapping: !giteaMapping
  # Filesystem location where Gitea stores repositories
  # Note that git-as-svn requires write access
  path: /data/git/repositories
  # Common settings for all repositories exposed to svn://
  #
  # template:
  #   branches:
  #     - master
  #   renameDetection: true
  #   emptyDirs: Disabled
  #   format: Latest
# Tells git-as-svn to use Gitea API for user authentication
userDB: !giteaUsers {}
shared:
  # Configures Gitea API for git-as-svn
  - !gitea
    # URL where your Gitea instance API is available
    url: http://localhost:3000/api/v1
    # Tells git-as-svn to store Git-LFS objects through Gitea LFS API
    # Note that this needs to be in sync with Gitea LFS_START_SERVER config option
    lfs: false
    # Gitea access token
    # Note that git-as-svn requires Gitea Sudo permission in order to authenticate users
    token: 90c68b84fb04e364c2ea3fc42a6a2193144bc07d
6. LFS server
git-as-svn has built-in Git Large File Storage server.
Configuration file example:
/etc/git-as-svn/git-as-svn.conf
shared:
  # git-as-svn builtin web server
  # It is used for GitLab system hook for repository creation/deletion notifications
  # Also, git-as-svn builtin LFS server is served through it
  - !web
    # git-as-svn base url. Leave empty for autodetect.
    # Default: empty
    #
    # baseUrl: http://localhost:8123/
    listen:
      - !http {
        # The network interface where git-as-svn web server binds to as an IP address or a hostname.  If 0.0.0.0, then bind to all interfaces.
        # Default: localhost
        #
        # host: localhost
        # Port where git-as-svn web server listens on.
        # Default: 8123
        #
        # port: 8123
        # HTTP idle timeout milliseconds. If not a single byte is sent or received over HTTP connection, git-as-svn closes it.
        # -1 = Use Jetty default
        # 0 = Disable timeout
        # Default: -1
        #
        # idleTimeout: -1
        # Tells git-as-svn to handle X-Forwarded-* headers.
        # Enable this if git-as-svn web server is running behind reverse HTTP proxy (like nginx)
        # Default: false
        #
        # forwarded: false
      }
  # Git LFS server
  - !localLfs
    # Secret token for git-lfs-authenticate script
    # secretToken:
    path: /var/git/lfs
6.1. git-lfs-authenticate
Script git-lfs-authenticate (provided by git-as-svn-lfs package) is used by git-lfs to obtain credentials for HTTP access to Git LFS server for Git-users working with Git repository by SSH (https://github.com/github/git-lfs/blob/master/docs/api/README.md).
To check the settings of the script can be run locally on the server the following command:
# Set environment variable defined in configuration file
export GL_ID=key-1
# Check access to repository
sudo su git -c "git-lfs-authenticate example download"
Or on the client the following command:
ssh git@remote -C "git-lfs-authenticate example download"
The output should look something like this:
{
  "href": "https://api.github.com/lfs/git-as-svn/git-as-svn",
  "header": {
    "Authorization": "Bearer SOME-SECRET-TOKEN"
  },
  "expires_at": "2016-02-19T18:56:59Z"
}
6.2. Running git-a-svn behind Nginx reverse proxy
- 
Add git-as-svn upstream server:
/etc/nginx/nginx.confupstream gitsvn { server localhost:8123 fail_timeout=5s; keepalive 100; } - 
Add resource redirection:
/etc/nginx/nginx.conflocation ~ ^.*\.git/info/lfs/ { proxy_read_timeout 300; proxy_connect_timeout 300; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Frame-Options SAMEORIGIN; proxy_pass http://gitsvn; } 
Also you need to set baseUrl parameter in !web section of git-as-svn configuration file to external URL accessible to LFS users.
7. LDAP (Lightweight Directory Access Protocol)
git-as-svn supports LDAP for user authentication. Refer to your LDAP server documentation to find out what configuration is appropriate in your case.
| Internally, git-as-svn uses UnboundID LDAP SDK for Java for all LDAP communication. | 
# Authenticates a user by binding to the directory with the DN of the entry for that user and the password
# presented by the user. If this simple bind succeeds the user is considered to be authenticated.
userDB: !ldapUsers
  # LDAP server URL
  # It usually specifies the domain name of the directory server to connect to,
  # and optionally the port number and distinguished name (DN) of the required root naming context.
  # For secure connections, use ldaps://
  #
  connectionUrl: ldap://localhost:389/ou=groups,dc=mycompany,dc=com
  # Optional LDAP SSL certificate for secure LDAP connections
  #
  # ldapCertPem: /path/to/ldap.pem
  # Pattern specifying the LDAP search filter to use after substitution of the username.
  #
  searchFilter: (&(objectClass=person)(objectClass=user))
  # LDAP bind configuration
  #
  # [see next documentation section]
  # LDAP attribute, containing user login.
  # Default: sAMAccountName
  #
  # loginAttribute: sAMAccountName
  # LDAP attribute, containing user name.
  # Default: name
  #
  # nameAttribute: name
  # LDAP attribute, containing user email.
  # Default: mail
  #
  # emailAttribute: mail
7.1. Supported LDAP bind methods
7.1.1. ANONYMOUS
Performs SASL ANONYMOUS bind as described in RFC 4505.
| This is default bind type. | 
userDB: !ldapUsers
  bind: !ANONYMOUS {}
7.1.2. CRAM-MD5
Performs SASL CRAM-MD5 bind as described in draft-ietf-sasl-crammd5.
userDB: !ldapUsers
  bind: !CRAMMD5
    authenticationID: <required>
    password: <required>
7.1.3. DIGEST-MD5
Performs SASL DIGEST-MD5 bind as described in RFC 2831.
userDB: !ldapUsers
  bind: !DIGESTMD5
    authenticationID: <required>
    authorizationID: <optional>
    password: <required>
    realm: <optional>
7.1.4. EXTERNAL
Performs SASL EXTERNAL bind as described in RFC 4422.
userDB: !ldapUsers
  bind: !EXTERNAL
    authenticationID: <optional>
7.1.5. PLAIN
Performs SASL PLAIN bind as described in RFC 4616.
userDB: !ldapUsers
  bind: !PLAIN
    authenticationID: <required>
    authorizationID: <optional>
    password: <required>
7.1.6. Simple
Performs LDAPv3 simple bind operation.
userDB: !ldapUsers
  bind: !Simple
    bindDn: <optional>
    password: <optional>
8. Logging
git-as-svn uses Apache Log4j 2 for logging.
Configuration file is located in /etc/git-as-svn/log4j2.xml.
Please, refer to Log4j 2 documentation on this file format and available options.
By default, all messages with INFO priority and higher are logged to /var/log/git-as-svn/git-as-svn.log and rotated at startup and per each 10 MB.
Also, all messages with ERROR priority and higher are logged to /var/log/git-as-svn/git-as-svn.error.log with same rotation policy.
For example, if you want to increase logging verbosity by switching to DEBUG logging, you need to change <Root level="info"> to <Root level="debug"> in /etc/git-as-svn/log4j2.xml
8.1. Loggers available in git-as-svn
- 
git- messages related to operations with Git repositories - 
gitea- messages related to communication with Gitea - 
gitlab- messages related to communication with GitLab - 
ldap- messages related to LDAP user authentication - 
lfs- messages related to Git LFS, including both internal and external LFS server if any of them is configured - 
misc- few unsorted messages, mostly related to startup/shutdown procedures - 
svn- messages related to incoming SVN connections - 
web- messages related to builtin HTTP server 
Additionally, git-as-svn uses some third-party libraries, most notable are JGit and Jetty, that can also log various stuff. Please, refer to their appropriate documentation on available loggers for these projects.
9. Path-based authorization
This feature is currently only supported for repositoryMapping: !listMapping
 | 
git-as-svn supports path-based authorization that allows granting (or denying) permissions to users, very similar to Subversion path-based authorization feature. Typically this is done over the entire repository: a user can read the repository (or not), and they can write to the repository (or not).
It’s also possible, however, to define finer-grained access rules. One set of users may have permission to write to a certain directory in the repository, but not others; another directory might not even be readable by all but a few special people. It’s even possible to restrict access on a per file basis.
9.1. Getting Started with Path-Based Access Control
Here’s a simple example demonstrating a piece of the access configuration which grants read access Sally, and read/write access to Harry, for the path /path/to/directory/ (and all its children) in the repository calc:
repositoryMapping: !listMapping
  repositories:
    calc:
      access:
        /path/to/directory:
          harry: rw
          sally: r
Permissions are inherited from a path’s parent directory. That means we can specify a subdirectory with a different access policy for Sally. Let’s continue our previous example, and grant Sally write access to a child of the directory that she’s otherwise permitted only to read:
repositoryMapping: !listMapping
  repositories:
    calc:
      access:
        /path/to/directory:
          harry: rw
          sally: r
        /path/to/directory/subdirectory:
          sally: rw
Now Sally can write to subdirectory, but can still only read other parts. Harry, meanwhile, continues to have complete read/write access to the whole directory.
It’s also possible to explicitly deny permission to someone via inheritance rules, by using empty string or none:
repositoryMapping: !listMapping
  repositories:
    calc:
      access:
        /path/to/directory:
          harry: rw
          sally: r
        /path/to/directory/secret:
          harry: none
In this example, Harry has read/write access to the entire directory, but has absolutely no access at all to the secret subdirectory within it.
| The thing to remember is that the most specific path always matches first. The server tries to match the path itself, and then the parent of the path, then the parent of that, and so on. The net effect is that mentioning a specific path in the access file will always override any permissions inherited from parent directories. | 
By default, nobody has any access to any repository at all.
If you want to give at least read permission to all users at the roots of the repositories.
You can do this by using the asterisk variable (*), which means "all users":
repositoryMapping: !listMapping
  repositories:
    calc:
      access:
        /:
          '*': r
Note that while all of the previous examples use directories, that’s only because defining access rules on directories is the most common case. You may similarly restrict access on file paths, too.
repositoryMapping: !listMapping
  repositories:
    calc:
      access:
        /README.md:
          harry: rw
          sally: r
You may also specify grant or restrict access only to specific branches.
repositoryMapping: !listMapping
  repositories:
    calc:
      access:
        /README.md:
          harry: r
        master:/README.md:
          harry: rw
In this example, Harry has read access to file on all branches but has read/write access on master branch.
9.2. Access Control Groups
git-as-svn also allows you to define whole groups of users.
To do this, describe your groups within groups section of git-as-svn.conf :
repositoryMapping: !listMapping
  groups:
    calc-developers:
      - harry
      - sally
      - joe
    paint-developers:
      - frank
      - sally
      - jane
Groups can be granted access control just like users.
Distinguish them with an "at sign" (@) prefix:
repositoryMapping: !listMapping
  repositories:
    calc:
      access:
        /:
          '@calc-developers': rw
    paint:
      access:
        /:
          'jane': r
          '@paint-developers': rw
Another important fact is that group permissions are not overridden by individual user permissions.
Rather, the combination of all matching permissions is granted.
In the prior example, Jane is a member of the paint-developers group, which has read/write access.
Combined with the jane = r rule, this still gives Jane read/write access.
Permissions for group members can only be extended beyond the permissions the group already has.
Restricting users who are part of a group to less than their group’s permissions is impossible.
Groups can also be defined to contain other groups:
repositoryMapping: !listMapping
  groups:
    calc-developers:
      - harry
      - sally
      - joe
    paint-developers:
      - frank
      - sally
      - jane
    everyone:
      - '@calc-developers'
      - '@paint-developers'
User needs read/write access to / path of master branch in order to be able to download/upload files from git-as-svn internal LFS server.
 | 
9.3. Advanced Access Control Features
git-as-svn also supports some "magic" tokens for helping you to make rule assignments based on the user’s authentication class.
One such token is the $authenticated token.
Use this token where you would otherwise specify a username or group name in your authorization rules to declare the permissions granted to any user who has authenticated with any username at all.
You may also use $authenticated:Local/$authenticated:GitLab/$authenticated:Gitea/$authenticated:LDAP to refer to users authenticated against specific user database.
Similarly employed is the $anonymous token, except that it matches everyone who has not authenticated with a username.
repositoryMapping: !listMapping
  repositories:
    calendar:
      access:
        /:
          '$anonymous': r
          '$authenticated': rw
10. SVN Properties
git-as-svn has limited support for Subversion Properties.
| Proper operation of this feature requires Subversion client version 1.8 or later. | 
10.1. .gitignore file
git-as-svn transparently builds svn:ignore and svn:global-ignores Subversion Properties based on .gitignore files in Git repository.
For example:
*.class */build
Results in:
$ svn pl -v <repo>
Properties on '<repo>':
    svn:global-ignores
        *.class
$ svn pl -v <repo>/foo/
Properties on '<repo>/foo/':
    svn:ignore
        build
Negated path masks (!/path/) are not supported
 | 
10.2. .gitattributes file
git-as-svn transparently builds svn:eol-style, svn:mime-type and svn:auto-props Subversion Properties based on .gitattributes files in Git repository.
For example, create .gitattributes with the following contents:
*.txt text *.xml eol=lf *.bin binary
It will be automatically exposed to Subversion via svn:auto-props property:
$ svn pl -v <repo>
Properties on '<repo>':
  svn:auto-props
    *.txt = svn:eol-style=native
    *.xml = svn:eol-style=LF
    *.bin = svn:mime-type=application/octet-stream
Additionally, individual files that match .gitattributes entries, will get corresponding Subversion properties:
$ svn pl -v <repo>/native.txt
Properties on '<repo>/native.txt':
  svn:eol-style
    native
$ svn pl -v '<repo>/unix.xml':
Properties on '<repo>/unix.xml':
  svn:eol-style
    LF
$ svn pl -v '<repo>/binary.bin':
  svn:mime-type
    application/octet-stream
10.3. .tgitconfig file
Please, refer to TortoiseGit documentation on exact syntax of .tgitconfig file
Example:
[bugtraq]
    logregex = #(\\d+)
    url = https://github.com/git-as-svn/git-as-svn/issues/%BUGID%
    warnifnoissue = false
git-as-svn converts this to:
$ svn pl -v <repo>
Properties on '<repo>':
  bugtraq:logregex
    #(\\d+)
  bugtraq:url
    https://github.com/git-as-svn/git-as-svn/issues/%BUGID%
  bugtraq:warnifnoissue
    false
10.4. Commit failed: Invalid svn properties on file
If you get "Commit failed: Invalid svn properties on file" error when trying to commit a new file via git-as-svn, you need:
- 
Decide if this file is text or binary
- 
If text, go to
.gitattributesand addtextentry for your file extension. You may optionally want to specify EOL behavior. - 
If binary, go to
.gitattributesand add-textentry for your file extension 
 - 
 - 
Commit
.gitattributes - 
svn upin working copy root - 
svn revert <new file>(this will undo effect ofsvn add) - 
svn add <new file>again - 
Finally,
svn commit 
11. Empty directories
| This feature is not fully SVN-compatible. Please, make sure you understand possible consequences of enabling it. | 
Unlike Subversion, Git doesn’t directly support empty directories. However, this is an often-requested feature, so git-as-svn provides somewhat working emulation.
It has three modes of operation:
emptyDirs: Disabled- 
Default, the safest mode. git-as-svn rejects commits that try to create empty directories.
 emptyDirs: AutoCreateKeepFile- 
When user tries to commit empty directory, git-as-svn automatically creates
.keepfile in it.Note that if user makes a commit from a working copy,
.keepfile will not appear in it. This limitation originates from Subversion commit protocol. It expects that items will be put in the repository exactly in the same state as user sent them. So, if user commits empty directory, they expect the directory to be also empty on the server.The only safe way to commit empty directories is when user doesn’t have a working copy but instead operates on remote repository URL directly.
Safe$ svn mkdir svn://server/repo/dirAlso safe$ svn co svn://server/repo $ cd repo $ svn svn://server/repo/dir $ svn up # User gets directory and .keep file from serverUnsafe$ svn co svn://server/repo $ cd repo $ mkdir dir $ svn add dir $ svn commit # Now user doesn't have .keep file but server does $ svn up # User still doesn't have .keep fileThis discrepancy only affects the user who commits empty directory. Other users will happily receive both directory and
.keepfile when they dosvn up. emptyDirs: AutoCreateAndDeleteKeepFile- 
Same as
emptyDirs: AutoCreateKeepFileplus git-as-svn will automatically delete.keepfile whenever directory becomes non-empty.This mode allows even more scenarios that would lead to discrepancy between client and server understanding of repository structure.
 
So. You was warned. If your working copy becomes corrupt up to the point when you have to re-checkout it from the server due to messing with empty directories, you chose this.
12. Alternatives
The problem of combining Git and Subversion work style with a version control system can be solved in different ways.
12.1. GitHub Subversion support
This is probably the closest analogue.
The main problem of this implementation is inseparable from GitHub. Also, all of a sudden, this implementation does not support Git LFS.
In the case of GitHub it is also not clear where the stored mapping between Subversion-revision and Git-commit. This can be a problem when restoring repositories after emergency situations.
12.2. SubGit
Website: https://subgit.com
Quite an interesting implementation which supports master-master replication with Git and Subversion repositories. Thereby providing synchronization of repositories is not clear.
12.3. Subversion repository and git-svn
This method allows you to use Git with Subversion repository, but using a shared Git repository between multiple developers very difficult.
At the same time, the developer has to use a specific command-line tool for working with the repository.
13. SVN+SSH
13.1. Rationale
The SVN protocol is totally unencrypted, and due to the way git-as-svn has to proxy authentication through to git servers, almost all authentication happens in plaintext.
Clearly this is undesirable, not only is potentially private code exposed over the svn protocol, but so are passwords and usernames.
Traditionally SVN has two ways of preventing this:
- 
Use HTTPS
 - 
Use svn+ssh
 
The HTTP protocol is substantially different from the SVN protocol and is currently unimplemented in git-as-svn
Thus leaving the svn+ssh mechanism.
13.2. How does SVN+SSH work?
Normally when a client calls svn <command> svn://host/path, for an appropriate <command>, the subversion client will open a connection to the host server on port 3690. After an initial handshake as per the SVN protocol the server will ask the client to authenticate.
If possible the client will attempt to perform its actions anonymously, and if necessary the server will then ask for reauthentication.
If a client calls svn <command> svn+ssh//username@host/path, the subversion client will internally ask ssh to open connection using something equivalent to: ssh username@host "svnserve -t".
If ssh succesfully connects, the SSH will run svnserve -t on the host, which will then proceed with the SVN protocol handshake over its stdin
and stdout, and the client will use the stdin and stdout of the ssh connection.
When the server asks the client to authenticate, the server will offer the EXTERNAL authentication mechanism.
(Possibly with the ANONYMOUS
mechanism.)
If the client uses EXTERNAL mechanism, the server sets the user to be either the currently logged in user from the ssh, (or an optional tunnel-user parameter.)
Securing the svnserve -t call and protecting against semi-malicious uses of the --tunnel-user option or even the calling of other commands in cases of multiple users for a single repository requires some thought.
Often this is protected through the use of a suitable command=""
parameter in the authorized_keys file, coupled with other options.
e.g.
command="/usr/bin/svnserve -t --tunnel-user username",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa …
Of note, in this example the command provided by the client is ignored but it could be checked and managed as appropriately.
In fact these techniques are used in the authorized_keys files of most git
servers.
This provides a simple first way to handle svn+ssh, if we set
command="nc localhost 3690" then whenever we connect by ssh we will be passed directly to the git-as-svn server.
The downside being that the client will be asked to authenticate.
13.3. A better git-as-svn-svnserve
Handling the EXTERNAL authentication mechanism properly without creating a new port to listen on and a new adjusted SVN protocol is not possible.
However there is another way:
We can stand in the middle of the SVN protocol stream, catch the authentication handshake, proxy it before stepping back and letting the client and server talk to each other.
We can create a new authentication mechanism on the git-as-svn server that requires a secret token known only by us, to allow us to pass in the external username (or other identifier) as the user authentication using sshKeyUsers to proxy the UserDB
We can then use git-as-svn-svnserve-tunnel SECRET EXTERNAL_USERNAME as a replacement for svnserve -t or nc localhost 3690 in the
command="" option in authorized_keys.
Of course we need to keep the authorized_keys file up-to-date
13.4. GitLab & git-as-svn-svnserve
There are two ways that Gitlab manages ssh access.
- 
Updating the git user’s
authorized_keysevery time a SSH key is changed. - 
The use of an SSH
AuthorizedKeysCommand 
First, let’s look at the authorized_keys case.
Gitlab will update the authorized_keys file over time.
If you set the option: gitlab_shell['auth_file'] in the gitlab.rb
configuration file to a different location, you can catch changes to this file, and change the command="" option to something that will check whether we are trying to perform svn and handle it if so.
The suggested config, at least for Gitlab docker and assuming that git-as-svn has been installed in /opt/git-as-svn is:
# gitlab_shell['auth_file'] = "/var/opt/gitlab/.ssh/authorized_keys"
gitlab_shell['auth_file'] = "/var/opt/gitlab/ssh-shadow/authorized_keys"
/etc/git-as-svn/git-as-svn.conf
!config:
# Specifies IP to listen to for svn:// connections
# Default: 0.0.0.0
#
# host: 0.0.0.0
# Specifies port number to listen to for svn:// connections
# Default: 3690
#
# port: 3690
# Subversion realm name. Subversion uses this for credentials caching
# Default: git-as-svn realm
#
# realm: git-as-svn realm
# Traffic compression level. Supported values: LZ4, Zlib, None
# Default: LZ4
#
# compressionLevel: LZ4
# If enabled, git-as-svn indexed repositories in parallel during startup
# This results in higher memory usage so may require adjustments to JVM memory options
# Default: true
#
# parallelIndexing: true
# Sets cache location
cacheConfig: !persistentCache
  path: /var/cache/git-as-svn/git-as-svn.mapdb
# Tells git-as-svn to use GitLab API for repository list
repositoryMapping: !gitlabMapping
  # Filesystem location where GitLab stores repositories
  # Note that git-as-svn requires write access
  # Default: /var/opt/gitlab/git-data/repositories/
  #
  # path: /var/opt/gitlab/git-data/repositories/
  # Uncomment following to only handle repositories with specified tags (add them to repositories via Settings -> General -> Tags in GitLab)
  #
  # repositoryTags:
  #   - git-as-svn
  # Common settings for all repositories exposed to svn://
  template:
    # renameDetection: true
    # emptyDirs: Disabled
    # format: Latest
    pusher: !pushEmbedded
      # This tells git-as-svn where GitLab commit hooks are located
      hooksPath: /opt/gitlab/embedded/service/gitaly-ruby/git-hooks
# Use GitLab user database
userDB:
  !sshKeyUsers
  userDB: !gitlabUsers {
    # Users can either authenticate using their GitLab login+password or login+access token
    # Possible values: Password, AccessToken
    # Default: Password
    #
    # authentication: Password
  }
  sshKeysToken: CHANGE_THIS_TO_SOMETHING_SECRET
shared:
  # git-as-svn builtin web server
  # It is used for GitLab system hook for repository creation/deletion notifications
  # Also, git-as-svn builtin LFS server is served through it
  - !web
    # git-as-svn base url. Leave empty for autodetect.
    # Default: empty
    #
    # baseUrl: http://localhost:8123/
    listen:
      - !http {
        # The network interface where git-as-svn web server binds to as an IP address or a hostname.  If 0.0.0.0, then bind to all interfaces.
        # Default: localhost
        #
        # host: localhost
        # Port where git-as-svn web server listens on.
        # Default: 8123
        #
        # port: 8123
        # HTTP idle timeout milliseconds. If not a single byte is sent or received over HTTP connection, git-as-svn closes it.
        # -1 = Use Jetty default
        # 0 = Disable timeout
        # Default: -1
        #
        # idleTimeout: -1
        # Tells git-as-svn to handle X-Forwarded-* headers.
        # Enable this if git-as-svn web server is running behind reverse HTTP proxy (like nginx)
        # Default: false
        #
        # forwarded: false
      }
  # Configures GitLab access for git-as-svn
  - !gitlab
    # GitLab base URL
    # Default: http://localhost/
    #
    # url: http://localhost/
    # Tells git-as-svn to use GitLab for LFS objects and file locking
    # Default: !httpLfs {}
    #
    lfsMode: !fileLfs
      # Directory where GitLab stores LFS ojects
      path: /var/opt/gitlab/gitlab-rails/shared/lfs-objects
    # GitLab access token. Note that git-as-svn requires sudo access.
    token: <GitLab Access Token>
    # Path to Gitaly socket file
    # You normally do not need to change it
    # Default: /var/opt/gitlab/gitaly/gitaly.socket
    #
    # gitalySocket: /var/opt/gitlab/gitaly/gitaly.socket
    # Gitaly secret token
    # This must match gitaly_token in /etc/gitlab/gitlab.rb
    # You normally do not need to change it
    # Default: secret token
    #
    # gitalyToken: secret token
    # Path to Gitaly binaries dir
    # You normally do not need to change it
    # Default: /opt/gitlab/embedded/bin
    #
    # gitalyBinDir: /opt/gitlab/embedded/bin
    # Value for GL_PROTOCOL hooks environment variable
    # See https://docs.gitlab.com/ee/administration/server_hooks.html#environment-variables-available-to-server-hooks
    # Possible values: HTTP, SSH, Web
    # Default: Web
    #
    # glProtocol: Web
  # Manage authorized_keys if your Gitlab instance creates this file
  - !sshKeys
    shadowSSHDirectory: /var/opt/gitlab/ssh-shadow
    realSSHDirectory: /var/opt/gitlab/.ssh
    originalAppPath: /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell
    svnservePath: /opt/git-as-svn/bin/git-as-svn-svnserve
  # If your gitlab instance is using AuthorizedKeysCommand
  # look at tools/git-as-svn-authorized-keys-command
/opt/git-as-svn/bin/git-as-svn-svnserve
#!/bin/bash
############################################################
# git-as-svn-svnserve
#
# Shadow the default gitlab/gitea shell and allow svnserve
############################################################
############################################################
# For Gitlab Docker:
############################################################
# SHADOW_SHELL_PATH="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell"
# TUNNEL_PATH="/opt/git-as-svn/bin/git-as-svn-svnserve-tunnel"
# KEY="$1"
# REAL_SHELL_PATH="$SHADOW_SHELL_PATH"
############################################################
# For Gitea Docker:
############################################################
SHADOW_SHELL_PATH="/app/gitea/gitea"
TUNNEL_PATH="/app/git-as-svn/git-as-svn-svnserve-tunnel"
KEY="$2"
SUBCOMMAND="$1"
REAL_SHELL_PATH="$SHADOW_SHELL_PATH"
if [ "$SUBCOMMAND" != "serv" ]; then
    exec -a "$REAL_SHELL_PATH" "$SHADOW_SHELL_PATH" "$@"
fi
############################################################
# Other options:
############################################################
# For either, you can move the shadowed binary to something like 
# /app/gitea/gitea.shadow and rename this script to /app/gitea/gitea.
# 
# If you follow his approach you do not need to rewrite the
# authorized_keys file, but may still need to process it.
#
# You would need to set the REAL_SHELL_PATH to point to this file
# and restore the shadowing on updates to the application
############################################################
SECRET="CHANGE_THIS_TO_SOMETHING_SECRET"
SSH_ORIGINAL_COMMANDS=($SSH_ORIGINAL_COMMAND)
if [ -n "$SSH_ORIGINAL_COMMAND" ] && [ "${SSH_ORIGINAL_COMMANDS[0]}" = "svnserve" ] ; then
    ## TUNNEL TO OUR SVNSERVER WITH MAGIC AUTHENTICATION ##
    exec "$TUNNEL_PATH" "$SECRET" "$KEY"
else
    exec -a "$REAL_SHELL_PATH" "$SHADOW_SHELL_PATH" "$@"
fi
/opt/git-as-svn/bin/git-as-svn-svnserve-tunnel
#!/bin/bash
############################################################
# git-as-svn-svnserve-tunnel
#
# Use a bit of bash hackery to implement svnserve -t by
# pushing stdin to the svn port (3690) but hijack the 
# authentication phase to pass in the ssh key id
############################################################
SECRET="$1"
KEY="$2"
FAKE_AUTH="( success ( ( EXTERNAL ) 16:Git-as-svn Realm ) )"
function failed {
    echo "$0: Unable to connect to svn service! Is it running?" 1>&2
    exit
}
trap failed err
OUR_PID=$$
function finish {
    pkill -P $OUR_PID
    exec 3>&- 3<&-
}
trap finish EXIT
exec 3<>/dev/tcp/localhost/3690
trap finish err
function read_bracket {
    BEEN_IN=false
    NBRACK=0
    while ! $BEEN_IN || [ $NBRACK != 0 ]; do
        IFS= read -n1 -r -d '' FROM
        case $FROM in
            '(')
            NBRACK=$(($NBRACK + 1))
            BEEN_IN=true
            ;;
            ')')
            NBRACK=$(($NBRACK - 1))
            ;;
            '')
            break
        esac
        echo -ne "$FROM"
    done
    IFS= read -n1 -r -d '' FROM
    echo -ne "$FROM"
    if [ "X$FROM" = "X" ]; then
        exec 0<&-
        exit
    fi
}
# Send server capabilities to client
read_bracket <&3 >&1
# Send client capabilities to server
read_bracket <&0 >&3
# Get the server authentication
AUTH_LIST_FROM_SERV=$(read_bracket <&3)
# Send the server our information
AUTHBODY=$(echo -ne "\0$SECRET\0$KEY" | base64)
AUTHBODY_LENGTH=${#AUTHBODY}
echo "( KEY-AUTHENTICATOR ( $AUTHBODY_LENGTH:$AUTHBODY ) )" >&3
if ! { command >&3; } 2>/dev/null; then
    exit
fi
# send the fake auth list to the client
echo "$FAKE_AUTH" >&1
if ! { command >&1; } 2>/dev/null; then
    exit
fi
# throwaway the client's response
read_bracket <&0 > /dev/null
# THEN PRETEND THAT THE REST OF IT WENT THAT WAY
(
    cat <&3 >&1 &
    CAT_PID=$!
    function on_exit {
        kill $CAT_PID
    }
    trap on_exit EXIT
    wait
    kill $OUR_PID
) &
cat <&0 >&3
pkill -P $OUR_PID
In the second case, if we proxy the AuthorizedKeysCommand, and just replace the command="" option as above then we have a working solution.
We have two main options, we can keep the same user, e.g. git for both subversion and git, or we could create another user.
The first option requires that we proxy the original app and replace it with our own. The second is similar but we leave the original response alone for git, just replacing it for svn
The first option is described below.
/assets/sshd_config
... # AuthorizedKeysCommand /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell-authorized-keys-check git %u %k # AuthorizedKeysCommandUser git AuthorizedKeysCommand /opt/git-as-svn/bin/git-as-svn-authorized-keys-command git %u %k AuthorizedKeysCommandUser git ...
/opt/git-as-svn/bin/git-as-svn-authorized-keys-command
#!/bin/bash
############################################################
# git-as-svn-authorized-keys_command
#
# Shadow the default ssh AuthorizedKeysComand and adjust its
# output to replace the original command with our svnserve
############################################################
############################################################
# For Gitlab Docker:
############################################################
ORIGINAL_AUTHORIZED_COMMAND="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell-authorized-keys-check"
ORIGINAL_APP_PATH="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell"
SVN_SERVE_PATH="/opt/git-as-svn/bin/git-as-svn-svnserve"
############################################################
# Gitea does not have AuthorizedKeysCommand at present
############################################################
exec -a "$ORIGINAL_AUTHORIZED_COMMAND"  "$ORIGINAL_AUTHORIZED_COMMAND" "$@" | sed -e 's|command="'"$ORIGINAL_APP_PATH"'|command="'"$SVN_SERVE_PATH"'|'
/etc/git-as-svn/git-as-svn.conf
!config:
# Specifies IP to listen to for svn:// connections
# Default: 0.0.0.0
#
# host: 0.0.0.0
# Specifies port number to listen to for svn:// connections
# Default: 3690
#
# port: 3690
# Subversion realm name. Subversion uses this for credentials caching
# Default: git-as-svn realm
#
# realm: git-as-svn realm
# Traffic compression level. Supported values: LZ4, Zlib, None
# Default: LZ4
#
# compressionLevel: LZ4
# If enabled, git-as-svn indexed repositories in parallel during startup
# This results in higher memory usage so may require adjustments to JVM memory options
# Default: true
#
# parallelIndexing: true
# Sets cache location
cacheConfig: !persistentCache
  path: /var/cache/git-as-svn/git-as-svn.mapdb
# Tells git-as-svn to use GitLab API for repository list
repositoryMapping: !gitlabMapping
  # Filesystem location where GitLab stores repositories
  # Note that git-as-svn requires write access
  # You normally do not need to change it
  # Default: /var/opt/gitlab/git-data/repositories/
  #
  # path: /var/opt/gitlab/git-data/repositories/
  # Common settings for all repositories exposed to svn://
  #
  template:
    # renameDetection: true
    # emptyDirs: Disabled
    # format: Latest
    pusher: !pushEmbedded
      # This tells git-as-svn where GitLab commit hooks are located
      hooksPath: /opt/gitlab/embedded/service/gitaly-ruby/git-hooks
# Tells git-as-svn to authenticate users against GitLab
userDB: !gitlabUsers {
  # Users can either authenticate using their GitLab login+password or login+access token
  # Possible values: Password, AccessToken
  # Default: Password
  #
  # authentication: Password
}
shared:
  # git-as-svn builtin web server
  # It is used for GitLab system hook for repository creation/deletion notifications
  # Also, git-as-svn builtin LFS server is served through it
  - !web
    # git-as-svn base url. Leave empty for autodetect.
    # Default: empty
    #
    # baseUrl: http://localhost:8123/
    listen:
      - !http {
        # The network interface where git-as-svn web server binds to as an IP address or a hostname.  If 0.0.0.0, then bind to all interfaces.
        # Default: localhost
        #
        # host: localhost
        # Port where git-as-svn web server listens on.
        # Default: 8123
        #
        # port: 8123
        # HTTP idle timeout milliseconds. If not a single byte is sent or received over HTTP connection, git-as-svn closes it.
        # -1 = Use Jetty default
        # 0 = Disable timeout
        # Default: -1
        #
        # idleTimeout: -1
        # Tells git-as-svn to handle X-Forwarded-* headers.
        # Enable this if git-as-svn web server is running behind reverse HTTP proxy (like nginx)
        # Default: false
        #
        # forwarded: false
      }
  # Configures GitLab access for git-as-svn
  - !gitlab
    # GitLab base URL
    # Default: http://localhost/
    #
    # url: http://localhost/
    # Tells git-as-svn to use GitLab for LFS objects and file locking
    # Default: !httpLfs {}
    #
    lfsMode: !fileLfs
      # Directory where GitLab stores LFS ojects
      path: /var/opt/gitlab/gitlab-rails/shared/lfs-objects
    # GitLab access token
    # Note that git-as-svn requires sudo access
    token: <GitLab Access Token>
    # Path to Gitaly socket file
    # You normally do not need to change it
    # Default: /var/opt/gitlab/gitaly/gitaly.socket
    #
    # gitalySocket: /var/opt/gitlab/gitaly/gitaly.socket
    # Gitaly secret token
    # This must match gitaly_token in /etc/gitlab/gitlab.rb
    # You normally do not need to change it
    # Default: secret token
    #
    # gitalyToken: secret token
    # Path to Gitaly binaries dir
    # You normally do not need to change it
    # Default: /opt/gitlab/embedded/bin
    #
    # gitalyBinDir: /opt/gitlab/embedded/bin
    # Value for GL_PROTOCOL hooks environment variable
    # See https://docs.gitlab.com/ee/administration/server_hooks.html#environment-variables-available-to-server-hooks
    # Possible values: HTTP, SSH, Web
    # Default: Web
    #
    # glProtocol: Web
- 
/opt/git-as-svn/bin/git-as-svn-svnserveand/opt/git-as-svn/bin/git-as-svn-svnserve-tunnelsame as above. 
13.5. Gitea
There are two ways that Gitea manages ssh access.
- 
If Gitea is deferring to an external SSHD. It will update the git user’s
authorized_keysevery time a SSH key is changed. - 
If Gitea is using its own internal SSHD. It will run the
gitea servcommand each time. - 
The use of an SSH
AuthorizedKeysCommandin Gitea v1.7.0+ 
First, let’s look at the authorized_keys case.
Gitea will update the authorized_keys file over time.
If you set the option: SSH_ROOT_PATH in the [server] of the gitea app.ini to a shadow location you can catch changes to this file, and change the command="" option to something that will check whether we are trying to perform svn and handle it if so.
The suggested config, at least for Gitea docker, and assuming that git-as-svn has been installed in /app/git-as-svn is:
...
[server]
...
SSH_ROOT_PATH=/data/git/ssh-shadow
...
/app/git-as-svn/config.yaml
!config:
# Specifies IP to listen to for svn:// connections
# Default: 0.0.0.0
#
# host: 0.0.0.0
# Specifies port number to listen to for svn:// connections
# Default: 3690
#
# port: 3690
# Subversion realm name. Subversion uses this for credentials caching
# Default: git-as-svn realm
#
# realm: git-as-svn realm
# Traffic compression level. Supported values: LZ4, Zlib, None
# Default: LZ4
#
# compressionLevel: LZ4
# If enabled, git-as-svn indexed repositories in parallel during startup
# This results in higher memory usage so may require adjustments to JVM memory options
# Default: true
#
# parallelIndexing: true
# Sets cache location
cacheConfig: !persistentCache
  path: /var/cache/git-as-svn/git-as-svn.mapdb
# Tells git-as-svn to use Gitea API for repository list
repositoryMapping: !giteaMapping
  # Filesystem location where Gitea stores repositories
  # Note that git-as-svn requires write access
  path: /data/git/repositories
  # Common settings for all repositories exposed to svn://
  #
  # template:
  #   branches:
  #     - master
  #   renameDetection: true
  #   emptyDirs: Disabled
  #   format: Latest
userDB:
  !sshKeyUsers
  # Tells git-as-svn to use Gitea API for user authentication
  userDB: !giteaUsers {}
  sshKeysToken: CHANGE_THIS_TO_SOMETHING_SECRET
shared:
  # Configures Gitea API for git-as-svn
  - !gitea
    # URL where your Gitea instance API is running
    url: http://localhost:3000/api/v1
    # Tells git-as-svn to store Git-LFS objects through Gitea LFS API
    # Note that this needs to be in sync with Gitea LFS_START_SERVER config option
    lfs: false
    # Gitea access token
    # Note that git-as-svn requires Gitea Sudo permission in order to authenticate users
    token: 90c68b84fb04e364c2ea3fc42a6a2193144bc07d
  - !sshKeys
    shadowSSHDirectory: /data/git/ssh-shadow
    realSSHDirectory: /data/git/.ssh
    originalAppPath: /app/gitea/gitea
    svnservePath: /app/gitea/git-as-svn-svnserve
  # If your gitea instance is using AuthorizedKeysCommand
  # look at tools/git-as-svn-authorized-keys-command
  # You don't need sshKeys in that case
/app/git-as-svn/bin/git-as-svn-svnserve
#!/bin/bash
############################################################
# git-as-svn-svnserve
#
# Shadow the default gitlab/gitea shell and allow svnserve
############################################################
############################################################
# For Gitlab Docker:
############################################################
# SHADOW_SHELL_PATH="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell"
# TUNNEL_PATH="/opt/git-as-svn/bin/git-as-svn-svnserve-tunnel"
# KEY="$1"
# REAL_SHELL_PATH="$SHADOW_SHELL_PATH"
############################################################
# For Gitea Docker:
############################################################
SHADOW_SHELL_PATH="/app/gitea/gitea"
TUNNEL_PATH="/app/git-as-svn/git-as-svn-svnserve-tunnel"
KEY="$2"
SUBCOMMAND="$1"
REAL_SHELL_PATH="$SHADOW_SHELL_PATH"
if [ "$SUBCOMMAND" != "serv" ]; then
    exec -a "$REAL_SHELL_PATH" "$SHADOW_SHELL_PATH" "$@"
fi
############################################################
# Other options:
############################################################
# For either, you can move the shadowed binary to something like 
# /app/gitea/gitea.shadow and rename this script to /app/gitea/gitea.
# 
# If you follow his approach you do not need to rewrite the
# authorized_keys file, but may still need to process it.
#
# You would need to set the REAL_SHELL_PATH to point to this file
# and restore the shadowing on updates to the application
############################################################
SECRET="CHANGE_THIS_TO_SOMETHING_SECRET"
SSH_ORIGINAL_COMMANDS=($SSH_ORIGINAL_COMMAND)
if [ -n "$SSH_ORIGINAL_COMMAND" ] && [ "${SSH_ORIGINAL_COMMANDS[0]}" = "svnserve" ] ; then
    ## TUNNEL TO OUR SVNSERVER WITH MAGIC AUTHENTICATION ##
    exec "$TUNNEL_PATH" "$SECRET" "$KEY"
else
    exec -a "$REAL_SHELL_PATH" "$SHADOW_SHELL_PATH" "$@"
fi
- 
/app/git-as-svn/bin/git-as-svn-svnserve-tunnelshould be the same as in the gitlab case. 
For the second case, we need to shadow the gitea binary
So we would need to move the original gitea from /app/gitea/gitea to /app/gitea/gitea.shadow
And either create /app/gitea/gitea as a symbolic link or just copy the below /app/git-as-svn/bin/git-as-svn-svnserve as it.
/app/git-as-svn/bin/git-as-svn-svnserve
#!/bin/bash
############################################################
# git-as-svn-svnserve
#
# Shadow the default gitlab/gitea shell and allow svnserve
############################################################
############################################################
# For Gitlab Docker:
############################################################
# SHADOW_SHELL_PATH="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell"
# TUNNEL_PATH="/opt/git-as-svn/bin/git-as-svn-svnserve-tunnel"
# KEY="$1"
# REAL_SHELL_PATH="$SHADOW_SHELL_PATH"
############################################################
# For Gitea Docker:
############################################################
SHADOW_SHELL_PATH="/app/gitea/gitea"
TUNNEL_PATH="/app/git-as-svn/git-as-svn-svnserve-tunnel"
KEY="$2"
SUBCOMMAND="$1"
REAL_SHELL_PATH="$SHADOW_SHELL_PATH"
if [ "$SUBCOMMAND" != "serv" ]; then
    exec -a "$REAL_SHELL_PATH" "$SHADOW_SHELL_PATH" "$@"
fi
############################################################
# Other options:
############################################################
# For either, you can move the shadowed binary to something like 
# /app/gitea/gitea.shadow and rename this script to /app/gitea/gitea.
# 
# If you follow his approach you do not need to rewrite the
# authorized_keys file, but may still need to process it.
#
# You would need to set the REAL_SHELL_PATH to point to this file
# and restore the shadowing on updates to the application
############################################################
SECRET="CHANGE_THIS_TO_SOMETHING_SECRET"
SSH_ORIGINAL_COMMANDS=($SSH_ORIGINAL_COMMAND)
if [ -n "$SSH_ORIGINAL_COMMAND" ] && [ "${SSH_ORIGINAL_COMMANDS[0]}" = "svnserve" ] ; then
    ## TUNNEL TO OUR SVNSERVER WITH MAGIC AUTHENTICATION ##
    exec "$TUNNEL_PATH" "$SECRET" "$KEY"
else
    exec -a "$REAL_SHELL_PATH" "$SHADOW_SHELL_PATH" "$@"
fi
/app/git-as-svn/bin/git-as-svn-svnserve-tunnel should be the same as in the gitlab case.
Managing the AuthorizedKeysCommand is similar to that in the Gitlab case.
14. Internal implementation details
14.1. Where Subversion data is stored?
To represent Subversion repository need to store information about how Subversion-revision number corresponds to which Git-commit.
We can’t compute this information every time on startup, because first git push --force change revision order.
This information stored persistent in git reference refs/git-as-svn/*.
In particular because it does not require a separate backup Subversion data.
Because of this separate backup Subversion data is not necessary.
Also part of the data necessary for the Subversion repository, is very expensive to get based on Git repository.
For example:
- 
the revision number with the previous change file;
 - 
information about where the file was copied;
 - 
MD5 file hash.
 
In order not to find out their every startup, the data is cached in files. The loss of the cache is not critical for the operation and backup does not make sense.
File locking information is currently stored in the cache file.
14.3. Для чего нужно хранилище
Хранилище данных о ревизиях нужно для:
- 
Сохранения информации об списке измененных файлов (в svn очень часто нужно выяснять, в какой из предыдущих ревизий поменялся файл);
 - 
Сохранения информации о том, к какой ревизии/ветке относится коммит;
 - 
Сохранения идентификатора svn-репозитория.
 
Так же побочным эффектом служит формирование дерева с svn layout-ом для более простой работы svn update/switch/log/diff и т.п.
14.3.1. Формат хранения информации о ревизиях
Для хранения информации о ревизиях используется ссылка refs/git-as-svn/v1.
Эта ссылка содержит в себе набор коммитов, выстроенных в цеполчку по первому родителю. Порядковый номер коммита от начала соответвует номеру ревизии в SVN.
Содержимое коммита
Данные самого коммита:
- 
Первый родитель всегда ссылается на предыдущий коммит;
 - 
Комментарий коммита берется из оригинального коммита. Именно этот комментарий выводится в svn log;
 - 
Автор коммита берется из оригинального коммита;
 
Дерево коммита:
- 
svn (tree) - дерево, которое соответсвует svn layout-у данного коммита;
 - 
commit.ref (commit) - ссылка на оригинальный коммит (для коммитов, которые связаны с удалением/созданием веток может отсутствовать);
 
Revision 0
Ревизия 0 несколько отличается от остальных коммитов.
В отличие от остальных коммитов:
- 
Она всегда содержит путое дерево svn;
 - 
В ней лежит файл
uuidс идентификатором репозитория. 
14.4. How does commit work?
One of the most important parts of the system — to save the changes.
In general, the following algorithm:
- 
At the moment the command
svn commitclient sends to the server of your changes. The server remembers them. At this point comes the first check the relevance of customer data. - 
The server takes the branch HEAD and begins to create new commit on the basis of client received delta. At this moment there is yet another check of the relevance of customer data.
 - 
Validating svn properties for changed data.
 - 
The server tries to push the new commit in the current branch of the same repository via console Git client. Next, the result of a push:
- 
if commits pushed successfully — loading the latest changes from git commits and rejoice;
 - 
if push is not fast forward — load the latest changes from git commits and go to step 2;
 - 
if push declined by hooks — inform the client;
 - 
on another error — inform the client;
 
 - 
 
Thus, through the use console Git client for push, we avoid the race condition pouring directly change Git repository, and get the native hooks as a nice bonus.
15. Changelog
4.3.0
- 
Fix sparse checkout when directory with empty depth has nested children with non-empty depth
 - 
Upgrade gitlab4j-api to 6.0.0
 
4.2.1
- 
Fix infinite loop in
get-location-segmentscommand 
4.2.0
- 
Fix GitLab 17.3+ hooks compatibility
 
4.1.0
- 
Fix GitLab access cache expiration
 
4.0.0
- 
Drop support for GitLab older than 10.2
 - 
Migrate from java-gitlab-api to gitlab4j-api
 - 
Fix compatibility with GitLab 16.9+
 
3.0.0
- 
Use virtual threads by default
 - 
Require Java 21+
 
2.9.8
- 
Fix chaos in changed files in
svn log 
2.9.7
- 
Remove legacy code that could be a source of slowdown when working with LFS files
 
2.9.6
- 
More memory optimizations
 
2.9.5
- 
Add configurable cache policy for in-memory git entries
 
2.9.4
- 
Fix memory regressions
 
2.9.3
- 
More memory optimizations
 
2.9.2
- 
Fix
svn upprotocol error - 
More memory optimizations
 
2.9.1
- 
Unsorted memory optimizations
 
2.8.1
- 
Do not authenticate users by access token if they enter wrong username
 - 
Update snakeyaml to 2.2
 
2.8.0
- 
Update dependencies
 - 
Experimental string interning feature
 
2.7.1
- 
Update dependencies
 - 
Add option to authenticate using GitLab access tokens
 
2.6.0
- 
Update dependencies
 - 
Fix git-as-svn not caching visibility of non-public GitLab projects
 
2.5.0
- 
Update dependencies
 - 
Fix compatibility with GitLab >= 15.2
 
2.3.0
- 
Update dependencies
 - 
Reduce memory consumption for repositories with big
.gitignoreor.gitattributes. #306 
2.2.0
- 
Add
glProtocoloption to configure GitLabGL_PROTOCOLserver hooks environment variable - 
Update dependencies
 
2.1.1
- 
Fix startup failure when
format: Latestis specified in config 
2.0.0
- 
git-as-svn migrated from Java to Kotlin
 - 
Java 8 is no longer supported. Minimal is Java 11 now.
 - 
Update dependencies
 - 
git-as-svn no longer implicitly sets
svn:eol-style=nativefor files by default - 
Starting with this release, git-as-svn introduces versioning for backward-incompatible changes. You can control whether you opt-in for new features that require re-checkout of Subversion working copies via
formatrepository parameter. By default, git-as-svn uses the latest version and thus requires re-checkout if repository was initially created with older format. - 
Add support for non-ASCII logins and passwords
 - 
Deploy Debian packages to Cloudsmith. #370
 
Repository data exported to SVN has changed.
Users will need to perform re-checkout of their working copies after git-as-svn upgrade unless you specify format: V4 in git-as-svn.conf.
 | 
1.30.1
- 
Pass
GITALY_HOOKS_PAYLOADenvironment variable to GitLab hooks to fix compatibility with GitLab 13.7+. #367 
1.29.0
- 
Update dependencies
 
1.28.1
- 
Fix regression bug introduced in 1.28.0 that made git-as-svn to ignore some configuration options
 
1.26.0
- 
Release remote LFS locks on commit unless keep-locks option is enabled
 - 
Block commit to locked file even if user claims he doesn’t have any local version of that file
 - 
Improve error message when commit is aborted due to lock
 - 
Update dependencies
 
1.25.2
- 
Fix file descriptor leak when
useHooksDiris enabled - 
Update dependencies
 
1.25.0
- 
Add
useHooksDiroption topusher: !pushEmbeddedthat runshooks/<hook_name>.d/\*executable files in addition to standardhooks/<hook_name>. Note that this feature is an extension to standard Git behavior and is subject to change in any later git-as-svn releases. 
1.24.3
- 
Fix
svn unlocknot actually unlocking anything if lock token was not provided 
1.24.2
- 
Upgrade httpclient to 4.5.12. #335
 - 
Fix
get-lockscmd not properly filtering paths when using HTTP LFS server 
1.24.0
- 
Fix a bug that caused Git-LFS locks in GitLab to be created on behalf of administator user instead of the user who locks file through git-as-svn
 
1.23.1
- 
Fix "Malformed network data" error for
svn blame 
1.23.0
- 
Drop support for nonstandard
eol=crin.gitattributeswith no replacement - 
Drop support for nonstandard
eol=nativein.gitattributes. Just addtextattribute to indicate that file has native EOLs. - 
Use JGit to parse
.gitattributesfiles. 
| Repository data exported to SVN has changed. Users will need to perform re-checkout of their working copies after git-as-svn upgrade. | 
1.22.0
- 
Systemd unit now correctly waits for git-as-svn to shut down. #275
 - 
Update dependencies
 - 
/usr/bin/git-as-svnno longer implicitly adds-Xmx512mJVM argument - 
Several file descriptor leaks fixed
 - 
git-as-svn no longer overrides
.gitattributessettings with text/binary auto-detection - 
svn:mime-type=application/octet-streamproperty is now added to files that have-textin.gitattributes. #317 
| Repository data exported to SVN has changed. Users will need to perform re-checkout of their working copies after git-as-svn upgrade. | 
1.21.8
- 
Write empty LFS files in a compatible with Git-LFS way
 - 
Update dependencies
 
1.21.7
- 
Fix Git LFS lock paths not handled properly, making it possible to lock same file multiple times
 - 
Send human-readable error message when locking fails due to already existing lock
 
1.21.6
- 
Add cleanup of bogus locks created with git-as-svn versions prior to 1.21.5
 
1.21.5
- 
Multiple fixes to remote LFS locking
 
1.21.4
- 
Fix commit of files larger than 8MB
 
1.21.3
- 
Fixes to
lfsMode: !fileLfs. 
1.21.2
- 
Fix bogus slashes in branch names for GitLab mapping
 
1.21.1
- 
Reduce log spam (LDAP and client disconnects)
 - 
Log client version on connect
 
1.21.0
- 
Do not write to
/tmpwhen streaming files from remote LFS server to SVN clients. #288 - 
Experimental
lfsMode: !fileLfsLFS mode for GitLab - 
lfs: falsereplaced withlfsMode: nullin!gitlabsection 
1.20.5
- 
Log all exceptions when talking to SVN clients
 - 
Fixed double buffering of client I/O
 - 
Fix downloading of large files from remote LFS server. Broken in 1.20.4
 
1.20.4
- 
Fix multiple file descriptor leaks
 
1.20.3
- 
Fix
svn blamefailing with "Malformed network data" error 
1.20.1
- 
Fix
git lfs unlock <path>not finding LFS lock 
1.20.0
- 
Fix inability to unlock files through Git-LFS
 - 
Fix lock paths having leading slash when listing locks via Git-LFS
 - 
Now path-based authorization supports branch-specific access
 
1.19.3
- 
Add
$authenticated:Local/$authenticated:GitLab/$authenticated:Gitea/$authenticated:LDAPto refer to users authenticated against specific user database in path-based ACL - 
Fix git-lfs failing with "Not Acceptable" error when uploading files
 
1.19.2
- 
Improve GitLab configuration defaults
 
1.19.0
- 
Add support for LZ4 compression. Replace
compressionEnabled=true/falseoption withcompressionLevel=LZ4/Zlib/None. #163 - 
Fix severe performance loss on commit. Broken in 1.8.0
 
1.18.0
- 
Add option to expose user-defined branches for GitLab. See GitLab configuration documentation. #188
 - 
repositoryTagsis no longer supported for!gitlabMapping 
1.17.0
- 
Drop ability to configure custom hook names in
!pushEmbeddedbecause Git doesn’t have such feature. Instead, addhooksPathoption that works as an override tocore.hooksPathGit configuration option. - 
Fix uploads of already existing files to remote LFS server.
 
1.16.0
- 
Update Jetty to 9.4.19
 - 
Update Log4j to 2.12.0
 - 
Update git-lfs-java to 0.13.3
 - 
Add support for
core.hooksPathGit configuration variable. #267 
1.15.0
- 
Now groups can be defined to contain other groups for path-based authorization
 - 
JGit updated to 5.4.0
 - 
UnboundID LDAP SDK updated to 4.0.11
 - 
google-oauth-client updated to 1.30.1
 - 
Remove
hookUrlfrom!gitlabsection, it is now automatically determined frombaseUrlin!websection. 
1.14.0
- 
-tand-Tcommand-line switches. See Command-line parameters documentation - 
-s/--show-configcommand-line switches removed. Use-Tinstead. 
1.13.0
- 
Changed LDAP bind configuration. See LDAP documentation.
 - 
Organize logs into categories and add logging documentation.
 
1.12.0
- 
Experimental support for LFS locking API Now git-as-svn forwards locking requests to LFS server. git-as-svn internal LFS server now supports LFS locks. Locks are now scoped to whole repositories instead of being per-branch. All existing svn locks will expire after upgrade.
 - 
URL scheme has changed, now it is
svn://<host>/<repo>/<branch>. Usesvn relocateto fix existing SVN working copies. - 
It is no longer valid to map a single repository under multiple paths. Use
branchestag to expose multiple branches of a single repository to SVN. 
1.11.1
- 
!giteaSSHKeysis no longer supported - 
Fix date formatting to be compatible with git-lfs. Was broken in 1.11.0
 
1.11.0
- 
Add support for Gitea LFS server. Gitea >= 1.7.2 is required now.
 - 
!gitlabLfs {}was replaced withlfs: trueparameter in!gitlabsection 
1.10.0
- 
File locking code cleanup. All existing svn locks will expire after upgrade.
 - 
Implement
get-file-revscommand. This is expected to speed upsvn blameseverely. #231 - 
Prospective blame support added
 
1.9.0
- 
Major code cleanup
 - 
repository: !gitchanged to justrepository:in git-as-svn.conf - 
access: !aclchanged to justacl:in git-as-svn.conf - 
svn statis now compatible with native svn for nonexistent paths 
1.8.1
- 
Update dependencies: jgit-5.3.0, svnkit-1.10.0, jetty-9.4.15, java-gitea-api-1.7.4, unboundid-ldapsdk-4.0.10 and others
 
1.7.6.1
- 
Fix broken URL construction in git-lfs-authenticate
 
1.7.6
- 
git-lfs-authenticate no longer silently falls back to anonymous mode if it failed to obtain user token
 - 
git-lfs-authenticate now properly handles absolute repository paths
 
1.7.5
- 
Ensure hook stdout is closed when using embedded pusher
 
1.7.4
- 
Revert #215, causes tens of thousands of CLOSE_WAIT connections in Jetty
 - 
Update Jetty to 9.4.14
 
1.7.2
- 
Reduce lock contention during commit
 - 
Log how long commit hooks take
 - 
Do not log exception stacktraces on client-side issues during commit
 
1.7.1
- 
Revert offloading file → changed revisions cache to MapDB (#207) as an attempt to fix (or, at least, reduce) issues with non-heap memory leaks
 
1.7.0
- 
Dramatically improve memory usage by offloading file → changed revisions cache to MapDB
 - 
--unsafe option no longer exists, all "unsafe" functionality was removed
 - 
git-lfs-authenticate.cfg format has changed. Now, git-lfs-authenticate talks to git-as-svn via http and uses shared token.
 - 
!api no longer exists in git-as-svn.conf
 - 
!socket no longer exists in git-as-svn.conf
 - 
LFS storage is no longer silently created, instead LfsFilter will error out when encounters LFS pointer without configured LFS storage
 - 
JGit updated to 5.1.2
 - 
GitLab API updated to 4.1.0
 
1.5.0
- 
Add tag-based repository filtering for GitLab integration
 
1.4.0
- 
Update JGit to 5.0.1.201806211838-r
 - 
Update SVNKit to 1.9.3
 - 
Reduce memory usage
 - 
Improve indexing performance
 
1.2.0
- 
x10 speedup of LDAP authentication
 - 
Drop dependency on GSon in favor of Jackson2
 - 
Update unboundid-ldapsdk to 4.0.3
 - 
Fix post-receive hook failing on GitLab 10 #160
 
1.1.8
- 
Fix git-as-svn unable to find prefix-mapped repositories (broken in 1.1.2)
 - 
Fix PLAIN authentication with native SVN client (broken in 1.1.4)
 
1.1.6
- 
Update various third-party libraries
 - 
Upgrade to Gradle 4.4
 - 
Fix GitLab repositories not becoming ready on git-as-svn startup #151
 - 
Improve logging on git-as-svn startup
 
1.1.5
- 
Fix submodules support (was broken in 1.1.3)
 - 
Invalidate caches properly if renameDetection setting was changed
 
1.1.2
- 
Add reference to original commit as parent for prevent commit removing by
git gc#118. - 
Fix repository mapping error #122.
 - 
Fix non ThreadSafe Kryo usage #121.
 - 
Add support for combine multiple authenticators.
 - 
Add support for authenticator cache.
 - 
Fix tree conflict on Windows after renaming file with same name in another case #123.
 - 
Use commit author instead of commiter identity in svn log.
 - 
Don’t allow almost expired tokens for LFS pointer requests.
 
1.1.0
- 
Use by default svn:eol-style = native for text files (fix #106).
 - 
Upload .deb package to debian repository.
 
1.0.17-alpha
- 
Add PDF, EPUB manual.
 - 
Add support for anonymous authentication for public repositories.
 
1.0.15-alpha
- 
Add support for GitLab 8.2 LFS storage layout #109.
 
1.0.14-alpha
- 
Add debian packaging.
 - 
Add configurable file logging.
 
1.0.13-alpha
- 
Embedded git-lfs server
 - 
Git-lfs batch API support.
 - 
Add support for LDAP users without email.
 - 
Add support for X-Forwarded-* headers.
 - 
Add HTTP-requests logging.
 - 
Change .gitignore mapping: ignored folder now mask all content as ignored.
 - 
Fix git-lfs file commit.
 - 
Fix quote parsing for .tgitconfig file.
 
1.0.12-alpha
- 
Initial git-lfs support (embedded git-lfs server).
 - 
Initial GitLab integration.
 - 
Import project list on startup.
 - 
Authentication.
 - 
Add support for embedded git push with hooks;
 - 
Git-as-svn change information moved outside git repostitory #60.
 - 
Configuration format changed.
 - 
Fixed some wildcard issues.
 
1.0.11-alpha
- 
Fix URL in authentication result on default port (Jenkins error:
E21005: Impossibly long repository root from server). - 
Fix bind on already used port with flag SO_REUSEADDR (thanks for @fcharlie, #70).
 - 
Add support for custom certificate for ldaps authentication.
 
1.0.10-alpha
- 
Fix get file size performance issue (
svn ls). - 
Fix update IMMEDIATES to INFINITY bug.
 - 
Fix NPE on absent email in LDAP.
 
1.0.9-alpha
- 
Fix svn update after aborted update/checkout.
 - 
Fix out-of-memory when update/checkout big directory.
 - 
Show version number on startup.
 
1.0.8-alpha
- 
Support commands:
svn lock/svn unlock. - 
Multiple repositories support.
 
1.0.7-alpha
- 
More simple demonstration run
 - 
svnsyncsupport 
1.0.6-alpha
- 
Add autodetection binary files (now file has
svn:mime-type = application/octet-streamif it set as binary in .gitattributes or detected as binary). - 
Expose committer email to svn.
 - 
Fix getSize() for submodules.
 - 
Fix temporary file lifetime.
 
1.0.5-alpha
- 
Add persistent cache support.
 - 
Dumb locks support.
 - 
Fix copy-from permission issue.
 
1.0.4-alpha
- 
Improve error message when commit is rejected due to wrong properties.
 
1.0.3-alpha
- 
Fix spaces in url.
 - 
Add support get-locations.
 - 
Add mapping binary to
svn:mime-type = svn:mime-type 
1.0.2-alpha
- 
Fix some critical bugs.
 
1.0.1-alpha
- 
Add support for more subversion commands
 - 
Fix some bugs.
 
1.0.0-alpha
- 
First release.