Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Migrates multiple SVN repos to Git, without anything fancy (no Ruby, no dependencies, no issues with Cygwin paths). Works with GitHub or any Git repo.
Migrates multiple SVN repos to Git, without anything fancy (no Ruby, no dependencies, no issues
with Cygwin paths). SVN tags are created as Git tags and properly pushed to the Git repo. To run:
1) Edit to have your SVN repos. Run it to get an authors.txt file.
2) Edit the authors.txt to have the names and emails if you like. You must use GitHub user email
addresses if you want contributions to be associated with GitHub accounts.
3) Edit to have your SVN repos and project name (for local temp directories). Run it.
4) Optionally, for each repo run this command to identify the largest 1000 files:
cd repo.git
join -o "1.1 1.2 2.3" <(git rev-list --objects --all | sort) <(git verify-pack -v objects/pack/*.idx | sort -k3 -n | tail -1000 | sort) | sort -k3 -n
Then run BFG Repo Cleaner to delete files that are too large:
java -jar bfg.jar --strip-blobs-bigger-than 100M repo.git
cd repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
5) Create each Git repo on the server: git init --bare reponame.git
Or if creating new repos on GitHub, don't populate them with a readme or license.
Push to server using your repo URL:
cd repo.git
git push --mirror --follow-tags ssh://
6) Done! Isn't Git fun!
cp authors.txt authors.temp
svn log -q $1 | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2">"}' | sort -u >> authors.temp
cat authors.temp | sort -u > authors.txt
rm authors.temp
echo "Cloning SVN repository to $name.svn... $svn_url"
rm -rf $name.svn
git svn clone $svn_url --no-metadata -A authors.txt --stdlayout $name.svn
cd $name.svn
echo "Creating .gitignore file..."
git svn show-ignore --id=origin/trunk > .gitignore
git add .gitignore
git commit -m 'Convert svn:ignore properties to .gitignore.'
echo "Initializing Git repository... $name.git"
cd ..
rm -rf $name.git
git init --bare $name.git
cd $name.git
git symbolic-ref HEAD refs/heads/trunk
echo "Pushing to Git repository... $name.git"
cd ../$name.svn
git remote add bare ../$name.git
git config remote.bare.push 'refs/remotes/origin/*:refs/heads/*'
git push bare
echo "Renaming trunk to master..."
cd ../$name.git
git branch -m trunk master
echo "Converting SVN tag branches to Git tags..."
git for-each-ref --format='%(refname)' refs/heads/tags | cut -d / -f 4 |
while read ref
git tag -a "$ref" -m "Tag: $ref" "refs/heads/tags/$ref";
git branch -D "tags/$ref";
echo "Done!"
rm authors.txt
sh reflectasm
sh kryo
sh minlog
sh kryonet
sh reflectasm
sh jsonbeans
sh yamlbeans
sh tablelayout
sh wildcard
sh scar
Copy link

C-Duv commented Dec 12, 2013

Thanks, your scripts were very handy.
Had some issue with git svn show-ignore that failed: fixed it by using --id trunk argument.

Also had special needs (no GitHub, preserve empty directories, add a develop branch) so I've improved them.
If that's of any help to anyone, here they are:

Copy link

NathanSweet commented Nov 14, 2016

Glad it was helpful. Doing this again, I also found show-ignore failing. I ended up needing git svn show-ignore --id=origin/trunk. Thanks for the tip!

I also found git push bare on Windows didn't work from a command prompt, but did from a Cygwin prompt (that was a huge WTF). Lastly I found I needed git config remote.bare.push 'refs/remotes/origin/*:refs/heads/*' (note the added "origin").

Both changes have been made to the gist above.

All this worked without changes back when I made the Gist initially. Git is ridiculous!

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