【Rails】migration後にブランチを切り替えるときは、rails db:rollbackした方がいいんじゃないかと思う
所属しているコミュニティで、migrationを実行したブランチから他のブランチに切り替えたときには rails db:reset
するとよい、というアイディアが出ています。
この方法を完全に否定するわけではないのですが、可能であれば別の方法を取ったほうがいいと思ったので記事を書きました。
(追記)実戦的なブランチ切替方法についてコメントをいただいたので、ページ末尾のコメントもぜひご覧ください
TL;DR
- 開発環境のデータを守りたいなら
rails db:rollback
した方がいい - 開発環境のデータをリセットしてもいい、かつロールバックが難しいなら、
rails db:migrate:reset
(rails db:reset
)もやむなし 困ったらとりあえずリセットすればいいと思うやっぱりできるだけロールバックしたほうがいい
rails db:migrate:reset
(rails db:reset
)が必要になる理由
例えば、mainブランチからAブランチを切って、migrationが必要な作業をしたとします。
- Aブランチで
orders
テーブルを追加する - AブランチのPRを作成する
- mainブランチに戻る
- mainブランチからBブランチを切って作業する
- Bブランチで
employees
テーブルを追加する
のように作業すると、Bブランチでのmigrationの状態はどうなるでしょうか?
結果は以下のようになります。
$ rails db:migrate:status database: db/development.sqlite3 Status Migration ID Migration Name -------------------------------------------------- ... up 20210917144639 ********** NO FILE ********** up 20210917145553 Create employees
Aブランチのmigrationが ********** NO FILE **********
となってBブランチに含まれてしまっていますね。
また、 schema.rb
は以下のようになります。
# ... create_table "orders", force: :cascade do |t| t.integer "user_id", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.index ["user_id"], name: "index_orders_on_user_id" end # ...
AブランチでDBに追加された orders
テーブルはBブランチでも残っているため、 schema.rb
に orders
テーブルの定義が記述されてしまっています。
この矛盾を解消する一つの手段が、 rails db:migrate:reset
(rails db:reset
)の実行です。
DBの構築を0からやり直し、migrationの矛盾を気にせずAブランチの変更を排除することができます。
非常に分かりやすい方法なので、手軽に実行することが可能です。
ですが、できるかぎりこの方法は避けた方がいいと思います。
rails db:migrate:reset
(rails db:reset
)しない方がいい理由
rails db:migrate:reset
(rails db:reset
)すると、開発環境のデータが吹っ飛びます。
例えば、日報を処理するアプリケーションを開発している場合、
- 日報一覧に日報を表示するため、数十件の日報を作成する
- 下書き状態と提出済み状態の日報をそれぞれ作成する
など、動作確認を行うために、Webアプリ上やDBで色々なデータを作成・編集したくなります。
しかし、rails db:migrate:reset
(rails db:reset
)はDBを削除してから再作成するので、動作確認用にコツコツ溜めたデータが消失してしまいます。
一方、rails db:rollback
なら、データを維持しながら他のブランチに移動することができます。
rails db:rollback
の実行の仕方
先ほどのAブランチ、Bブランチの例を流用します。
赤字がロールバックのために行う手順です。
- Aブランチで
orders
テーブルを追加する - AブランチのPRを作成する
- Aブランチで
rails db:rollback
を実行し、orders
テーブルの作成を巻き戻す - mainブランチに戻る
- mainブランチからBブランチを切って作業する
- Bブランチで
employees
テーブルを追加する
こうすることで、Aブランチのmigration実行がBブランチに持ち込まれないようになります。
まず、 orders
テーブルを追加したあとのmigrationの状態を確認します。
$ rails db:migrate:status database: db/development.sqlite3 Status Migration ID Migration Name -------------------------------------------------- ... up 20210917144639 Create orders
PRを作成したら、最後に実行されている Create orders
をrollbackします。
$ rails db:rollback
実行したmigrationが複数ある場合は、 rails db:rollback STEP=2
などで、そのブランチで実行した全てのmigrationをrollbackします。
その後、再びmigrationの状態を確認します。
$ rails db:migrate:status database: db/development.sqlite3 Status Migration ID Migration Name -------------------------------------------------- ... down 20210917144639 Create orders
末尾のStatusがdownになっていればOKです。
あとは git switch main --force
して、Bブランチを切り出したらそちらで作業を遂行していきます。
rails db:rollback
し忘れたまま別のmigrationを実行してしまった場合
Aブランチ、Bブランチの例で言うと、以下のようにしてしまった場合です。
- Aブランチで
orders
テーブルを追加する - AブランチのPRを作成する
- mainブランチに戻る
- mainブランチからBブランチを切って作業する
- Bブランチで
employees
テーブルを追加する
この場合は、以下のようにmigrationを巻き戻します。
- Bブランチのmigrationファイルをコミットなりstashなりで保存する(
schema.rb
は含めない) - Bブランチで
rails db:rollback
を実行し、employees
テーブルの作成を巻き戻す - Aブランチに戻る
- Aブランチで
rails db:rollback
を実行し、orders
テーブルの作成を巻き戻す - Bブランチに戻る
- Bブランチで
rails db:migrate
を実行する
それでもどうしようもなくなったら
migrationを複数のブランチで実行しているうちに、どうにも rails db:rollback
できなくなってしまう場合もあります。
その場合は rails db:migrate:reset
(rails db:reset
) を実行しましょう。
seedが整備されているプロジェクトであれば、必要最低限の開発環境用データは再生成されます。
※ rails db:migrate:reset
を実行した場合は、そのあとに rails db:seed
を実行する必要があります
余談: rails db:migrate:reset
と rails db:reset
のどちらを選ぶか
この二つには以下のような差異があります。
rails db:migrate:reset
: migrationファイルからDBを構築する。rails db:seed
を実行しないrails db:reset
:schema.rb
からDBを構築する。rails db:seed
を実行する
冒頭で示したような例に陥ると、 schema.rb
は別ブランチの変更を含んでしまっている可能性があります。
そのため、基本的には rails db:migrate:reset
(+ rails db:seed
) を実行するのが良いと思います。