-
-
Save dgmike/3815867 to your computer and use it in GitHub Desktop.
Redmine SVN to Git Migrator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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