svn2git is a tiny utility for migrating projects from Subversion to Git while keeping the trunk, branches and tags where they should be. It uses git-svn to clone an svn repository and does some clean-up to make sure branches and tags are imported in a meaningful way, and that the code checked into master ends up being what's currently in your svn trunk rather than whichever svn branch your last commit was in.
Say I have this code in svn:
trunk
...
branches
1.x
2.x
tags
1.0.0
1.0.1
1.0.2
1.1.0
2.0.0
git-svn will go through the commit history to build a new git repo. It will import all branches and tags as remote svn branches, whereas what you really want is git-native local branches and git tag objects. So after importing this project I'll get:
$ git branch
* master
$ git branch -a
* master
1.x
2.x
tags/1.0.0
tags/1.0.1
tags/1.0.2
tags/1.1.0
tags/2.0.0
trunk
$ git tag -l
[ empty ]
After svn2git is done with your project, you'll get this instead:
$ git branch
* master
1.x
2.x
$ git tag -l
1.0.0
1.0.1
1.0.2
1.1.0
2.0.0
Finally, it makes sure the HEAD of master is the same as the current trunk of the svn repo.
Make sure you have git, git-svn, and ruby installed. svn2git is a ruby wrapper around git's native SVN support through git-svn. It is possible to have git installed without git-svn installed, so please do verify that you can run $ git svn
successfully. For a Debian-based system, the installation of the prerequisites would look like:
$ sudo apt-get install git-core git-svn ruby
Once you have the necessary software on your system, you can install svn2git through rubygems, which will add the svn2git
command to your PATH.
$ sudo gem install svn2git
There are several ways you can create a git repo from an existing svn repo. The differentiating factor is the svn repo layout. Below is an enumerated listing of the varying supported layouts and the proper way to create a git repo from a svn repo in the specified layout.
-
The svn repo is in the standard layout of (trunk, branches, tags) at the root level of the repo.
$ svn2git http://svn.example.com/path/to/repo
-
The svn repo is NOT in standard layout and has only a trunk and tags at the root level of the repo.
$ svn2git http://svn.example.com/path/to/repo --trunk dev --tags rel --nobranches
-
The svn repo is NOT in standard layout and has only a trunk at the root level of the repo.
$ svn2git http://svn.example.com/path/to/repo --trunk trunk --nobranches --notags
-
The svn repo is NOT in standard layout and has no trunk, branches, or tags at the root level of the repo. Instead the root level of the repo is equivalent to the trunk and there are no tags or branches.
$ svn2git http://svn.example.com/path/to/repo --rootistrunk
-
The svn repo is in the standard layout but you want to exclude the massive doc directory and the backup files you once accidently added.
$ svn2git http://svn.example.com/path/to/repo --exclude doc --exclude '.*~$'
-
The svn repo actually tracks several projects and you only want to migrate one of them.
$ svn2git http://svn.example.com/path/to/repo/nested_project --no-minimize-url
-
The svn repo is password protected.
$ svn2git http://svn.example.com/path/to/repo --username <<user_with_perms>>
If this doesn't cooperate and you need to specify a password on the command-line:
$ svn2git http://svn.example.com/path/to/repo --username <<user_with_perms>> --password <<password>>
-
You need to migrate starting at a specific svn revision number.
$ svn2git http://svn.example.com/path/to/repo --revision <<starting_revision_number>>
-
You need to migrate starting at a specific svn revision number, ending at a specific revision number.
$ svn2git http://svn.example.com/path/to/repo --revision <<starting_revision_number>>:<<ending_revision_number>>
-
Include metadata (git-svn-id) in git logs.
$ svn2git http://svn.example.com/path/to/repo --metadata
The above will create a git repository in the current directory with the git version of the svn repository. Hence, you need to make a directory that you want your new git repo to exist in, change into it and then run one of the above commands. Note that in the above cases the trunk, branches, tags options are simply folder names relative to the provided repo path. For example if you specified trunk=foo branches=bar and tags=foobar it would be referencing http://svn.example.com/path/to/repo/foo as your trunk, and so on. However, in case 4 it references the root of the repo as trunk.
As of svn2git 2.0 there is a new feature to pull in the latest changes from SVN into your git repository created with svn2git. This is a one way sync, but allows you to use svn2git as a mirroring tool for your SVN repositories.
The command to call is:
$ cd <EXISTING_REPO> && svn2git --rebase
To convert all your svn authors to git format, create a file somewhere on your system with the list of conversions to make, one per line, for example:
jcoglan = James Coglan <[email protected]>
stnick = Santa Claus <[email protected]>
Then pass an authors option to svn2git pointing to your file:
$ svn2git http://svn.example.com/path/to/repo --authors ~/authors.txt
Alternatively, you can place the authors file into ~/.svn2git/authors
and
svn2git will load it out of there. This allows you to build up one authors
file for all your projects and have it loaded for each repository that you
migrate.
If your project has many contributors or tracking down the information for every author is unfeasible, you can use the --authors-prog option:
$ svn2git http://svn.example.com/path/to/repo --authors-prog ~/authors.sh
If specified, for each svn committer name that does not exist in the authors
file, the given file is executed with the committer name as the first argument.
The program is expected to return a single line of the form "Name <email>",
which will be created as if included in the authors file. This script can also be placed into ~/.svn2git/authors-prog
and svn2git will load it from there.
If you need a jump start on figuring out what users made changes in your
svn repositories the following command sequence might help. It grabs all
the logs from the svn repository, pulls out all the names from the commits,
sorts them, and then reduces the list to only unique names. So, in the end
it outputs a list of usernames of the people that made commits to the svn
repository which name on its own line. This would allow you to easily
redirect the output of this command sequence to ~/.svn2git/authors
and have
a very good starting point for your mapping.
$ svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
Or, for a remote URL:
$ svn log --quiet http://path/to/root/of/project | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
If you're having problems with converting your repository and you're not sure why, try turning on verbose logging. This will print out more information from the underlying git-svn process.
You can turn on verbose logging with the -v
or --verbose
flags, like so:
$ svn2git http://svn.yoursite.com/path/to/repo --verbose
$ svn2git --help
Usage: svn2git SVN_URL [options]
Specific options:
--rebase Instead of cloning a new project, rebase an existing one against SVN
--username NAME Username for transports that needs it (http(s), svn)
--password PASSWORD Password for transports that needs it (http(s), svn)
--trunk TRUNK_PATH Subpath to trunk from repository URL (default: trunk)
--branches BRANCHES_PATH Subpath to branches from repository URL (default: branches); can be used multiple times
--tags TAGS_PATH Subpath to tags from repository URL (default: tags); can be used multiple times
--rootistrunk Use this if the root level of the repo is equivalent to the trunk and there are no tags or branches
--notrunk Do not import anything from trunk
--nobranches Do not try to import any branches
--notags Do not try to import any tags
--no-minimize-url Accept URLs as-is without attempting to connect to a higher level directory
--revision START_REV[:END_REV]
Start importing from SVN revision START_REV; optionally end at END_REV
-m, --metadata Include metadata in git logs (git-svn-id)
--complextags Create tags from the tag commit instead the tag commit parent
--authors AUTHORS_FILE Path to file containing svn-to-git authors mapping (default: ~/.svn2git/authors)
--authors-prog AUTHORS_PROG Path to script containing svn-to-git authors mapping (default: ~/.svn2git/authors-prog)
--exclude REGEX Specify a Perl regular expression to filter paths when fetching; can be used multiple times
-v, --verbose Be verbose in logging -- useful for debugging issues
-h, --help Show this message
-
Why is the code different from svn when I check out a tag?
Similar to a git annotated tag, a svn tag is the current source tree copied into another path (typically /tags). Using
svn copy
allows copying local changes and files manually updated to an arbitrary revision which become part of the tag commit. This mixed history commit is referred to as a complex tag and there is no parallel in git.If you need to support this behavior you can use the
--complextags
option.The tags won't show up in the master branch because the tags are tied to a commit in the branch that was created by git-svn converting the tags directory, which is deleted locally by svn2git after making an annotated tag. If you want to see all the branches and tags and their relationships in gitk you can run the following:
gitk --all