Skip to content

Instantly share code, notes, and snippets.

@magnetikonline
Last active November 22, 2018 15:05
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save magnetikonline/5061085 to your computer and use it in GitHub Desktop.
Save magnetikonline/5061085 to your computer and use it in GitHub Desktop.
Subversion (SVN) to Git repository conversion notes.

SVN to Git repository conversion notes

Generate Git authors file

$ svn log -q file://path/to/svn/root | \
	awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2">"}' | \
	sort -u > gitauthors.txt

$ echo "(no author) = Unknown User <unknown.user@emailaddr>" >> gitauthors.txt

Tweak the generated gitauthors.txt to suit, adding in fullname and email address details. Including a final line of (no author) = Unknown User <unknown.user@emailaddr> allows for a "catch all" user.

Create Git repository

# init new Git repo based off source SVN repo
# note: may need to tweak --trunk/--tags/--branches/--stdlayout to suit, refer to man git-svn(1)
$ git svn clone \
	--authors-file=gitauthors.txt \
	--trunk=svn/path/to/trunk \
	--no-metadata --prefix=svn/ \
	svn+ssh://user@svnserver/path/to/svn/repository gitrepo.tmp

# fetch repo contents from SVN
$ git svn fetch

# now branch remotes/svn/trunk as the new local master
$ cd gitrepo.tmp
$ git branch -m master master.old
$ git checkout -b master svn/trunk
$ git branch -D master.old

Handling irregular branch structures

In the instance where source branches aren't all contained nicely within a within a single sub-tree (e.g. /branches/*/), or you wish to cherry-pick specific branches to export to the new Git repository. In this case we can't simply git svn clone, but need to use a little git config trickery to get the job done.

# init new Git repo based off source SVN repo
$ git svn init \
	--authors-file=gitauthors.txt \
	--trunk=svn/path/to/trunk \
	--no-metadata --prefix=svn/ \
	svn+ssh://user@svnserver/path/to/svn/repository gitrepo.tmp

# add two branch pattern matching examples, will ONLY match/import the following branches
# - /branches/one/
# - /branches/two/
# - /branches/three/
# - /branches/four/
# - /branches/subtopic/apples/
# - /branches/subtopic/oranges/
$ git config --local --add svn-remote.svn.branches \
	"branches/{one,two,three,four}:refs/remotes/svn/*"
$ git config --local --add svn-remote.svn.branches \
	"branches/subtopic/{apples,oranges}:refs/remotes/svn/*"

# fetch repo contents from SVN
$ git svn fetch

# now branch remotes/svn/trunk as the new local master
$ cd gitrepo.tmp
$ git branch -m master master.old
$ git checkout -b master svn/trunk
$ git branch -D master.old

Create local branches from remotes/svn/*

Script would be run from the freshly created Git repository directory gitrepo.tmp outlined in previous step.

#!/bin/bash -e

# convert all remaining remotes/svn branches into local branches
for branchName in `git branch -r | \
	grep '^ *svn/' | grep -v '^ *svn/tags/' | grep -v '^ *svn/trunk$' | \
	grep -vE '^ *svn/.+@[0-9]+$'`; do
	echo $branchName
	git checkout -b `echo $branchName | sed -e 's/^svn\///'` $branchName
done

Create clean Git remote and populate

Taking the corrected repository in gitrepo.tmp and pushing all branches to a new Git shared remote in gitrepo.remote for a development team to start using.

#!/bin/bash -e

GIT_REPO_TMP_DIR="gitrepo.tmp"
GIT_REPO_REMOTE_DIR="gitrepo.remote"


# create a new Git remote and push all local branches from our cleaned temp repository into it
git init --bare --shared=group $GIT_REPO_REMOTE_DIR
git --git-dir=$GIT_REPO_TMP_DIR/.git remote add origin $GIT_REPO_REMOTE_DIR

for branchName in `git --git-dir=$GIT_REPO_TMP_DIR/.git branch | sed -e 's/^* //'`; do
	echo $branchName
	git --git-dir=$GIT_REPO_TMP_DIR/.git push origin $branchName
done

Also you can push your SVN remotes created in gitrepo.tmp directly into a Git shared remote gitrepo.remote, (with target branch renaming too if needed) - like so.

#!/bin/bash -e

GIT_REPO_TMP_DIR="gitrepo.tmp"
GIT_REPO_REMOTE_DIR="gitrepo.remote"


git init --bare --shared=group $GIT_REPO_REMOTE_DIR
git --git-dir=$GIT_REPO_TMP_DIR/.git remote add transfer $GIT_REPO_REMOTE_DIR

for branchName in `git --git-dir=$GIT_REPO_TMP_DIR/.git branch -r | \
	grep '^ *svn/' | grep -vE '^ *svn/.+@[0-9]+$'`; do
	echo $branchName
	git --git-dir=$GIT_REPO_TMP_DIR/.git push transfer \
		$branchName:refs/heads/`echo $branchName | sed -e 's/^svn\///'`
done

# or to rename a source branch in the targer
git --git-dir=$GIT_REPO_TMP_DIR/.git push transfer svn/somebranchname:refs/heads/targetbranchname

Other useful links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment