Resolving Rails schema.rb conflicts properly
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:migrate
ing 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.
Roll back any of your new migrations, if they aren't already:
rake db:rollback
OR
rake db:migrate:down VERSION=(stamp)
Migrate them up again to recreate your
schema.rb
:rake db:migrate
Inspect the differences that your branch is now introducing:
git diff master... db/schema.rb
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.
Drop your test database to nuke your
schema_migrations
table. This is required beacuserake db:setup
doesn't restore theschema_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 inschema_migrations
.RAILS_ENV=test rake db:drop
Check out an unmodified
schema.rb
file from your branch's base (probably master):git checkout master -- db/schema.rb
Rebuild your database from
schema.rb
:RAILS_ENV=test bin/rake db:setup
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.