Migration is not always easy. However, git-svn
is a quite useful tool to do the job.
To do the job right, this post from stackoverflow is tremendously helpful in my entire process.
I wrote the m.py to automate the entire process.
#!/usr/bin/env python | |
""" | |
m.py: Migrate svn to git. | |
Beware that this version ignore the initial direction creation commit. | |
""" | |
__author__ = "Zhenyang Hua" | |
__license__ = "MIT" | |
__version__ = "0.0.1" | |
__maintainer__ = "Zhenyang Hua" | |
__email__ = "edison.hua@gmail.com" | |
__status__ = "Development" | |
from subprocess import (PIPE, Popen) | |
import os | |
import shutil | |
""" | |
Settings | |
""" | |
# Don't miss the trailing slash | |
svnUsername = "svn username" | |
svnRemoteRepo = "url to svn remote repo" | |
gitRemoteRepo = "url to git remote repo" | |
""" | |
Program starts here | |
""" | |
root = os.path.dirname(os.path.abspath(__file__)) | |
svnUrlParts = svnRemoteRepo.split('/') | |
svnLocalRepo = "./" + svnUrlParts[len(svnUrlParts) - 2] | |
gitUrlParts = gitRemoteRepo.split('/') | |
gitLocalRepo = "./" + gitUrlParts[len(gitUrlParts) - 2] | |
starting_revision = "" | |
def invoke(command): | |
return Popen(command, stdout=PIPE, shell=True).stdout.read() | |
def getSvnRepo(): | |
co = "svn co " + svnRemoteRepo | |
invoke(co) | |
def getAuthors(): | |
os.chdir(svnLocalRepo) | |
lines = invoke("svn log -q").split('\n') | |
authorList = [] | |
revisionList = [] | |
for line in lines: | |
entry = line.split(' | ') | |
if (len(entry) > 1): | |
name = entry[1] | |
author = name + " = " + name + ' <' + name + '@woolpert.com>' | |
authorList.append(author) | |
revision = entry[0] | |
revisionList.append(revision) | |
authorList = sorted(list(set(authorList))); | |
with open("../author_list.txt", "w") as authorListTxt: | |
for author in authorList: | |
authorListTxt.write(author) | |
global starting_revision | |
starting_revision = revisionList[len(revisionList) - 2][1:] | |
def checkAuthors(): | |
editingWarning = "> Please make sure you have updated the author_list.txt, enter Y to confirm: " | |
response = raw_input(editingWarning) | |
if response.upper() == "Y": | |
print "\n> author_list.txt was confirmed.\n" | |
else: | |
checkAuthors() | |
def cloneSvnRepo(): | |
os.chdir(root) | |
cloneRepo = "git svn clone -r " + starting_revision + " --stdlayout --username=" + svnUsername + " --authors-file=author_list.txt --no-minimize-url " + svnRemoteRepo + " " + gitLocalRepo | |
invoke(cloneRepo) | |
os.chdir(gitLocalRepo) | |
rebase = "git svn rebase" | |
invoke(rebase) | |
def pushToGit(): | |
os.chdir(root) | |
os.chdir(gitLocalRepo) | |
addRemote = "git remote add origin " + gitRemoteRepo | |
invoke(addRemote) | |
pushRepo = "git push origin master" | |
invoke(pushRepo) | |
def onerror(func, path, exc_info): | |
""" | |
Error handler for ``shutil.rmtree``. | |
If the error is due to an access error (read only file) | |
it attempts to add write permission and then retries. | |
If the error is for another reason it re-raises the error. | |
Usage : ``shutil.rmtree(path, onerror=onerror)`` | |
""" | |
import stat | |
if not os.access(path, os.W_OK): | |
# Is the error an access error ? | |
os.chmod(path, stat.S_IWUSR) | |
func(path) | |
else: | |
raise | |
def cleanup(): | |
os.chdir(root) | |
if os.path.isdir(svnLocalRepo): | |
shutil.rmtree(svnLocalRepo, onerror=onerror) | |
if os.path.isdir(gitLocalRepo): | |
shutil.rmtree(gitLocalRepo, onerror=onerror) | |
if os.path.exists("./author_list.txt"): | |
os.remove("./author_list.txt") | |
def main(): | |
print "\n----Start----\n" | |
print "1. Checking out the svn repository...\n" | |
getSvnRepo() | |
print "2. Retrieving a list of all Subversion committers...\n" | |
getAuthors() | |
print "3. Confirming username and email of the committers\n" | |
checkAuthors() | |
print "4. Cloning the Subversion repository using git-svn...\n" | |
cloneSvnRepo() | |
print "5. Pushing the local repository to the remote...\n" | |
pushToGit() | |
print "6. Cleaning up...\n" | |
cleanup() | |
print "----End----\n" | |
if __name__ == "__main__": | |
main() |