#!/usr/bin/env ruby require 'optparse' OPTIONS = { :environment => "production", :reload_db => false, :dry_run => false, :verbose => false, :truncate_db => false } ARGV.options do |opts| script_name = File.basename($0) opts.banner = "Usage: ruby #{script_name} [options]" opts.separator "" opts.on("-t", "--truncate-db", "resync the entire database, truncating it first") { |OPTIONS[:truncate_db]| } opts.on("-e", "--environment=name", String, "Specifies the environment to run this script against", "(test/development/production). Default: production") { |OPTIONS[:environment]| } opts.on("-d", "--dry-run", "Doesn't insert anything into the database") { |OPTIONS[:dry_run]| } opts.on("-v", "--verbose", "Use verbose output on STDOUT") { |OPTIONS[:verbose]| } opts.separator "" opts.on("-h", "--help", "Show this help message.") { puts opts; exit } opts.parse! end # Set up our environment ENV["RAILS_ENV"] = OPTIONS[:environment] require File.dirname(__FILE__) + '/../config/environment' if OPTIONS[:dry_run] puts '*' * 50 puts ' ' * 10 + 'THIS IS A DRY RUN ONLY!' puts '*' * 50 end # Truncate the tables if requested if OPTIONS[:truncate_db] ActiveRecord::Base.connection.execute("TRUNCATE TABLE changesets") unless OPTIONS[:dry_run] ActiveRecord::Base.connection.execute("TRUNCATE TABLE changes") unless OPTIONS[:dry_run] puts ">>> Truncated 'changesets' and 'changes' tables <<<" end # Reset column info Change.reset_column_information Changeset.reset_column_information # The actual sync stuff begin Changeset.transaction do last_stored = Changeset.find_first(nil, "revision DESC") if last_stored.nil? youngest_stored = 0 else youngest_stored = last_stored.revision end youngest_rev = Repository.get_youngest_rev # FOR DEBUG !!! #youngest_stored = 93 #youngest_rev = 94 revs_to_sync = ((youngest_stored + 1)..youngest_rev).to_a revs_to_sync.each do |rev| revision = Repository.get_changeset(rev) cs = Changeset.create(:revision => rev, :author => revision.author, :log => revision.log_message, :revised_at => revision.date) unless OPTIONS[:dry_run] if OPTIONS[:verbose] puts '-' * 50 puts "revision #{rev} by #{revision.author}. Commit message:" puts revision.log_message puts '-' * 50 end revision.copied_nodes.each do |path| cs.changes << Change.new(:revision => rev, :name => 'CP', :path => path[0], :from_path => path[1], :from_revision => path[2]) unless OPTIONS[:dry_run] puts " CP #{path[0]} (from #{path[1]}:#{path[2]})" if OPTIONS[:verbose] end revision.moved_nodes.each do |path| cs.changes << Change.new(:revision => rev, :name => 'MV', :path => path[0], :from_path => path[1], :from_revision => path[2]) unless OPTIONS[:dry_run] puts " MV #{path[0]} (from #{path[1]}:#{path[2]})" if OPTIONS[:verbose] end revision.added_nodes.each do |path| cs.changes << Change.new(:revision => rev, :name => 'A', :path => path) unless OPTIONS[:dry_run] puts " A #{path}" if OPTIONS[:verbose] end revision.deleted_nodes.each do |path| cs.changes << Change.new(:revision => rev, :name => 'D', :path => path) unless OPTIONS[:dry_run] puts " D #{path}" if OPTIONS[:verbose] end revision.updated_nodes.each do |path| cs.changes << Change.new(:revision => rev, :name => 'M', :path => path) unless OPTIONS[:dry_run] puts " M #{path}" if OPTIONS[:verbose] end puts "* Synced changeset #{rev}" puts if OPTIONS[:verbose] # Run GC manually to cut down the memory footprint, # TODO: recheck it on next svn update, current release has known memory issues revision = nil cs = nil GC.start sleep 0.01 # give GC a chance end if youngest_stored < youngest_rev end # end transaction rescue ActiveRecord::RecordInvalid => rie # presuming it'll only get raised when revision is not unique puts "Revision already exists, moving on.." end