はじめに
複数のメンバーで同じRailsアプリケーションを開発しているとコードのコンフリクトがときどき発生します。
普通のファイルは人間が目で見てどうマージすべきか判断すればいいですが、schema.rbのようにRailsによって自動更新されるファイルは手で修正しない方が良いです。
じゃあschema.rbがコンフリクトしたらどうしたらいいの!?という人のためにマージする手順を説明します。
対象となるバージョン
本記事は以下のバージョンを対象としています。
- Rails 5.0.2
- Git 2.12.2
サンプルコード
この手順で使ったRailsアプリケーション(マージがすべて終わった状態)はGitHubにアップしてあります。
想定するシナリオ
ここでは以下のようにアリスとボブが同じテーブル(同じモデル)に対して異なるカラムを追加しようとする状況を想定します。
- アリス = Blogモデルに
author_name
カラムを追加 - ボブ = Blogモデルに
published_at
カラムを追加
二人は次のような順番(時間軸)で作業を実施しました。
アリス | ボブ |
---|---|
add-author-nameブランチを作成 | |
author_nameカラムを追加 | |
add-published-atブランチを作成 | |
published_atカラムを追加 | |
GitHubにpush | |
pull request作成 | |
masterにマージ | |
GitHubにpush | |
pull request作成 | |
<<コンフリクトしてると言われた!>> |
ご覧のとおり、二人が同じテーブルに対してマイグレーションを実行してしまったため、後からpull requestを作成したボブは「schema.rbがコンフリクトしてるから自動マージできないよ!」と怒られました。
ではボブはここからどうすればいいでしょうか?
次からその手順を説明します。
手順
masterブランチをチェックアウトしてgit pullする
add-published-atブランチからmasterブランチに切り替え、GitHubから最新のコードをpullします。
$ git checkout master
$ git pull
マイグレーションのステータスを確認
rails db:migrate:status
コマンドを実行してマイグレーションのステータスを確認します。
$ rails db:migrate:status
Status Migration ID Migration Name
--------------------------------------------------
up 20170426032205 Create blogs
down 20170426032325 Add author name to blogs
up 20170426032438 ********** NO FILE **********
Statusの列がupになっていれば、すでにrails db:migrate
で実行されたマイグレーションであることを表しています。
downであれば逆にまだ実行していないマイグレーションであることを表しています。
また、Migration Nameの列が"NO FILE"になっているものは、「db/migrateディレクトリに該当するマイグレーションファイルがないこと」を表しています。
よって、現在の状況は以下のようになっています。
- 2つ目のマイグレーションはまだローカルでは実行されていない
- 3つ目のマイグレーションは実行されているが該当するマイグレーションファイルが(masterブランチには)ない
これが1件目のマイグレーションのようにすべて「upかつMigration Nameあり」にするのがこの手順のゴールです。
開発用ブランチに切り替えてmasterブランチをマージする
次に自分の開発用ブランチ(今回はadd-published-atブランチ)に切り替えます。
ついでにマイグレーションのステータスも確認しておきましょう。
$ git checkout add-published-at
$ rails db:migrate:status
Status Migration ID Migration Name
--------------------------------------------------
up 20170426032205 Create blogs
down 20170426032325 Add author name to blogs
up 20170426032438 Add published at to blogs
ブランチを切り替えたので、3件目のマイグレーションでMigration Nameが表示されるようになりました。
ただし、2件目のマイグレーションは未実行なのでdownのままです。
続いて、masterブランチをマージします。
ですが、当然コンフリクトが発生します。
$ git merge master
Auto-merging db/schema.rb
CONFLICT (content): Merge conflict in db/schema.rb
Automatic merge failed; fix conflicts and then commit the result.
ちなみにマージを試みたあとのschema.rbはこんなふうになっています。
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20170426032438) do
=======
ActiveRecord::Schema.define(version: 20170426032325) do
>>>>>>> master
create_table "blogs", force: :cascade do |t|
t.string "title"
t.text "content"
<<<<<<< HEAD
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "published_at"
=======
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "author_name"
>>>>>>> master
end
end
schema.rbの変更を取り消して元に戻す
schema.rbがコンフリクトして内容がおかしくなってしまったので、次の手順でいったん変更をリセットして元に戻します。
$ git reset HEAD db/schema.rb
Unstaged changes after reset:
M db/schema.rb
$ git checkout db/schema.rb
するとschema.rbは「published_at
カラムだけが追加された状態」になります。
ActiveRecord::Schema.define(version: 20170426032438) do
create_table "blogs", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "published_at"
end
end
マイグレーションを実行する
schema.rbを元に戻したら、マイグレーションを実行します。
これで未実行だった2件目のマイグレーション(author_name
カラムの追加)が実行されます。
$ rails db:migrate
== 20170426032325 AddAuthorNameToBlogs: migrating =============================
-- add_column(:blogs, :author_name, :string)
-> 0.0109s
== 20170426032325 AddAuthorNameToBlogs: migrated (0.0110s) ====================
マイグレーションのステータスはすべてupになりました。
$ rails db:migrate:status
Status Migration ID Migration Name
--------------------------------------------------
up 20170426032205 Create blogs
up 20170426032325 Add author name to blogs
up 20170426032438 Add published at to blogs
schema.rbも無事にpublished_at
カラムとauthor_name
カラムが追加された状態になっています。
ActiveRecord::Schema.define(version: 20170426032438) do
create_table "blogs", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "published_at"
t.string "author_name"
end
end
マージを続行する
これでコンフリクトが解消したので、schema.rbをaddして、マージを続行します。
(git merge
の--continue
オプションはGit 2.12から導入されたオプションです。)
$ git add db/schema.rb
$ git merge --continue
[add-published-at 505033f] Merge branch 'master' into add-published-at
これでmasterブランチからadd-published-atブランチへのマージは完了しました。
masterブランチに切り替えて開発用ブランチをマージする
さて、いよいよ最後のステップです。
masterブランチに切り替えて開発用ブランチ(add-published-atブランチ)をマージします。
$ git checkout master
$ git merge --no-ff add-published-at
Merge made by the 'recursive' strategy.
db/migrate/20170426032438_add_published_at_to_blogs.rb | 5 +++++
db/schema.rb | 7 ++++---
2 files changed, 9 insertions(+), 3 deletions(-)
create mode 100644 db/migrate/20170426032438_add_published_at_to_blogs.rb
念のためマイグレーションのステータスと、schema.rbの内容に問題がないことを確認しておきましょう。
$ rails db:migrate:status
database: /Users/jit/dev/sandbox/schema-rb-sandbox/db/development.sqlite3
Status Migration ID Migration Name
--------------------------------------------------
up 20170426032205 Create blogs
up 20170426032325 Add author name to blogs
up 20170426032438 Add published at to blogs
ActiveRecord::Schema.define(version: 20170426032438) do
create_table "blogs", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "published_at"
t.string "author_name"
end
end
大丈夫そうなので、GitHubにpushします。
$ git push origin master
GitHubを見に行くと、ボブが作ったpull requestは"Merged"になっているはずです。
これでボブは無事に作業を終わらせることができました。
手順は以上になります。お疲れ様でした!
まとめ
ボブの作業内容(確認作業以外)を簡単にまとめておきましょう。
- masterブランチをpull
git checkout master
git pull
- 開発用ブランチに切り替える
git checkout add-published-at
- masterブランチをマージ
git merge master
- schema.rbの変更をリセット
git reset HEAD db/schema.rb
git checkout db/schema.rb
- マイグレーション実行
rails db:migrate
- マージを続行
git add db/schema.rb
git merge --continue
- masterブランチに切り替える
git checkout master
- 開発用ブランチをマージ
git merge --no-ff add-published-at
- GitHubにpush
git push origin master
こんなふうにすればコンフリクトしたschema.rbをきれいにマージすることができるはずです。
ぜひ参考にしてみてください!
参考文献
この記事は下記ページの内容を参考にして執筆しました。
こちらのページもぜひ一度ご覧ください。
How to Recover From Rails Database Schema Conflicts When Rebasing – dgms code