Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuler/2599814 to your computer and use it in GitHub Desktop.
Save tuler/2599814 to your computer and use it in GitHub Desktop.
Redmine SVN to Git Migrator
#!/usr/bin/ruby
#
# Migration Tool for Redmine issue and time entry comments from SVN revisions to Git (i.e. r1234 => commit:abcdef789)
# ==============================
# (c) 2009 Christoph Olszowka & Sebastian Georgi, Capita Unternehmensberatung
#
# We used this when we migrated two of our main repositories from svn to git (see http://gist.github.com/139478)
# and wanted to have our revision links in Redmine reflect that change, too.
#
# WARNING: This worked for us, but you better backup your database before letting this off!
# Provided "as is", no waranties, blah!
#
#
# Please make sure you have grit installed before launching: sudo gem install grit
#
# Simply run this with:
# $ ./redmine_svn_to_git_migrator.rb ./path/to/a/proper/git_clone/of_your_repo REDMINE_PROJECT_ID
#
# NOTE:
# If you want to check the conversions first, put a "if 1 == 2" behind journal.save! and te.save!
# The result will be a "dry run", only displaying the changes that would be done, without actually touching things
#
require 'rubygems'
require 'grit'
require 'pp'
include Grit
def migrate(repo_path, project_id)
project = Project.find(project_id)
if __FILE__ == $0
puts "", ""
print "Working with directory #{repo_path} and project #{project.name}. Continue? "
foo = STDIN.gets
end
repo = Repo.new(repo_path)
def extract_svn_revision(message)
match = message.match(/; revision=(\d+)/)
if match
match[2].strip.chomp
else
nil
end
rescue => err
raise "Failed to extract from #{message}"
end
read_commits = {} # map from git commit to svn revision
revisions = {} # map from svn revision to git commit(s)
repo.heads().each do |head|
puts "Reading branch #{head.name}"
offset = 0
limit = 50
# read commits in batches
while commits = repo.commits(head.name, limit, offset) and not commits.empty?
commits.each do |commit|
c = "commit:#{commit.to_s[0..8]}"
if !read_commits[c]
# extract svn revision from commit message
svn_revision = extract_svn_revision(commit.message)
if svn_revision = extract_svn_revision(commit.message)
revision = "r#{svn_revision}"
if (revisions[revision])
if !revisions[revision].index(c)
revisions[revision] = revisions[revision] + " #{c}"
puts "[WARNING] Revision #{revision} is ambiguous, poiting to #{revisions[revision]}"
end
else
revisions[revision] = c
end
read_commits[c] = svn_revision
else
puts "[WARNING] #{commit} does not have svn revision"
puts commit.message
puts
end
end
end
offset += limit
end
end
puts "Found #{revisions.size} revision mappings..."
# Fix issues
project.issues.each do |issue|
puts "Working on issue #{issue.id}: #{issue.subject}"
issue.journals.each do |journal|
puts " Journal \##{journal.journalized_id}"
revisions.each do |svn_revision, git_commit|
svn_regexp = /#{svn_revision}([^\d])/
if journal.notes =~ svn_regexp
puts "Match found for #{svn_revision}:"
puts "#{journal.notes}"
puts " converted to"
converted_notes = "#{journal.notes.gsub(svn_regexp, "#{git_commit}#{$1}")}"
puts converted_notes
# Update notes
journal.notes = converted_notes
journal.save!
end
end
end
puts "", "="*80, ""
end
# Fix time entries
project.time_entries.each do |te|
puts "Working on time entry #{te.id}"
revisions.each do |svn_revision, git_commit|
svn_regexp = /#{svn_revision}([^\d])/
if te.comments =~ svn_regexp
puts "Match found for #{svn_revision}:"
puts "#{te.comments}"
puts " converted to"
converted_comments = "#{te.comments.gsub(svn_regexp, "#{git_commit}#{$1}")}"
puts converted_comments
# Update notes
te.comments = converted_comments
te.save!
puts "", "="*80, ""
end
end
end
end
if __FILE__ == $0
repo_path = ARGV[0].strip.chomp
project_id = ARGV[1].strip.chomp
puts "Loading environment..."
RAILS_ENV = 'production'
require '/var/www/redmine/config/environment' # Replace with path to your actual Redmine install
migrate(repo_path, project_id)
end
@tuler
Copy link
Author

tuler commented May 5, 2012

Changes from the original script:

  • changed the revision pattern
  • changed the default environment path
  • getting repo commits from every branch instead of just 'master'
  • dealing with ambiguous svn revisions

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