Overview

Git allows you to interact not only with other Git repositories, but also with SVN repositories. This means that you can use SmartGit like an SVN client:

In addition to the SVN functionality, you can use all (local) Git features like local commits and branching. SmartGit performs all SVN operations transparently, so you will notice only a few points in the program where you need to understand which server VCS you are using.

Compatibility and Incompatibility Modes

SmartGit's SVN integration is available in two modes:

Ignores (Normal Mode Only)

SmartGit maps svn:ignore properties to .gitignore files. Unlike the git svn create-ignore command SmartGit puts .gitignore files under version control. If you modify a .gitignore file and pushes the change, the corresponding svn:ignore property is changed.

The .gitignore syntax is significantly more powerful than the svn:ignore syntax. Hence, svn:ignore can be mapped losslessly to .gitignore, however a .gitignore file may contain a pattern that can't be mapped backed to svn:ignore. In that case the pattern is not translated.

Adding or removing a recursive pattern in .gitignore corresponds to setting or unsetting that pattern on every existing directory in the SVN repository. Conversely, when an SVN revision is fetched (back) into the Git repository, a recursive pattern will be translated to a set of non-recursive patterns, one pattern for each directory.

Let's assume we have the following directories in the SVN repository:

A {
  B {}
  C {}
}

And we add .gitignore with only one line:

somefile

and push. This will set the svn:ignore-property to somefile for all directories: A, B, C. After fetching such a revision we have the following .gitignore contents (ordering of lines is unimportant):

A/somefile
A/B/somefile
A/C/somefile

Git doesn't support patterns that contain spaces. Hence, SmartGit replaces all spaces in the svn:ignore value with [!!-~] during the creation of .gitignore. Conversely, all newly added patterns containing [!!-~] are converted to svn:ignore with spaces at the corresponding places.

EOLs (Normal Mode Only)

When using git-svn on Windows, different EOLs on different systems may cause trouble. For instance, if the SVN repository contains a file with svn:eol-style set to CRLF, its content is stored with CRLF line endings. Moreover, git-svn puts the file contents directly into Git blobs without modification. Now, if you have the core.autocrlf Git option set to true, it may be impossible to get a clean working tree, and hence git svn dcommit won't work. This happens because while checking whether the working tree is clean, Git converts working tree file EOLs to LF and compares with the blob contents (which has CRLF). On the other hand, setting core.autocrlf to false causes problems with files that contain LF EOLs.

Instead of setting a global option, SmartGit carefully sets the EOL for every file in the SVN repository using its svn:eol-style and svn:mime-type values. It uses the versioned .gitattributes file for this purpose. Its settings have higher priority than the core.autocrlf-option, so with SmartGit it doesn't matter what the core.autocrlf value is.

The .git/info/attributes file has higher priority than the versioned .gitattributes files, so it is strongly recommended to delete the former or leave it empty. Otherwise, this may confuse Git or SmartGit.

By default, a newly added text file (or more precisely, a file that Git thinks is a text file) which is pushed has svn:eol-style set to native and no svn:mime-type property set. A newly added binary file has no properties at all.

One can control individual file properties using the svneol Git attribute. The syntax is svneol=<svn:eol-style value>#<svn:mime-type value>, so for example

*.c svneol=LF#unset

means all *.c files will have svn:eol-style=LF and no svn:mime-type set after pushing. Recursive attributes are translated like recursive ignores: Their changes result in changes of properties of all files in the SVN repository.

Externals (Normal Mode Only)

SmartGit maps svn:externals properties to its own kind of submodules, that have the same interface as Git submodules.

Only externals pointing to directories are supported, not externals pointing to individual files ('file externals').

SVN submodules are defined in the file .gitsvnextmodules, which has the following format:

[submodule "path/to/submodule"]
  path = path/to/submodule
  owner = /
  url = https://server/path
  revision = 1234
  branch = trunk
  fetch = trunk:refs/remotes/svn/trunk
  branches = branches/*:refs/remotes/svn/*
  tags = tags/*:refs/remote-tags/svn/*
  remote = svn
  type = dir

Changes in .gitsvnextmodules are translated to the SVN repository as changes in svn:externals and vice versa.

There are two types of SVN submodules between which you can choose during submodule initialization:

SmartGit shows the repository status in the Directories view. If the submodule's current state does not exactly correspond to the state defined by .gitsvnextmodules (same URL, revisions, ...), it will show ups as modified. In this case, can use Local|Stage to update the .gitsvnextmodules configuration to the current SVN submodule state or you can use Remote|Submodule|Reset to put the submodule back into the state as it is registered in .gitsvnextmodules.

Symlinks and Executable Files

Symlink processing and executable-bit processing work in the same way as in git-svn. SVN uses the svn:special property to mark a file as being a symlink. Then its content should look like this:

link path/to/target

Such files are converted to Git symlinks. In a similar way, files with svn:executable are converted to Git executable files and vice versa.

Other SVN properties (Normal Mode Only)

SmartGit maps all other properties which do not have a special meaning in the Git world to Git attributes.

Depending on the size of the property value, SmartGit may store the entire property definition (name and value) just as an attribute in .gitattributes or it may decide to store the property value in a separate file. In the latter case, .gitattributes will contain an attribute attr which is just set, i.e. has no value, denoting the presence of the property for the corresponding file. The value will be stored in .gitsvnattributes/path/to/entry/attr instead, where path/to/entry is the same path as in .gitattributes, i.e. the working tree path of the file or directory to which the property belongs. The mapping between SVN property name and Git attribute name depends on the property name:

Adding a property

If the property value is a small, one-line text property, you may add it directly to .gitattributes.

To add custom property foo with value bar to file file.txt, insert following line into .gitattributes (or add the attribute to an already existing line for /file.txt):

					/file.txt svnc_foo=bar
				

If the property value is large, consists of multiple lines or is even of binary content, you have to add the control entry to .gitattributes and create the file .gitsvnattributes/path/to/entry/attr containing the property's value.

To add a binary property foo with some binary value to file file.txt, insert following line into .gitattributes (or add the attribute to an already existing line for /file.txt):

					/file.txt svnc_foo
				

and add file .gitattributes/file.txt/svnc_foo with the desired binary property content.

Modifying a property

Depending on the size of the property value, the value will either be stored directly in .gitattributes, or in .gitsvnattributes, as explained before. To modify the value, locate either of the two places where it is stored and modify the value there. After having committed this change and pushed to SVN, the modified property value will show up in the SVN repository.

To change a small, one-line property value foo for file.txt which is stored in .gitattributes to a larger, multi-line or binary property value, replace the Git attribute value by the Git attribute name itself (i.e. mark it as set):

					/file.txt svnc_foo
				

and add file .gitattributes/file.txt/svnc_foo with the desired property content.

Removing a property

Depending on the size of the property value, the value will either be stored directly in .gitattributes, or in .gitsvnattributes, as explained before. To remove the property, remove the corresponding attribute from .gitattributes and remove the corresponding file from .gitsvnattributes, if it's present there. After having committed this change and pushed to SVN, the property deletion will show up in the SVN repository.

Tags

Unlike git-svn, SmartGit creates Git tags for SVN tags. If an SVN tag was created by a simple directory copying, SmartGit creates a tag that points to the copy-source; otherwise SmartGit creates a tag that points to the corresponding commit of refs/remote-tags/svn/<tagname>. Git tags can be sent back to the SVN repository (as SVN tags) by right-clicking the tag in the Branches view and invoking Push.

Git tags that are actually objects on their own (not just simple refs) are not supported.

History Processing

Branch Replacements

In compatibility mode, SmartGit processes the SVN history like git-svn does, with the difference that SmartGit doesn't support the svk:merge property. In the case where one SVN branch was replaced, SmartGit and git-svn create a merge-commit.

In normal mode, SmartGit uses its own way of history processing: In case of branch replacements no merge commit is created; instead a Git reference refs/svn-attic/svn/<branch name>/<the latest revision where the branch existed> is created.

Though that functionality should be used with care, it is easy to create a branch replacement commit from SmartGit:

  • Use Local|Reset to reset to some other commit
  • Invoke Remote|Push. SmartGit will ask whether the current branch should be replaced.

Merges

Translating Merges from SVN to Git

Completely merged SVN branches correspond to merged Git branches. In particular, for SVN revisions that change svn:mergeinfo in such a way that some branch becomes completely merged, SmartGit creates a Git merge-commit. For branches which have not been completely merged, no merge-commit is created.

Translating Merges from Git to SVN

Pushing Git merge-commits results in a corresponding svn:mergeinfo modification, denoting that the branch has been completely merged.

Cherry-picks

SmartGit supports translation of two kinds of cherry-pick merges between SVN and Git:

Only cherry-picks of Git commits that correspond to (already pushed) SVN revisions (but not local commits) are supported. Pushing of a cherry-pick commit results in a corresponding svn:mergeinfo change.

Branch Creation

SmartGit allows to create SVN branches simply by pushing locally created Git branches. In this case, SmartGit will ask you to configure the branch for pushing.

SmartGit always creates a separate SVN revision when creating a branch, which contains purely the branch creation. This helps to avoid troubles when merging from that branch later.

Anonymous Branches

Anonymous branches show up very often in Git repositories where the default Pull behavior is merge instead of rebase. Such branches are not mapped back to SVN, as anonymous SVN branches are not supported. For instance, the following history:

  E-F
 /   \
A-B-C-D-G-H (branch)

will be pushed as a linear list of commits: A,B,C,D,G,H. E and F won't be pushed at all.

The Push Process

Pushing a commit consists of 3 phases:

Note that not only the local commit is replaced but also all commits and tags that depend on it. For example, if there is a local commit with a Git tag attached to it, after pushing the Git tag will be moved to the commit that has been fetched back from the SVN repository.

The pushing process requires the working tree to be clean to start, and it uses the working tree very actively during the whole process. Hence, it is STRONGLY RECOMMENDED not to make any changes in the working tree during the pushing process, otherwise these changes may become discarded.

Sometimes it is impossible to replace the existing local commit with the commit being fetched back, because other commits (from other users) might have been fetched back as well, containing changes that conflict with the remaining local commits. In this case, SmartGit leaves the working tree clean and asks you whether to resolve the problem. The easiest way to do so is to press Pull with the Rebase option turned on, which will start the rebase process.

The last repository revision is r10. There are 2 local commits A and B that will be pushed. First, A is sent, resulting in revision r12, because in the meantime someone else had committed r11. Now, r11 and r12 (corresponding to local commit A) are fetched back. Let's assume that r11 and local commit B contain changes for the same file in the same line. Hence, replacing A by the fetched-back commits r11 and r12 won't work, because the changes of B are conflicting now and can't be applied on top of r12.

SVN Support Configuration

SVN URL and SVN Layout Specification

In compatibility mode, .git/config is used for the specification of the SVN URL and the SVN repository layout. In normal mode, SmartGit uses the file .git/svn/.svngit/svngitkit.config for this purpose.

In compatibility mode, the SVN URL and SVN layout are specified in the svn-remote section. In normal mode, the corresponding section is called svn-git-remote.

The section generally looks like this:

[svn-git-remote "svn"]
		url = https://server/path
		rewriteRoot = https://anotherserver/path
		fetch = trunk:refs/remotes/svn/trunk
		branches = branches/*:refs/remotes/svn/*
		additional-branches = path/*:refs/remotes/*;another/path:refs/remotes/another/branch
		tags = tags/*:refs/remote-tags/svn/*

Translation Options

SmartGit keeps all translation options in the core section of the file .git/svn/.svngit/svngitkit.config.

The section looks like this:

[core]
			processExternals = true
			processIgnores = true
			processEols = true
			processTags = true
			processOtherProperties = true
			gitSvnAttributesThreshold = 32
			

These boolean options specify whether SmartGit should enable special handling of svn:externals, svn:eol-style/svn:mime-type, svn:ignore, SVN tags and all other SVN properties, as explained above. The gitSvnAttributesThreshold specifies the maximum length of a Git attribute value, which may be stored in .gitattributes. If the length of the attribute value is larger, it will be stored in the .gitsvnattributes directory structure, as explained in Other SVN properties (Normal Mode Only).

These options are set once before the first fetch and must not be changed afterwards.

In nomal mode, all these options are set to true by default except when SmartGit detects that the .gitattributes file has become too large (in that case processEols and processOtherProperties is set to false). In compatibility mode, all these options are set to false.

Tracking Configuration

SmartGit's SVN support has a tracking configuration similar to Git's. If some local branch (say, refs/heads/branch) tracks some remote branch (refs/remotes/svn/branch), then:

If some branch contains a merge-commit that has a merge-parent that doesn't belong to any tracking branch, svn:mergeinfo won't be modified when pushing such a branch.

SmartGit uses the branch sections of the .git/svn/.svngit/svngitkit.config file for tracking configuration.

The section looks like this:

[branch "master"]
			tracks = refs/remotes/svn/trunk
			remote = svn
			

The name of the section is the local branch name.

Tweaking the configuration

When cloning a repository with Just initialize clone (expert mode) option, SmartGit will only create a repository stub which will be reported as incomplete. This allows you to tweak the Translation options and the Tracking Configuration by changing the .git/svn/.svngit/svngitkit.config file accordingly. After you have done so, you will have to restart the clone by pulling your repository.

After the clone has been initialized, only [svn-git-remote] can be tweaked. [core] options can't be changed anymore at this stage, instead they have to be customized using system properties before starting SmartGit. Changing the [core] configuration by system properties, will not only affect SmartGit's processing but also the Git repository configuration:

  • If you set processEols=false, then core.autocrlf is set to true: the idea behind this logic is that if EOLs are controlled by .gitattributes settings (processEols=true), then core.autocrlf should be false in order not to interfere with .gitattributes. If processEols=false then there will be no .gitattributes which (usually) performs CRLF-to-LF canonicalization in the repository (what usually is desirable). core.autocrlf=true has proven to be the lesser evil in this case.

NTLM authentication

If your server supports NTLM authentication and SmartGit fails to connect to your server with authentication-related errors like 401 Authorization Required, you should try to force SmartGit to use Basic authentication first by setting following system property:

svnkit.http.methods=Basic,NTLM

SVN authors mapping

SmartGit does not support Git config svn.authorsfile, but provides a similar mechanism via system property smartgit.svn.authorsfile: the specified file is respected when translating SVN revisions to Git commits. For the opposite direction it's the SVN server which chooses the revision author (usually the same as the credentials username). The file format is the same as git-svn's authors file (for --authors-file option). File encoding is UTF-8 (since version 17.1).

On Windows, if your authors-file is located at c:\temp\svn.authors, set the system property to:


smartgit.svn.authorsfile=c:/temp/svn.authors

Known Limitations

There are following notable limitations of SmartGit's SVN integration,

File externals not supported

File externals are currently not supported. There are following possible workarounds:

Further Limitations