Sometimes, I find myself working across several branches helping different teammates with their stories. Often, these branches use Rails migrations to easily manage the database changes that need to be made to support the work. This means that I end up running migrations from many different branches. Because I am human, I often forget to roll those migrations back before switching branches. When a destructive migration has to happen as part of a branch, it leaves me in a situation where I have several migrations that I need to roll back at any given time. In addition, they might be interleaved with migrations that exist on the master
branch if it’s a longer-running branch.
Missing Rails migrations
When I have database migrations that I want to track across branches, I use rails db:migrate:status
to see what the status is for every migration that exists on my current branch. However, when I have run migrations that haven’t been merged into the master
branch, that leaves me with output that looks like this:
I find myself wondering, “where did migration 201803021132931 come from? Is it something I need to roll back for my current task?” When this happens, I need a way to figure out what the missing migrations do in order to track what work I need to do when I switch contexts. To solve this problem, I created a small Git extension, called git-find-migration
.
New workflow
My workflow now for tracking these migrations across branches is as follows:
- I change to the
upstream/master
branch on Git. This gives me the context for the current state of what is running in production (n.b. I don’t actually check outupstream/master
, but I sync my local copy ofmaster
with it). - Then, I run
g fm -l
to list any migrations that have been run, but I am missing on the branch (n.b. I use a function calledg
and aliasedgit-find-migration
to fm to make this easier to type). - For each migration that I am missing, I copy the timestamp, then run
g fm <timestamp>
to see the name of the file in my Git repository. - Once I know the name of the file, I run
git log --all -- db/migration/<migration_file>
to see the context for that file.
This workflow allows me to easily make decisions to handle the migration. Sometimes, the migration is an old one that has since been pruned from the repository (not a suggested tactic, might I add!) and it’s fine. Sometimes, it’s something that I should roll back to continue my work. In that case, I check out the branch with the migration and run a rails db:migrate:down VERSION=<timestamp>
to roll it back1.
Shortcomings
I could likely still make some improvements to this workflow. For example, I currently limit my script to searching for only a single timestamp at once. This was because I didn’t want to figure out how to write the Git query that could find more than one. Perhaps that would speed along the process when I have several migrations that I need to roll back. This happens a lot when we’re working on a multistage, backwards-compatible, destructive change.
Even with these shortcomings, the new workflow serves me well. Feel free to download the script if this sounds interesting! Do you have any techniques for managing migrations across multiple branches?
-
You always make sure your migrations can be rolled back, don’t you? ↩