UPDATED: July 4, 2018

As a Rails developer, you'll often run into conflicts with your schema.rb file when merging or rebasing. Usually it's your teammates introducing other schema changes after you generated your migration(s). Or, you may have unintentionally included unrelated schema changes when working on multiple branches, each with migrations that you've run on your development database.

If you're reading this, you've probably discovered that simply rake db:migrateing won't always produce the correct schema.rb output.

Understand this file is generated from the current state of your database. Never attempt to manually resolve conflicts -- even if they are simple, you'll inevitably wind up with a tiny difference, such as a whitespace or newline diff.

As such, to properly resolve a conflict, try these two techniques.

First, some general tips

Use your test database - I highly recommend using your test database to work through correcting schema.rb because it's fast (no data in it) and you won't wipe out your development data. Your development database can have any conglomeration of migrations while your test database remains pristine or close to it.

Always make your migrations reversible - Writing your down methods will make for the easiest conflict resolution and greatly assists in the development of migrations. Even if the complexity of reproducing the original data is too great, putting back the original columns is useful for development purposes.

Isolate your migrations from the rest of your app - You will be surprised when something you referenced from a migration becomes unavailable. Trust me. Cut all dependency from your migrations -- raw sql if possible.

Prefer merge over rebase, at a point - If you have a lot of commits that change schema.rb in your branch, simply merge your base into your branch rather than rebasing. Rebasing would require you to regenerate the file for each commit, whereas merging just requires one effort.

Method #1 - Try migrating

This works if your database (test or development) has no extraneous changes.

  1. Roll back any of your new migrations, if they aren't already:

    rake db:rollback
    

    OR

    rake db:migrate:down VERSION=(stamp)
    
  2. Migrate them up again to recreate your schema.rb:

    rake db:migrate
    
  3. Inspect the differences that your branch is now introducing:

    git diff master... db/schema.rb
    
  4. If you see changes to the schema that you don't want to keep or aren't part of someone else's merged branch, proceed to method #2.

Method #2 - Rebuild whole database

This removes any extraneous changes from your schema.rb via restoring your test database to a clean state, and reapplying your migrations.

  1. Drop your test database to nuke your schema_migrations table. This is required beacuse rake db:setup doesn't restore the schema_migrations table to a clean state. If you don't do this first, that effectively "force" rolls your migrations back, causing much cussing when trying to re-migrate. Rails still thinks the migrations are up, since the migration versions remain in schema_migrations.

    RAILS_ENV=test rake db:drop
    
  2. Check out an unmodified schema.rb file from your branch's base (probably master):

    git checkout master -- db/schema.rb
    
  3. Rebuild your database from schema.rb:

    RAILS_ENV=test bin/rake db:setup
    
  4. Re-run your new migration:

    RAILS_ENV=test rake db:migrate
    

Migration design can be a serious beast. Another way of thinking about this problem is that you need to re-apply your schema changes in the same way that you'd re-apply your code changes when running into merge conflicts. You're taking the new database blueprint and re-running your change to it. Starting with that new blueprint, however, can be a little squirrely.

More blog posts