如何使用 Git 分支和 Rails 迁移

我正在开发一个 Rails 应用程序,它有很多 git 分支,其中很多都包含 db 迁移。我们尽量小心,但是有时候 master 中的一些代码会要求在另一个分支中删除/重命名一个列。

  1. 将 git 分支与 DB 状态“耦合”的好解决方案是什么?

  2. 这些“状态”到底是什么?

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

  3. 合并后会发生什么?

  4. 这个解决方案也能转换成 noSQL 数据库吗?

    我们目前使用的是 MySQL、 mongodb 和 redis


编辑: 看起来我忘了提到一个非常重要的问题,我只对 发展环境感兴趣,但是它有很大的数据库(几个 GB 的大小)。

25636 次浏览

在任何分支中添加新的迁移时,运行 rake db:migrate并提交两个迁移 还有db/schema.rb

如果这样做,在开发过程中,您将能够切换到另一个具有不同迁移集的分支,只需运行 rake db:schema:load

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

您可能只想在一个分支上运行生产,因为您对这个分支非常谨慎,所以这些步骤不适用于该分支(只需在该分支上像往常一样运行 rake db:migrate)。但是在开发中,从模式重新创建数据库应该没有什么大不了的,这就是 rake db:schema:load将要做的。

您希望为每个分支保留一个“ db 环境”。查看 smudge/clean 脚本以指向不同的实例。如果用完了 db 实例,让脚本分离一个临时实例,这样当您切换到一个新的分支时,它已经在那里了,只需要通过脚本重命名。DB 更新应该在执行测试之前运行。

希望这个能帮上忙。

如果您有一个很大的数据库,不能很容易地复制,那么我建议使用普通的迁移工具。如果你想要一个简单的过程,我建议你这样做:

  • 在切换分支之前,回滚(rake db:rollback)到分支点之前的状态。然后,在切换分支之后,运行 db:migrate。这在数学上是正确的,只要您编写 down脚本,它就会工作。
  • 如果您在切换分支之前忘记这样做,通常您可以安全地切换回来、回滚和再次切换,所以我认为作为一个工作流,这是可行的。
  • 如果在不同分支的迁移之间存在依赖关系... ... 那么,您必须仔细考虑。

我完全体验到了你在这里吃的皮塔饼。我认为,真正的问题是所有的分支都没有回滚特定分支的代码。我在姜戈的世界里,所以我不太了解耙子。我正在考虑这样一个想法,即迁移存在于它们自己的 repo 中,而不会被分支(我最近了解到的 git-submodule)。这样所有的分支都有所有的迁移。棘手的部分是确保每个分支仅限于它们关心的迁移。手动执行/保持跟踪将是一个小问题,而且很容易出错。但是没有一个迁移工具是为此而构建的。这就是我无路可走的时候。

也许您应该将此作为您的开发数据库太大的暗示?如果您可以使用 db/Seeds.rb 和一个更小的数据集进行开发,那么您的问题就可以通过使用当前分支中的 schema.rb 和 Seeds.rb 轻松解决。

这假设您的问题与开发相关; 我无法想象为什么需要定期切换生产中的分支。

我也在为同样的问题苦苦挣扎,以下是我的解决办法:

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

  2. 应该有一个用于部署到生产环境的人员/机器。让我们把这个机器称为合并机器。当更改被拉到合并计算机时,schema.rb 的自动合并将失败。没问题。只需将内容替换为 schema.rb 之前的内容即可(如果使用的话,可以将副本放在一边或从 github 获取...)。

  3. 这是重要的一步。来自所有开发人员的迁移现在都可以在数据库/迁移文件夹中找到。继续运行 bundle exec rake db: shift。它将使合并机器上的数据库与所有更改保持一致。它还将重新生成 schema.rb。

  4. 提交并将更改推送到所有存储库(远程存储库和个人存储库,也是远程存储库)。你应该完蛋了!

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

Https://gist.github.com/4076864

它不能解决你提到的所有问题,但是给出一个分支名称就可以了:

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

在我们的项目中,我发现自己一直在手动做这件事,所以我认为自动化这个过程会很好。

这就是我所做的,我不确定我是否已经涵盖了所有的基础:

在开发中(使用 postresql) :

  • Sql _ dump db _ name > tmp/branch1.sql
  • Git 结帐分支2
  • Dropdb db _ name
  • Createdb db _ name
  • Psql db _ name < tmp/branch2.sql # (来自上一个分支切换)

这比具有大约50K 记录的数据库上的 rake 实用程序快得多。

对于生产环境,将 master 分支维护为神圣不可侵犯的,所有的迁移都会被签入,shema.rb 被正确地合并。进行标准升级程序。

关于发展环境:

您应该使用 rake db:migrate:redo来测试脚本是否可逆,但是请记住,应该始终使用带有数据填充的 seed.rb

如果您使用 git,那么您的 Seed.rb 应该随着迁移的更改而更改,并且从头开始执行 db:migrate:redo(为其他机器或新数据库上的新开发加载数据)

除了更改之外,随着您的向上和向下的方法,您的代码总是涵盖此刻的“更改”和从零开始的“更改”场景。

每个分支独立的数据库

这是飞行的唯一方法。

更新于2017年10月16日

过了一段时间,我又回到了这个问题上,并做了一些改进:

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

config/database.yml

为了简化操作,下面介绍如何更新 database.yml文件,以根据当前分支动态确定数据库名称。

<%
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环境变量。基于 @ 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

这个任务将在当前分支(master 或其他)之外创建一个 git 分支,签出它并将当前分支的数据库克隆到新分支的数据库中。这是狡猾的 AF。

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,输入新的分支名称并开始杀死僵尸。

我有两个建议:

选择一

  1. 把你的数据输入 seeds.rb。一个不错的选择是通过 FactoryGirl/Fabricationgem 创建种子数据。这样,如果我们假设工厂随着列的添加/删除一起更新,就可以保证数据与代码同步。
  2. 在从一个分支切换到另一个分支之后,运行 rake db:reset,它有效地删除/创建/种子数据库。

选择二

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

如果使用 git pull,那么您应该已经拥有了最新的模式,该模式受到通过 pull 进行的任何迁移的影响,但是您的数据库表可能不会更新

因此,您确实需要在提取之后运行迁移,但是这通常会更改 db/schema.rb

如果您所做的只是提取和迁移,那么您没有理由负责提交任何结果模式更改,因为它们在技术上不属于您,并且它们可能最终是无关的/不正确的

重置模式 diff 是最有意义的

下面是我在创建新分支之前要做的事情的逐步版本

  • 切换到父分支/基分支
  • 调出最新密码
  • 运行 bundle exec rake db:migrate以在本地更新 schema.rb文件
  • 执行 git checkout db/schema.rb以抛弃由 db:migrate带来的更改(如果有的话)
  • 创建您的新分支并切换到它
  • 确保在切换到另一个分支之前提交您的更改

改编自 给你