如何使用Git分支和Rails迁移

我正在一个Rait应用程序与很多的git分支,其中许多包括数据库迁移。 我们尽量小心,但偶尔会有一段代码要求在另一个分支中删除/重命名的列。

  1. 什么是一个很好的解决scheme,以“耦合”与DB状态的git分支?

  2. 这些“国家”实际上会是什么?

    如果数据库大小只有几GB,我们不能复制一个数据库。

  3. 合并会发生什么?

  4. 解决scheme是否也转换为noSQL数据库?

    我们目前使用MySQL,mongodb和redis


编辑:看起来像我忘了提到一个非常重要的一点,我只对开发环境感兴趣,但与大型数据库(几GB的大小)。

在任何分支中添加新迁移时,请运行rake db:migrate并提交migration db/schema.rb

如果你这样做,在开发中,你将能够切换到另一个具有不同迁移集的分支,并简单地运行rake db:schema:load

请注意,这将重新创build整个数据库,现有的数据将丢失

你可能只想从一个你非常小心的分支上运行生产,所以这些步骤不适用于那里(只需运行rake db:migrate像往常一样rake db:migrate )。 但是在开发过程中,从架构重新创build数据库应该没有什么大不了的,这就是rake db:schema:load会做的事情。

如果你有一个大型的数据库,你不能很容易地复制,那么我build议使用正常的迁移工具。 如果你想要一个简单的过程,这是我build议:

  • 在切换分支之前,回滚( rake db:rollback )到分支点之前的状态。 然后,在切换分支之后,运行db:migrate 。 这在math上是正确的,只要你写下脚本,它就可以工作。
  • 如果在切换分支之前忘了这样做,一般情况下,您可以安全地切回,回滚并再次切换,所以我认为这是一个工作stream程,这是可行的。
  • 如果你在不同分支的迁移之间有依赖关系,那么你就必须认真思考。

以下是我为在包含不同迁移的分支之间切换而编写的脚本:

https://gist.github.com/4076864

它不会解决你所提到的所有问题,但给一个分支的名称会:

  1. 回滚当前分支上给定分支上不存在的任何迁移
  2. 放弃对db / schema.rb文件的任何更改
  3. 查看给定的分支
  4. 运行给定分支中存在的任何新的迁移
  5. 更新你的testing数据库

我发现自己一直在我们的项目上手动执行这个操作,所以我认为自动化这个过程会很好。

也许你应该把这个看成是你的开发数据库太大了? 如果你可以使用db / seeds.rb和一个较小的数据集进行开发,那么通过使用当前分支的schema.rb和seeds.rb可以很容易地解决你的问题。

这假设你的问题涉及发展; 我无法想象为什么你需要定期改变分支机构的生产。

我在同一个问题上挣扎。 这是我的解决scheme:

  1. 确保所有开发人员签入schema.rb和所有迁移。

  2. 应该有一个人/机器部署到生产。 我们称这台机器为合并机器。 当更改被拉到合并机器时,schema.rb的自动合并将失败。 没有问题。 只要用schema.rb以前的内容replace内容就可以了(如果你使用它,你可以放一个副本或者从github中获取它)。

  3. 这是重要的一步。 现在,所有开发人员的迁移将在db / migrate文件夹中提供。 继续运行bundle exec rake db:migrate。 它将把所有的变化与合并机器上的数据库相提并论。 它也将重新生成schema.rb。

  4. 提交并推送到所有存储库(遥控器和个人,也是遥控器)的变化。 你应该做的!

这就是我所做的,我不太清楚我是否覆盖了所有的基础:

在开发中(使用postgresql):

  • sql_dump db_name> tmp / branch1.s​​ql
  • git checkout branch2
  • dropdb db_name
  • createdb db_name
  • psql db_name <tmp / branch2.sql#(来自以前的分支交换机)

这比数据库上的rake实用程序快得多,大约有50K条logging。

为了生产,维护主分支作为神圣不可侵犯的,并且所有迁移被签入,shema.rb被适当地合并。 通过您的标准升级程序。

你想保留每个分支的“数据库环境”。 看看污迹/干净的脚本指向不同的实例。 如果用完数据库实例,让脚本分离一个临时实例,所以当你切换到一个新的分支时,它已经在那里,只需要脚本重命名。 数据库更新应该在执行testing之前运行。

希望这可以帮助。

我完全体验了你在这里的皮塔。 正如我想的那样,真正的问题是所有分支都没有回滚某些分支的代码。 我在Django的世界,所以我不知道耙得那么好。 我玩弄的想法是,迁移生活在自己的回购没有分支(我最近了解到的git-submodule)。 这样所有的分支都有所有的迁移。 棘手的部分是确保每个分支仅限于他们关心的迁移。 手动做/保持跟踪将是一个皮塔饼,容易出错。 但是没有一个迁移工具是为此而构build的。 那就是我没有前进的方向。

独立的数据库为每个分支

这是飞行的唯一方法。

2017年10月16日更新

经过一段时间,我回到了这里,并做了一些改进:

  • 我已经添加了另一个命名空间rake任务来创build一个分支,并用bundle exec rake git:branch一举克隆数据库。
  • 我现在意识到,从主人克隆并不总是你想要做的,所以我更明确地指出db:clone_from_branch任务需要一个SOURCE_BRANCH和一个TARGET_BRANCH环境variables。 当使用git:branch ,它将自动使用当前分支作为SOURCE_BRANCH
  • 重构和简化。

config/database.yml

为了使您更容易,下面介绍如何更新database.yml文件,以根据当前分支dynamic确定数据库名称。

 <% database_prefix = 'your_app_name' environments = %W( development test ) current_branch = `git status | head -1`.to_s.gsub('On branch ','').chomp %> defaults: &defaults pool: 5 adapter: mysql2 encoding: utf8 reconnect: false username: root password: host: localhost <% environments.each do |environment| %> <%= environment %>: <<: *defaults database: <%= [ database_prefix, current_branch, environment ].join('_') %> <% end %> 

lib/tasks/db.rake

这是一个Rake任务,可以轻松地将数据库从一个分支克隆到另一个分支。 这需要一个SOURCE_BRANCH和一个TARGET_BRANCH环境variables。 基于@ spalladino的任务。

 namespace :db do desc "Clones database from another branch as specified by `SOURCE_BRANCH` and `TARGET_BRANCH` env params." task :clone_from_branch do abort "You need to provide a SOURCE_BRANCH to clone from as an environment variable." if ENV['SOURCE_BRANCH'].blank? abort "You need to provide a TARGET_BRANCH to clone to as an environment variable." if ENV['TARGET_BRANCH'].blank? database_configuration = Rails.configuration.database_configuration[Rails.env] current_database_name = database_configuration["database"] source_db = current_database_name.sub(CURRENT_BRANCH, ENV['SOURCE_BRANCH']) target_db = current_database_name.sub(CURRENT_BRANCH, ENV['TARGET_BRANCH']) mysql_opts = "-u #{database_configuration['username']} " mysql_opts << "--password=\"#{database_configuration['password']}\" " if database_configuration['password'].presence `mysqlshow #{mysql_opts} | grep "#{source_db}"` raise "Source database #{source_db} not found" if $?.to_i != 0 `mysqlshow #{mysql_opts} | grep "#{target_db}"` raise "Target database #{target_db} already exists" 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}` end end 

lib/tasks/git.rake

这个任务将创build一个从当前分支(master,否则)的git分支,检查出来并克隆当前分支的数据库到新分支的数据库中。 这是光滑的自动对焦。

 namespace :git do desc "Create a branch off the current branch and clone the current branch's database." task :branch do print 'New Branch Name: ' new_branch_name = STDIN.gets.strip CURRENT_BRANCH = `git status | head -1`.to_s.gsub('On branch ','').chomp say "Creating new branch and checking it out..." sh "git co -b #{new_branch_name}" say "Cloning database from #{CURRENT_BRANCH}..." ENV['SOURCE_BRANCH'] = CURRENT_BRANCH # Set source to be the current branch for clone_from_branch task. ENV['TARGET_BRANCH'] = new_branch_name Rake::Task['db:clone_from_branch'].invoke say "All done!" end end 

现在,你需要做的就是运行bundle exec git:branch ,input新的分支名称并开始杀死僵尸。

在开发环境上:

您应该使用rake db:migrate:redo来testing您的脚本是否可逆,但请记住总是应该有一个具有数据填充的seed.rb

如果你使用git,seed.rb应该随着迁移改变而改变,并且执行db:migrate:redo来开始(在其他机器或新的数据库上加载一个新开发的数据)

除了“变化”之外,用你自己的方法,你的代码总是在这个时刻和从零开始的时候变成“变化”的覆盖场景。

我会build议两种select之一:

选项1

  1. 把你的数据在seeds.rb 。 一个不错的select是通过FactoryGirl / Fabrication创build你的种子数据。 这样,如果我们假设工厂与更新/删除列一起更新,则可以保证数据与代码同步。
  2. 从一个分支切换到另一个分支后,运行rake db:reset ,这将有效地删除/创build/种子数据库。

选项2

通过始终运行rake db:rollback手动维护数据库的状态rake db:rollback / rake db:migrate在分支签出之前/之后进行rake db:migrate 。 需要注意的是,你所有的迁移都需要是可逆的,否则这是行不通的。