Merging migrations
In Rails, the file db/structure.sql
or db/schema.rb
(depending)
captures a snapshot of the state of the development database after a
migration, when you run the db:migrate
or db:schema:dump
tasks.
What do you do when you have migrations in two different branches of your development tree? Do you get a merge conflict? How do you resolve it? What if you don’t get a merge conflict?
I’ll use the term “schema” now to refer to the file
(db/structure.sql
or db/schema.rb
).
It is the file that captures the structure of the database.
Because the schema is generated by a program, not hand written, the best
effort of git
to try to merge two changes to it will often produce a
result different than what you will get from running the db:schema:dump
.
Why is that important?
- It might have duplications or content out of sequence.
- The next migration will produce a schema with differences unrelated to the migration.
- The schema is the only source of truth about the database structure. It deserves extra care, to keep it aligned with what the framework and tooling expect and produce.
Here’s a process that allows the tooling to keep the schema in order, combining independent migrations without errors induced by merging.
First, do not allow git to merge the file. Set-up git to always treat
independent changes to the schema as a conflict. You do this by placing
a .gitattributes
file in the db
directory with content,
schema.rb -merge
structure.sql -merge
(Or add those lines to the existing .gitattributes
file if there is one.)
The reference for doing that is deep down in the documentation of
gitattributes.
Second, when you do get a conflict, resolve it as follows:
If your database migrations come after those you picked-up in the merge:
- Abort the merge
git merge --abort
- Roll back your migrations, however many,
rails db:rollback
- Pull the changes a second time
In either case,
- Run the migrations picked-up in the merge,
rails db:migrate
- Accept the newly generated schema, e.g.
git add db/structure.sql
- Make that merge commit,
git commit
In this way, your database schema will always be wholly in agreement with the text wanted by the framework, because it is always and only ever generated by the framework.