GitブランチとRailsマイグレーションの操作方法


131

私はかなりの数のgitブランチを持つRailsアプリに取り組んでおり、それらの多くにはdbマイグレーションが含まれています。注意を払いますが、マスターのコードの一部が、別のブランチで削除/名前変更された列を要求することがあります。

  1. gitブランチをDB状態と「結合」するための良い解決策は何でしょうか?

  2. これらの「状態」は実際にはどうなりますか?

    サイズが数GBのデータベースを複製することはできません。

  3. そして、マージはどうなりますか?

  4. ソリューションはnoSQLデータベースにも変換されますか?

    現在、MySQL、mongodb、およびredisを使用しています


編集:私は非常に重要なポイントを言及するのを忘れたように見えます、私は開発環境にのみ興味がありますが、大規模なデータベース(サイズが数GB)があります。


他のブランチによってデータベースを変更できるマスターブランチを実行している環境があるとしたら、どうしますか?私はあなたのワークフローが何であるか、またはブランチを特定のデータベースと同期させる必要があると思う理由がわかりません。
ジョナ

3
データベースにクライアント(名前、電子メール、電話)があるテーブルがあり、ブランチで列の1つを分割するとします(name-> first_name + last_name)。ブランチをマスターとマージするまで、マスターとそれに基づく他のすべてのブランチは失敗します。
Kostas 2011年

回答:


64

ブランチに新しい移行を追加するときrake db:migrateは、移行実行の両方を実行してコミットします db/schema.rb

これを行うと、開発時に、移行の異なるセットを持つ別のブランチに切り替えて、単に実行することができますrake db:schema:load

これによりデータベース全体再作成され、既存のデータは失われます。

非常に注意している1つのブランチから本番環境を実行したいだけなので、これらの手順はそこでは適用されません(rake db:migrate通常どおりそこで実行するだけです)。しかし、開発では、スキーマからデータベースを再作成することは大したことでrake db:schema:loadはありません。


5
これはスキーマの問題を解決するだけだと思います。データが失われると、ダウンマイグレーションのたびに二度と見られなくなります。ブランチから移動するときに保存されるdb-data-patchと、別のブランチに移動するときにロードされる別のdb-data-patchを保存することは良い考えでしょうか?パッチには、途中で失われるデータ(移行)のみを含める必要があります。
Kostas

4
データをロードしたい場合は、db/seeds.rb そこに適切なシードデータを設定しておけば、開発DBを破壊するほど破壊的であってはなりません。
アンディリンデマン、2011年

核を破壊する必要はありません。以下の私の解決策を参照してください。インスタンスが多数あり、ブランチを切り替えるとデータがそこにないことに注意してください。テストで開発している場合、これはまったく問題ありません。
Adam Dymitruk、2011年

アンディありがとう、この回答も私の質問です。そしてdb/seeds.rb、失われたdbデータの再移植に使用することに同意します
pastullo 2014年

ローカルで実際のバグを再現する必要がある大きく複雑なアプリの場合、シードファイルを使用することは絶対に不可能です。本番(またはステージング)からの実際のデータが必要です。また、データベースの復元にはかなり時間がかかる可能性があるため、これは私の場合には適切な解決策ではありません。
Joel_Blum

21

簡単に再現できない大きなデータベースがある場合は、通常の移行ツールを使用することをお勧めします。単純なプロセスが必要な場合は、これがお勧めです。

  • ブランチを切り替えるrake db:rollback前に、ブランチポイントの前の状態にロールバック()します。次に、ブランチを切り替えた後、を実行しdb:migrateます。これは数学的には正しく、downスクリプトを記述している限り機能します。
  • ブランチを切り替える前にこれを行うのを忘れた場合は、通常、安全に元に戻してロールバックしてから再び切り替えることができるので、ワークフローとしては実現可能だと思います。
  • 異なるブランチのマイグレーション間に依存関係がある場合は、よく考えなければなりません。

2
すべての移行が元に戻せるわけではないことを覚えておく必要があります。つまり、最初に提案された手順が成功するとは限りません。開発環境では、@ noodlが言っていたようにrake db:schema:load、それを使用することをお勧めしますrake db:seed
pisaruk 2012

@pisarukこれは6年前に答えたと思いますが、不可逆的な移行の例が何であるかを知りたいと思います。状況を想像するのに苦労しています。最も単純なのは、大量のデータを含むドロップされた列だと思いますが、これを「逆」にして、空の列またはデフォルト値のある列を作成できます。他のケースを考えていましたか?
ルークグリフィス

1
あなたはあなた自身の質問に答えたと思います!はい、削除された列は良い例です。または破壊的なデータ移行。
ndp

13

これは、異なる移行を含むブランチ間を切り替えるために私が作成したスクリプトです。

https://gist.github.com/4076864

それはあなたが言及したすべての問題を解決するわけではありませんが、ブランチ名が与えられるとそれはします:

  1. 現在のブランチで、指定されたブランチに存在しないマイグレーションをロールバックします
  2. db / schema.rbファイルへの変更を破棄します
  3. 指定されたブランチをチェックしてください
  4. 指定されたブランチに存在する新しい移行を実行します
  5. テストデータベースを更新する

私は自分のプロジェクトでいつもこれを手動で行っているので、プロセスを自動化するといいと思いました。


1
このスクリプトは、まさに私がやりたいことを実行します。自動チェックアウトフックに入れられるのを見てみたいです。
brysgo 14

1
これだけで、私はあなたの要点を分岐し、それをチェックアウト後のフックにしました:gist.github.com/brysgo/9980344
brysgo 14

あなたの脚本で、あなたは本当に言うつもりでしたか、それともgit checkout db/schema.rbどういう意味git checkout -- db/schema.rbでしたか?(つまり、2つのダッシュを含む)
user664833 2014

1
ええと…当時はダブルダッシュについては知りませんでした。しかし、というブランチがない限り、コマンドは同じように機能しますdb/schema.rb。:)
Jon Lemmon 2014

@brysgoの進化したgit_railsコマンド(github.com/brysgo/git-rails)はうまく機能します。ジョンのおかげで:)
Zia Ul Rehman Mughal 2017

7

ブランチごとに個別のデータベース

それが飛行する唯一の方法です。

2017年10月16日更新

私はしばらくしてからこれに戻り、いくつかの改善を行いました。

  • 別の名前空間のrakeタスクを追加して、ブランチを作成し、を使用してデータベースを一挙に複製しましたbundle exec rake git:branch
  • マスターからのクローン作成が必ずしも望んでいることではないことがわかったので、db:clone_from_branchタスクがSOURCE_BRANCHTARGET_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

このタスクは、現在のブランチ(マスターなど)から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

これで、実行する必要があるのは、run bundle exec git:branch、新しいブランチ名を入力して、ゾンビの殺害を開始することだけです。


4

おそらく、これを、開発データベースが大きすぎるというヒントとしてとるべきでしょうか?db / seeds.rbとより小さなデータセットを開発に使用できる場合、現在のブランチのschema.rbとseeds.rbを使用することで問題を簡単に解決できます。

それはあなたの質問が開発に関連していることを前提としています。本番環境で定期的にブランチを切り替える必要がある理由は想像できません。


知らなかったのでdb/seeds.rb、調べてみます。
Kostas

3

私は同じ問題で苦労していました。これが私の解決策です:

  1. schema.rbとすべての移行の両方がすべての開発者によってチェックインされていることを確認してください。

  2. 本番環境への展開には、1人のユーザー/マシンが必要です。このマシンをマージマシンと呼びましょう。変更がマージマシンにプルされると、schema.rbの自動マージは失敗します。問題ない。内容をschema.rbの以前の内容で置き換えてください(コピーを脇に置いたり、使用する場合はgithubから取得したりできます...)。

  3. ここに重要なステップがあります。すべての開発者からの移行は、db / migrateフォルダーで利用できるようになります。先に進んでbundle exec rake db:migrateを実行してください。それは、すべての変更と同等のマージマシン上のデータベースをもたらします。また、schema.rbを再生成します。

  4. 変更をコミットして、すべてのリポジトリ(リモートでもあるリモートと個人)にプッシュします。あなたはやるべきです!


3

これは私がやったことであり、私がすべての基盤をカバーしたかどうかはよくわかりません:

開発中(postgresqlを使用):

  • sql_dump db_name> tmp / branch1.s​​ql
  • git checkout branch2
  • dropdb db_name
  • createdb db_name
  • psql db_name <tmp / branch2.sql#(前のブランチスイッチから)

これは、約50Kレコードのデータベースのrakeユーティリティよりもはるかに高速です。

本番環境では、マスターブランチをsacrosanctとして維持し、すべての移行をチェックインし、shema.rbを適切にマージします。標準のアップグレード手順を実行します。


データベースサイズが十分に小さく、ブランチをチェックアウトするときにこれをバックグラウンドで実行することは、非常に優れたソリューションのように見えます。
Kostas 2013年

2

ブランチごとに「db環境」を保持したい。別のインスタンスを指すようにsmudge / cleanスクリプトを確認します。dbインスタンスが不足した場合は、スクリプトで一時インスタンスをスピンオフして、新しいブランチに切り替えると、スクリプトがすでに存在しているため、スクリプトで名前を変更するだけで済みます。テストを実行する直前に、DB更新を実行する必要があります。

お役に立てれば。


このソリューションは、「一時的な」ブランチにのみ適しています。たとえば、ブランチ「エッジ」がある場合、あらゆる種類のクレイジーなものをテストし(おそらく他のサブブランチと一緒に)、それを時々マスターにマージすると、2つのデータベースがバラバラになります(これらのデータは同じである)。
Kostas

このソリューションは、正反対の場合に適しています。データベースバージョンスクリプトをバージョン管理する場合、これは非常に優れたソリューションです。
Adam Dymitruk、2011年

2

私はあなたがここで持っているピタを完全に体験します。私が考えると、本当の問題は、すべてのブランチに特定のブランチをロールバックするコードがないことです。私はジャンゴの世界にいるので、レーキがよくわかりません。私は、マイグレーションが分岐しない独自のリポジトリ(git-submodule、私が最近学んだ)に住んでいるという考えをもてあそんでいます。このようにして、すべてのブランチにすべての移行があります。厄介な部分は、各ブランチが関心のある移行のみに制限されるようにすることです。手動でそのことを追跡することは、ピタになり、エラーが発生しやすくなります。ただし、このために作成された移行ツールはありません。それが、私が進む道がないところです。


これはいいアイデアですが、ブランチが列の名前を変更するとどうなりますか?残りのブランチは壊れたテーブルを調べます。
Kostas

ええと-それは厄介な部分です-どのブランチがどのマイグレーションを気にするかです。「同期」すると、「この移行を元に戻す」ことがわかり、列が戻ります。
JohnO、2011

1

2つのオプションのいずれかをお勧めします。

オプション1

  1. データをに入れますseeds.rb。優れたオプションは、FactoryGirl / Fabrication gemを介してシードデータを作成することです。このようにして、列の追加/削除とともにファクトリーが更新されると想定した場合、データがコードと同期していることを保証できます。
  2. あるブランチから別のブランチに切り替えた後、を実行rake db:resetします。これにより、データベースが効果的に削除/作成/シードされます。

オプション2

手動で常に実行して、データベースの状態を維持rake db:rollback/ rake db:migrateブランチのチェックアウト後にする前に/。注意点は、すべての移行を元に戻す必要があることです。そうしないと、これは機能しません。


0

開発環境について:

を使用しrake db:migrate:redoて、スクリプトがリバーシブルかどうかをテストする必要がありますが、常にseed.rbデータの母集団を。

gitを使用している場合、seed.rbはマイグレーションの変更で変更する必要があります。 db:migrate:redo(他のマシンまたは新しいデータベースでの新しい開発のデータをロードします)

「変更」とは別に、アップとダウンのメソッドでは、コードは常に、この瞬間とゼロから始まる「変更」のシナリオをカバーします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.