Finding Rails migrations

Scrabble pieces arranged to spell S-E-A-R-C-H signifying the search for Rails migrations across branches.

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:

   up     20180301163041  Add chocolate chips to the cookies
   up     20180302132931  ********** NO FILE **********
   up     20180302161231  Remove the egg from the recipe
   up     20180306171402  Change flour to almond flour

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 out upstream/master, but I sync my local copy of master 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 called g and aliased git-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?


  1. You always make sure your migrations can be rolled back, don’t you?