James Bowes

Purveyor of Pre-eminent Programmes

Converting SVN Commits to Git Patches

leave a comment »

In case you find yourself in need of a way to turn an svn revision into a git patch that can be applied with ‘git am’, keeping the commit message and authorship information, here’s a script I used recently:

#!/usr/bin/python
#
# svnrev2git.py - Convert an SVN revsion to a Git patch.
#
# Author: James Bowes <jbowes@repl.ca>
#
# Usage:
#   $> cd my-svn-repo
#   $> python svnrev2git.py [AUTHORS_FILE] [REV_RANGE | REVSION [REVISION..]]
#
#   AUTHORS_FILE - a CSV of  svn username, full name, email
#   REV_RANGE - an svn revision range, like 100-700
#   REVISION - a single svn revision
#
#   You may specify either a revision range, or a series of individual
#   svn revisions
#
# Output:
#   A series of git style patch files, one per svn revision, which can then be
#   applied with 'git am'
#
# Why use this instead of 'git svn'?
#   I had done a large repo conversion via git svn where we wanted no downtime
#   for the switchover. After removing the git svn specific info from our git
#   commits, I used this tool to bring in commits from svn, keeping svn and git
#   in sync, until we were ready to switch.

import sys
import commands

def svnlog_to_gitlog(authors, svnlog):

    lines = svnlog.split("\n")
    lines = lines[1:-1]

    metainfo = lines[0].split(" | ")
    subject = lines[2]
    description = lines[3:]

    author = metainfo[1]

    day = metainfo[2].split("(")[1][:-1]
    time = metainfo[2].split(" ")[1]
    offset = metainfo[2].split(" ")[2]

    gitlog = []
    gitlog += ["From: %s <%s>" % authors[author]]
    gitlog += ["Date: %s %s %s" % (day, time, offset)]
    gitlog += ["Subject: [PATCH] %s" % subject]
    gitlog += [""]
    gitlog += description
    gitlog += [""]

    return '\n'.join(gitlog)

def svndiff_to_gitdiff(svndiff):
    lines = svndiff.split("\n")

    gitdiff = []
    for line in lines:
        if line.startswith("--- "):
            gitdiff.append("--- a/" + line[4:])
        elif line.startswith("+++ "):
            gitdiff.append("+++ b/" + line[4:])
        else:
            gitdiff.append(line)

    return '\n'.join(gitdiff)

def make_patch(authors, rev):
    out = commands.getoutput("svn log -c %s ." % rev)

    if len(out.split("\n")) < 2:
        print "skipping r%s" % rev
        return

    patch = open(rev + ".patch", 'w')
    patch.write(svnlog_to_gitlog(authors, out))
    patch.write("---\n\n")

    out = commands.getoutput("svn diff -c %s ." % rev)
    patch.write(svndiff_to_gitdiff(out))

    patch.write("\n---\n")
    patch.write("svnrev2git.py\n")

    patch.close()
    print "wrote %s.patch" % rev

def main(args):
    author_file = open(args[0])
    authors = {}

    print "loading authors"
    for line in author_file.readlines():
        parts = line.strip().split(", ")
        authors[parts[0]] = (parts[1], parts[2])

    author_file.close()

    revs = args[1:]

    if len(revs) == 1 and '-' in revs[0]:
        start, end = revs[0].split('-')
        start = int(start)
        end = int(end)
        revs = [str(x) for x in range(start, end + 1)]

    for rev in revs:
        make_patch(authors, rev)

if __name__ == "__main__":
    main(sys.argv[1:])
Advertisements

Written by jbowes

June 23, 2009 at 6:29 am

Posted in SCM, tech

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: