namespace :migrations do
desc 'reverts, renames and re-executes uncommitted migrations that conflict with already committed migrations'
task :repair => :environment do
uncommitted = `svn status`.select {|line| line =~ /db\/migrate\//}.collect {|line| line.match(/ db\/migrate\/(.*rb)/)[1]}
committed = Dir.glob('db/migrate/*').collect {|line| line.match(/db\/migrate\/(.*rb)/)[1]} - uncommitted
highest_committed = committed.collect(&:to_i).max
lowest_uncommitted = uncommitted.collect(&:to_i).min
schema_version = ActiveRecord::Base.connection.select_one("select version from schema_info")["version"].to_i
# revert uncommitted migrations
uncommitted.sort_by(&:to_i).reverse.each do |migration|
version = migration.to_i
if version <= schema_version
name = migration.match(/(\d*_.*).rb/)[1]
class_name = (require 'db/migrate/' + name)[0]
puts class_name.constantize.down
end
end
#set it back to the version prior to all the uncommitted migrations - this doesn't get done automatically by calling down
ActiveRecord::Base.connection.execute("update schema_info set version=#{schema_version-1}")
version_offset = (highest_committed - lowest_uncommitted) + 1
uncommitted.each do |old_name|
version = old_name.to_i + version_offset
new_name = version.to_s.rjust(3,'0') + old_name.gsub(/\d/, '')
`mv db/migrate/#{old_name} db/migrate/#{new_name}`
end
puts "Uncommited migrations have been reversed and renamed. You can now migrate."
end
end
Refactorings
No refactoring yet !
macournoyer
October 29, 2007, October 29, 2007 20:57, permalink
This is the most useful rake task ever for a Rails user!
A couple of changes:
* rename to db:migrate:repair
* fix version on line 24 if more then one uncommitted migration
* invoke db:migrate at the end
namespace :db do
namespace :migrate do
desc 'Reverts, renames and re-executes uncommitted migrations that conflict with already committed migrations'
task :repair => :environment do
uncommitted = `svn status`.select {|line| line =~ /db\/migrate\//}.collect {|line| line.match(/ db\/migrate\/(.*rb)/)[1]}
committed = Dir.glob('db/migrate/*').collect {|line| line.match(/db\/migrate\/(.*rb)/)[1]} - uncommitted
highest_committed = committed.collect(&:to_i).max
lowest_uncommitted = uncommitted.collect(&:to_i).min
schema_version = ActiveRecord::Base.connection.select_one("SELECT version FROM schema_info")["version"].to_i
# revert uncommitted migrations
uncommitted.sort_by(&:to_i).reverse.each do |migration|
version = migration.to_i
if version <= schema_version
name = migration.match(/(\d*_.*).rb/)[1]
class_name = (require 'db/migrate/' + name)[0]
puts class_name.constantize.down
end
end
# set it back to the version prior to all the uncommitted migrations - this doesn't get done automatically by calling down
ActiveRecord::Base.connection.execute("UPDATE schema_info SET version = #{schema_version - uncommitted.size}")
version_offset = (highest_committed - lowest_uncommitted) + 1
uncommitted.each do |old_name|
version = old_name.to_i + version_offset
new_name = version.to_s.rjust(3,'0') + old_name.gsub(/\d/, '')
`mv db/migrate/#{old_name} db/migrate/#{new_name}`
end
Rake::Task['db:migrate'].invoke
end
end
end
Explained here: http://www.danielharan.com/2007/10/26/rails-migrations-handling-naming-conflicts/