What is the best way to resolve Rails orphaned migrations?

Ruby on-RailsRuby on-Rails-3GitMigrationRails Migrations

Ruby on-Rails Problem Overview


I have been switching between branches in a project and each of them have different migrations... This is the scenario:

> $ 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 **********

The problem is rake db:rollback don't work at all because of the missing files...

What should I do to be able to rollback again and get rid of the NO FILE messages?

Btw, rake db:reset or rake db:drop are not an option, I cannot lose data from other tables...

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

I ended up solving the problem like this:

(1) Go to the branches that has the migration files and roll them back. This is not trivial when you have many branches which are will result in many conflicts if you try to merge them. So I use this commands to find out the branches of each orphan migration belongs to.

So, I need to find commit of the last time the migration was modified.

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

I take the commit hash and determine which branch it belongs like this:

git branch --contains <COMMIT_HASH>

Then I can go back to that branch, do a rollback and repeat this process for all the missing files.

(2) Run migrations: checkout the branch you finally want to work on and run the migrations and you should be good to go.

Troubleshooting

I also ran in some cases where orphaned migrations where on deleted branches.

To solve this I created dummy migration files with the same migration_id of the missing files and roll them back. After that, I was able to delete they dummy migrations and have a clean migration status :)

Another alternative is deleting the missing files from the database directly:

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

Solution 2 - Ruby on-Rails

Migrations are stored in your database. If you want to remove the abandoned migrations, remove them from the db.

Example for Postgres:

  1. Open psql:

    psql
    
  2. Connect to your db:

    \c your_database
    
  3. If you're curious, display schema_migrations:

    SELECT * FROM schema_migrations;
    
  4. If you're curious, check if the abandoned migrations are present:

    SELECT version FROM schema_migrations WHERE version IN 
    ('20130320144219', '20130320161939', '20130320184628', '20130403190042',
     '20130403195300', '20130403214000', '20130410194222');
    
  5. Delete them:

    DELETE FROM schema_migrations WHERE version IN (<version list as above>);
    

Now if you run bundle exec rake db:migrate:status, you'll see the orphaned migrations have been successfully removed.

Solution 3 - Ruby on-Rails

Here is a rake version of the psql answer from @medik, which won't erase your db or do anything crazy:

  1. Find your orphaned migration versions:
rails db:migrate:status
  1. Note the versions of the missing migrations and head into the db console:
rails dbconsole
  1. Now remove the versions from the migration table manually:
delete from schema_migrations where version='[version_number]';

Solution 4 - Ruby on-Rails

Edit: THE FOLLOWING WILL DROP YOUR DATABASE

A simpler approach that has worked for me (note that this command will drop the database and all your data will be lost):

rake db:migrate:reset

..and then:

rake db:migrate:status

The orphan(s) should disappear.

Solution 5 - Ruby on-Rails

create new files with names like 20130320144219_migration_1 put some blank code into

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

and run command rails db:migrate:down VERSION= 20130320144219 and at last - remove this files

Solution 6 - Ruby on-Rails

Here's a rake task I wrote for this purpose. It invokes the same method that the db:migrate:status uses under the hood, 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

Solution 7 - Ruby on-Rails

Assuming that you are using Git, it should be relatively simple to grab these migrations and bring them into your current branch. If you have a specific commit you want a file from, you can use:

git checkout <commit hash> <file_name>

(Thanks to this answer)

Alternatively, you can check out from a specific branch HEAD:

git checkout <branch name> -- <file_name>

According to this blog post

Assuming these are, in fact, the versions of the migrations run on the database, you should be good to rollback.

Solution 8 - Ruby on-Rails

You could merge the two branches back into the master so that you have all migrations available. If you really don't want those migrations there, but want to be able to roll back, you could edit the schema_migrations table in your database to remove the rows corresponding to the migrations for which you don't have files. However, this will cause problems if you then switch to another branch with different migrations.

Solution 9 - Ruby on-Rails

If the migration files are truly missing (e.g. ran migration, forgot to roll back migration, then deleted migration file before commit), I was able to reproduce the missing migration as follows:

  1. go back in git history to get a copy of the schema.rb file and save outside of git repo (git log; git checkout xxxxxx; cp schema.rb ~/schema_old.rb, git checkout master).
  2. run a diff on the two files, and copy the migration commands into a migration file that matches the missing migration ID (diff schema.rb ~/schema_old.rb > migration_file.rb; vi migration_file.rb)
  3. Check your migration status and rollback (rake db:migrate:status; rake db:rollback; rake db:migrate:status;)

Solution 10 - Ruby on-Rails

A one liner for Rails console once you have the version numbers from failed rake db:migrate or NO FILE entries from rake db:migrate:status

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

Which means, from terminal, you can

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

Though, at that point, it might be faster to use one of the direct database commands from previous answers.

Solution 11 - Ruby on-Rails

I have created Migration for that.

class DeleteOrphanedMigrationFile < ActiveRecord::Migration[6.0]

  def up
    db_connection = ActiveRecord::Base.connection
    migration_context = ActiveRecord::Base.connection.migration_context
    removed_file_versions = migration_context.migrations_status.filter_map { |_status, version, name| version if name.include?('NO FILE') }

    removed_file_versions.each do |version|
      sql = "delete from schema_migrations where version=#{version};"
      db_connection.execute(sql)
    end
  end

  def down; end

end

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAdrianView Question on Stackoverflow
Solution 1 - Ruby on-RailsAdrianView Answer on Stackoverflow
Solution 2 - Ruby on-RailsmedikView Answer on Stackoverflow
Solution 3 - Ruby on-RailsstackPusherView Answer on Stackoverflow
Solution 4 - Ruby on-RailsspandataView Answer on Stackoverflow
Solution 5 - Ruby on-RailsEvseev VadimView Answer on Stackoverflow
Solution 6 - Ruby on-RailsJaredView Answer on Stackoverflow
Solution 7 - Ruby on-RailsmikeryzView Answer on Stackoverflow
Solution 8 - Ruby on-RailsWhit KemmeyView Answer on Stackoverflow
Solution 9 - Ruby on-RailsTaylored Web SitesView Answer on Stackoverflow
Solution 10 - Ruby on-RailsChad MView Answer on Stackoverflow
Solution 11 - Ruby on-RailsJin LimView Answer on Stackoverflow