Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dgmike/3815867 to your computer and use it in GitHub Desktop.
Save dgmike/3815867 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(/git-svn-id(.*)@(\d+) /)
if match
match[2].strip.chomp
else
nil
end
rescue => err
raise "Failed to extract from #{message}"
end
# Set up collection of commits (grit fails to load all of them in one run when setting a high limit)
commit_collection = []
offset = 0
limit = 50
while commits = repo.commits('master', limit, offset) and not commits.empty?
commit_collection += commits
offset += limit
end
# Go through all commits, try to extract svn revision number and map it
revisions = {}
commit_collection.each do |commit|
if svn_revision = extract_svn_revision(commit.message)
revisions["r#{svn_revision}"] = "commit:#{commit.to_s[0..8]}"
else
puts "[WARNING] #{commit} does not have svn revision"
puts commit.message
puts
puts
end
# puts "\n#{commit} => #{}\n#{commit.message}\n", "=" * 80
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 '/path/to/config/environment' # Replace with path to your actual Redmine install
migrate(repo_path, project_id)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment