Help us understand the problem. What is going on with this article?

コンフリクトしたschema.rbをきれいにマージする手順

More than 1 year has passed since last update.

はじめに

複数のメンバーで同じRailsアプリケーションを開発しているとコードのコンフリクトがときどき発生します。
普通のファイルは人間が目で見てどうマージすべきか判断すればいいですが、schema.rbのようにRailsによって自動更新されるファイルは手で修正しない方が良いです。

じゃあschema.rbがコンフリクトしたらどうしたらいいの!?という人のためにマージする手順を説明します。

対象となるバージョン

本記事は以下のバージョンを対象としています。

  • Rails 5.0.2
  • Git 2.12.2

サンプルコード

この手順で使ったRailsアプリケーション(マージがすべて終わった状態)はGitHubにアップしてあります。

https://github.com/JunichiIto/schema-rb-sandbox

想定するシナリオ

ここでは以下のようにアリスとボブが同じテーブル(同じモデル)に対して異なるカラムを追加しようとする状況を想定します。

  • アリス = 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カラムだけが追加された状態」になります。

schema.rb
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カラムが追加された状態になっています。

schema.rb
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
schema.rb
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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away