Switching Rails database per git branch

Working with the git-flow pattern in an application involves a lot of switching back and forth along several branches. Whether it is implementing a new feature in a dedicated branch, applying a hotfix to a deployed version, or making the final touches to a release candidate, it is most likely that these switches involve moving between different schema versions. This implies that when you switch back to an older version, your current development database might have changes that were not present in that version, which you’d need to roll back.

Since setting up a development database with some testing data is a process that might take some time, even if you have fully automated it, destroying the database and recreating from scratch is not a very appealing option. Another option, running db:rollback to the target version, can be cumbersome, and will probable cause data loss that you’ll need to recreate when switching back to the most recent versions.

A better option is to keep one database per branch, so each branch has its own copy of the development schema and data. To accomplish this, an easy way is to modify your database.yml to look for a database based on the branch name:

require 'config'
branch = `git rev-parse --abbrev-ref HEAD`.strip rescue nil
use_single_db = !Settings.db_per_branch || !branch || branch == 'master'
branch_spec = (use_single_db ? "" : "_#{branch}").underscore.gsub(/[\.\/\-]/, '_')

<<: *default
database: app_development<%= branch_spec %>

Note that here we’re turning on this feature only if a local setting is set, so this feature is actually opt-in. You can also use environment variables or git config variables.

In order to easily set up the branch database, we added a rake task that automatically copies the current development database into the branch database if it doesn’t exist:

namespace :db do

desc "Copies master development database, or specified by SOURCE env param, into current branch name only if it does not exist"
task :clone do
config = Rails.application.config.database_configuration[Rails.env]
source_db = ENV['SOURCE'].presence || 'app_development'
target_db = config['database']
mysql_opts = "-u #{config['username']} "
mysql_opts << "--password=\"#{config['password']}\" " if config['password'].presence

`mysqlshow #{mysql_opts} #{target_db.gsub('_', '\\\\\_')}`
raise "Target database #{target_db} already exists" if $?.to_i == 0

`mysqlshow #{mysql_opts} #{source_db.gsub('_', '\\\\\_')}`
raise "Source database #{source_db} not found" if $?.to_i != 0

puts "Creating empty database #{target_db}"
`mysql #{mysql_opts} -e "CREATE DATABASE #{target_db}"`

puts "Copying #{source_db} into #{target_db}"
`mysqldump #{mysql_opts} #{source_db} | mysql #{mysql_opts} #{target_db}`


Thus, by running rake db:clone after switching to a new branch, the branch database is automatically created based on the current master development database; you can also specify a different SOURCE database depending on where you’re branching from. You can also automate this by adding a git hook to perform this task every time you switch branches, if you find yourself forgetting a little too often to recreate the database branch.

There are multiple alternatives to this approach, such as keeping the database data in dump file and always using the app_development database (see here). Still, this approach has been working great for us so far. Let us know which works best for you and your team!

Want to say anything? Reach @manastech on Twitter!

Like what you read? Give ManasTech a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.