解决Rails孤立迁移的最佳方法是什么?

时间:2013-04-16 16:03:44

标签: ruby-on-rails ruby-on-rails-3 git migration rails-migrations

我一直在项目中的分支之间切换,每个分支都有不同的迁移......这就是场景:

  

$ rake db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   ...
   up     20130307154128  Change columns in traffic capture
   up     20130311155109  Remove log settings
   up     20130311160901  Remove log alarm table
   up     20130320144219  ********** NO FILE **********
   up     20130320161939  ********** NO FILE **********
   up     20130320184628  ********** NO FILE **********
   up     20130322004817  Add replicate to root settings
   up     20130403190042  ********** NO FILE **********
   up     20130403195300  ********** NO FILE **********
   up     20130403214000  ********** NO FILE **********
   up     20130405164752  Fix ap hostnames
   up     20130410194222  ********** NO FILE **********

问题是rake db:rollback根本不起作用,因为文件丢失......

如果能够再次回滚并删除NO FILE消息,我该怎么办?

顺便说一句,rake db:resetrake db:drop不是一个选项,我不能丢失其他表中的数据......

10 个答案:

答案 0 :(得分:50)

我最终解决了这样的问题:

(1)转到包含迁移文件的分支并将其回滚。如果您有许多分支,如果您尝试合并它们会导致许多冲突,这并非易事。所以我用这个命令找出每个孤儿迁移所属的分支。

所以,我需要找到上次修改迁移的提交。

git log --all --reverse --stat | grep <LASTEST_ORPHAN_MIGRATION_ID> -C 10

我接受提交哈希并确定它属于哪个分支:

git branch --contains <COMMIT_HASH>

然后我可以回到那个分支,进行回滚并对所有丢失的文件重复此过程。

(2)运行迁移:检查您最终要处理的分支并运行迁移,您应该好好去。

<强>疑难解答

我还遇到了一些孤立迁移的情况,其中删除了分支。

为了解决这个问题,我创建了虚拟迁移文件,其中包含丢失文件的migration_id,并将其回滚。之后,我能够删除他们的虚拟迁移并具有干净的迁移状态:)

另一种方法是直接从数据库中删除丢失的文件:

delete from schema_migrations where version='<MIGRATION_ID>';

答案 1 :(得分:25)

迁移存储在您的数据库中。如果要删除已放弃的迁移,请从db中删除它们。

Postgres的例子:

  1. 打开psql:

    psql
    
  2. 连接到您的数据库:

    \c your_database
    
  3. 如果您好奇,请显示schema_migrations:

    SELECT * FROM schema_migrations;
    
  4. 如果您感到好奇,请检查是否存在已弃用的迁移:

    SELECT version FROM schema_migrations WHERE version IN 
    ('20130320144219', '20130320161939', '20130320184628', '20130403190042',
     '20130403195300', '20130403214000', '20130410194222');
    
  5. 删除它们:

    DELETE FROM schema_migrations WHERE version IN (<version list as above>);
    
  6. 现在,如果您运行bundle exec rake db:migrate:status,您将看到已成功删除孤立迁移。

答案 2 :(得分:17)

编辑:正如评论中所述,以下将删除您的数据库

一种对我有用的简单方法(请注意,此命令将丢弃数据库,并且所有数据都将丢失):

rake db:migrate:reset

..然后:

rake db:migrate:status

孤儿应该消失。

答案 3 :(得分:2)

使用以下名称创建新文件 20130320144219_migration_1 将一些空白代码放入

class Migration1 < ActiveRecord::Migration 
  def change; end 
end 

并运行命令rails db:migrate:down VERSION= 20130320144219 最后,删除此文件

答案 4 :(得分:0)

假设您正在使用Git,获取这些迁移并将它们带入当前分支应该相对简单。如果您有特定的提交,则可以使用:

git checkout <commit hash> <file_name>

(感谢this answer

或者,您可以从特定的分支机构退房:

git checkout <branch name> -- <file_name>

根据此blog post

实际上,假设这些迁移的版本在数据库上运行,您应该很好地回滚。

答案 5 :(得分:0)

您可以将两个分支合并回主服务器,以便可以进行所有迁移。如果您确实不希望那些迁移,但希望能够回滚,则可以编辑数据库中的schema_migrations表,以删除与您没有文件的迁移相对应的行。但是,如果您切换到具有不同迁移的另一个分支,则会导致问题。

答案 6 :(得分:0)

如果真正丢失了迁移文件(例如,运行迁移,忘记回滚迁移,然后在提交之前删除了迁移文件),我就能够重现丢失的迁移,如下所示:

  1. 返回git历史记录以获取schema.rb文件的副本并保存在git repo之外(git log; git checkout xxxxxx; cp schema.rb ~/schema_old.rb, git checkout master)
  2. 对这两个文件运行diff,并将迁移命令复制到与缺少的迁移ID(diff schema.rb ~/schema_old.rb > migration_file.rb; vi migration_file.rb)匹配的迁移文件中
  3. 检查您的迁移状态和回滚(rake db:migrate:status; rake db:rollback; rake db:migrate:status;

答案 7 :(得分:0)

这是psql答案的替代版本,不会删除您的数据库或做任何疯狂的事情:

1)查找您的孤立迁移版本:

rails db:migrate:status

2)注意缺少的迁移的版本,并进入db控制台:

rails dbconsole

3)现在,手动从迁移表中删除版本:

从schema_migrations中删除,其中version ='[version_number]';

答案 8 :(得分:0)

一旦获得失败的rake db:migrate的版本号或rake db:migrate:status的NO FILE条目后,就可以使用一个Rails控制台衬套

 class SchemaMigration < ActiveRecord::Base; end; SchemaMigration.where(version: %i[the version numbers to delete]).delete_all

这意味着从终端可以

rails runner "class SchemaMigration < ActiveRecord::Base; end; SchemaMigration.where(version: %i[the version numbers to delete]).delete_all"

尽管如此,使用以前答案中的直接数据库命令之一可能会更快。

答案 9 :(得分:0)

这是我为此目的编写的一个耙任务。它调用db:migrate:status在内部使用的相同方法ActiveRecord::Base.connection.migration_context.migrations_status

# lib/tasks/cleanup_migration_entries.rake

desc 'Removes schema_migration entries for removed migration files'
task 'db:migrate:cleanup': :environment do
  migration_context = ActiveRecord::Base.connection.migration_context
  versions_to_delete =
    migration_context.migrations_status
                     .filter_map { |_status, version, name| version if name.include?('NO FILE') }

  migration_context.schema_migration.delete_by(version: versions_to_delete)

  puts "Cleaned up #{versions_to_delete.size} orphaned migrations."
end