マイグレーションのバージョン管理

過去の経緯

Rails 2.1 以前のバージョンでは、マイグレーションファイルは「001_create_hoge.rb」のように単純な連番で管理されており、現在のマイグレーション番号については schema_info というテーブルで最後に実行された数値だけを管理していました。
そのため、リポジトリ等を用いて複数の開発者が作業を行う場合、簡単にバージョンの衝突が発生しました(例えば現在のバージョンが「001」の時、二人の作業者がそれぞれマイグレーションファイルを作成すると、「002」のファイルがふたつ作成されるため、マイグレーションは正しく動作しなくなります)。
このような経緯から、現在の Rails では別の方法でマイグレーションの管理を行っています。

現在のバージョン管理

では、現在のマイグレーションファイルはどのように管理されているのか。
まずネーミングですが「20130603012345_create_hoge.rb」のようにタイムスタンプを用いるように変更されています。秒数まで同じタイミングでマイグレーションが作成された場合は相変わらず衝突しますが、以前の数字を用いる方式と比べればずっと可能性は低くなっています。
さらに、バージョン管理についても schema_migrations というテーブルを用いて、実行されたマイグレーションのファイル名に記載されたタイムスタンプを、ユニークキーとして管理するようになっています。

schema_migrations
20120104093151
20120104093154

また、マイグレーション実行時には schema_migrations テーブルに含まれないバージョンすべてのマイグレーションが実行されるようになったため、更新を忘れるケースも大幅に減少しています。

ちなみに、過去のバージョン管理を使いたい場合 config/application.rb で次のように記載すればいいようです。

config.active_record.timestamped_migrations = false

上で述べたようにデメリットの多い方式であるため、できる限り使わない方がいい事は重ねて述べておきます。

その他いろいろ

先ほど述べたとおり、マイグレーションで実行されるのは schema_migrations テーブルに含まれないタイムスタンプが指定されたファイルのため、ファイル名を変更すれば同じマイグレーションを2回実行する事も可能です。

一方で、同じタイムスタンプのマイグレーションファイルが2つ以上存在すると、マイグレーション実行時にエラーとなり、コマンドは破棄されます(トランザクション処理になっているため、更新すべてが巻き戻ります)。

rails g model hoge name:string
      create    db/migrate/20130603075832_create_hoges.rb
rails g model fuga name:string
      create    db/migrate/20130603075838_create_fugas.rb

mv db/migrate/20130603075838_create_fugas.rb db/migrate/20130603075832_create_fugas.rb

bundle exec rake db:migrate
rake aborted!
Multiple migrations have the version number 20130603075832

また、マイグレーションファイルには未来のタイムスタンプを入れる事も可能です。

mv db/migrate/20130603075832_create_fugas.rb db/21000603075832_create_hoges.rb

bundle exec rake db:migrate
==  CreateFugas: migrating ====================================================
-- create_table(:fugas)
   -> 0.0133s
==  CreateFugas: migrated (0.0134s) ===========================================

==  CreateHoges: migrating ====================================================
-- create_table(:hoges)
   -> 0.0014s
==  CreateHoges: migrated (0.0015s) ===========================================

しかし、この状態で新しいマイグレーションファイルを作成すると…

rails g model piyo name:string
      create    db/migrate/21000603075833_create_piyos.rb

このように、最終のタイムスタンプ +1 の値がファイル名として採用されてしまいます。これでは、旧来の数字によるバージョン管理同様の衝突が多発します。これも避けた方がいいでしょう。