Git 브랜치 및 Rails 마이그레이션 작업 방법


131

나는 꽤 많은 git 브랜치로 rails 앱을 만들고 있는데 많은 것들이 db 마이그레이션을 포함합니다. 우리는 조심하려고하지만 때로는 마스터의 일부 코드 조각이 다른 지점에서 제거 / 이름이 바뀐 열을 요청합니다.

  1. 자식 브랜치를 DB 상태와 "커플 링"하는 좋은 솔루션은 무엇입니까?

  2. 이 "상태"는 실제로 무엇입니까?

    크기가 몇 GB 인 데이터베이스 만 복제 할 수는 없습니다.

  3. 병합은 어떻게됩니까?

  4. 솔루션이 noSQL 데이터베이스로도 변환됩니까?

    우리는 현재 MySQL, mongodb 및 redis를 사용합니다.


편집 : 나는 매우 중요한 점을 언급하는 것을 잊어 버린 것 같습니다. 개발 환경 에만 관심이 있지만 큰 데이터베이스 (수 GB)가 있습니다.


다른 지점에서 데이터베이스를 수정할 수있는 마스터 지점을 실행하는 환경이 있습니까? 워크 플로가 무엇인지, 왜 브랜치가 특정 데이터베이스와 동기화되도록 유지해야한다고 생각하는지 모르겠습니다.
요나

3
데이터베이스에 클라이언트 (이름, 이메일, 전화 번호)가있는 테이블이 있고 지점에 열 (이름-> first_name + last_name) 중 하나를 분할한다고 가정 해 봅시다. 지점을 마스터와 병합 할 때까지 마스터 및이를 기반으로하는 다른 모든 지점이 실패합니다.
Kostas

답변:


64

지점에 새 마이그레이션을 추가 할 때 rake db:migrate마이그레이션 db/schema.rb

이렇게하면 개발 과정에서 다른 마이그레이션 집합이있는 다른 지점으로 전환하고 간단히 실행할 수 rake db:schema:load있습니다.

이렇게하면 전체 데이터베이스다시 작성되고 기존 데이터가 손실됩니다 .

당신은 아마도 당신이 매우 조심하는 하나의 브랜치에서만 프로덕션을 실행하고 싶을 것입니다. 따라서이 단계는 적용되지 않습니다 ( rake db:migrate평소와 같이 실행하십시오 ). 그러나 개발 과정에서 스키마에서 데이터베이스를 재생성하는 것은 별다른 문제가되지 않습니다 rake db:schema:load.


5
나는 이것이 스키마 문제 만 해결할 것이라고 생각하며, 다운 다운 마이그레이션마다 다시는 볼 수없는 모든 데이터가 손실됩니다. 지점에서 이동할 때 저장되는 다른 종류의 db-data-patch와 다른 지점으로 이동할 때로드되는 다른 종류의 db-data-patch를 저장하는 것이 좋습니다. 패치에는 중단 중에 손실 될 데이터 (마이그레이션) 만 포함해야합니다.
Kostas

4
데이터를로드하려면 db/seeds.rb 합리적인 시드 데이터를 설정 한 경우 개발 DB를 압축하는 것이 너무 끔찍하지 않아야합니다.
Andy Lindeman

핵무기를 할 필요가 없습니다. 아래 내 솔루션을 참조하십시오. 인스턴스가 많이 있고 분기를 전환하면 데이터가 존재하지 않습니다. 테스트로 개발하는 경우에는 이것이 좋습니다.
Adam Dymitruk

앤디 감사합니다.이 질문도 제 질문입니다. 그리고 db/seeds.rb잃어버린 db 데이터를 리포 퓰 레이트하는데 사용 하는 것에 동의
pastullo

실제 버그를 로컬로 재현해야하는 크고 복잡한 앱의 경우 seed 파일을 사용하는 것이 절대 불가능하므로 프로덕션 (또는 준비)의 실제 데이터가 필요합니다. 데이터베이스를 복원하는 데 시간이 오래 걸릴 수 있으므로 필자의 경우에는 좋은 해결책이 아닙니다.
Joel_Blum

21

쉽게 재생할 수없는 큰 데이터베이스가있는 경우 일반 마이그레이션 도구를 사용하는 것이 좋습니다. 간단한 프로세스를 원한다면 이것이 좋습니다.

  • 분기를 전환하기 전에 rake db:rollback분기 지점 이전 상태로 롤백 ( )하십시오. 그런 다음 분기를 전환 한 후을 실행하십시오 db:migrate. 이것은 수학적으로 정확하며 down스크립트 를 작성 하는 한 작동합니다.
  • 분기를 전환하기 전에이 작업을 잊어 버린 경우 일반적으로 안전하게 전환, 롤백 및 다시 전환 할 수 있으므로 워크 플로로 생각하면 가능합니다.
  • 다른 지점의 마이그레이션간에 종속성이있는 경우 ... 잘 생각해야합니다.

2
모든 마이그레이션이 되돌릴 수있는 것은 아니라는 점을 명심해야합니다. 즉, 첫 번째 제안 단계가 성공을 보장하지는 않습니다. 나는 개발 환경에서 좋은 아이디어가 사용하는 것입니다 생각 rake db:schema:load하고 rake db:seed@noodl 말씀하신대로.
pisaruk

@pisaruk 나는 6 년 전에이 질문에 답했다는 것을 알고 있지만, 되돌릴 수없는 이주의 예가 무엇인지 궁금합니다. 상황을 상상하는 데 어려움을 겪고 있습니다. 가장 간단한 것은 많은 데이터를 포함하는 삭제 된 열일 것 같지만 빈 열이나 일부 기본값을 가진 열을 갖도록 "역 분개"될 수 있습니다. 다른 경우를 생각하고 있었습니까?
Luke Griffiths

1
나는 당신이 당신의 자신의 질문에 대답했다고 생각합니다! 예, 삭제 된 열이 좋은 예입니다. 또는 파괴적인 데이터 마이그레이션.
ndp

13

다음은 다른 마이그레이션이 포함 된 분기 간을 전환하기 위해 작성한 스크립트입니다.

https://gist.github.com/4076864

언급 한 모든 문제를 해결할 수는 없지만 지점 이름이 주어지면 다음과 같이됩니다.

  1. 주어진 브랜치에 존재하지 않는 현재 브랜치에서 마이그레이션을 롤백
  2. db / schema.rb 파일에 대한 변경 사항을 폐기하십시오.
  3. 주어진 지점을 확인하십시오
  4. 주어진 브랜치에 존재하는 새로운 마이그레이션을 실행합니다
  5. 테스트 데이터베이스 업데이트

프로젝트에서 항상 수동 으로이 작업을 수행하므로 프로세스를 자동화하는 것이 좋을 것이라고 생각했습니다.


1
이 스크립트는 내가하고 싶은 일을 정확하게 수행합니다. 자동 체크 아웃 후크에 넣고 싶습니다.
brysgo

1
이것만으로, 나는 당신의 요점을 포크하고 그것을 체크 아웃 후 고리로 만들었습니다 : gist.github.com/brysgo/9980344
brysgo

당신의 대본에서, 당신은 정말로 말 git checkout db/schema.rb하거나 의미 git checkout -- db/schema.rb했습니까? (즉, 이중 대시)
user664833

1
글쎄요 ... 당시에는 이중 대시에 대해 몰랐습니다. 그러나이라는 지점이 없으면 명령이 동일하게 작동합니다 db/schema.rb. :)
Jon Lemmon

@brysgo의 진화 된 git_rails 명령 ( github.com/brysgo/git-rails )은 훌륭하게 작동합니다. 감사합니다 Jon :)
Zia Ul Rehman Mughal

7

각 지점에 대한 별도의 데이터베이스

비행하는 유일한 방법입니다.

2017 년 10 월 16 일 업데이트

꽤 오랜 시간이 지나서 이것으로 돌아와서 약간의 개선을했습니다.

  • 와 함께 네임 스페이스 레이크 작업을 추가하여 지점을 만들고 데이터베이스를 한 번에 복제했습니다 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

다음은 한 지점에서 다른 지점으로 데이터베이스를 쉽게 복제하는 레이크 작업입니다. 이것은 소요 SOURCE_BRANCHTARGET_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 및 seed.rb를 사용하여 문제를 쉽게 해결할 수 있습니다.

귀하의 질문은 개발과 관련이 있다고 가정합니다. 프로덕션에서 분기를 정기적으로 전환해야하는 이유를 상상할 수 없습니다.


에 대해 몰랐 db/seeds.rb습니다. 살펴 보겠습니다.
코스타스

3

나는 같은 문제로 고심하고 있었다. 내 해결책은 다음과 같습니다.

  1. 모든 개발자가 schema.rb 및 모든 마이그레이션을 체크인했는지 확인하십시오.

  2. 프로덕션 환경에 배포 할 사람 / 기계가 하나 있어야합니다. 이 머신을 병합 머신이라고합니다. 변경 내용을 병합 시스템으로 가져 오면 schema.rb에 대한 자동 병합이 실패합니다. 문제 없습니다. schema.rb의 이전 내용으로 내용을 바꾸십시오 (사본을 사용하거나 github에서 사용하면 ...).

  3. 중요한 단계는 다음과 같습니다. 모든 개발자의 마이그레이션은 이제 db / migrate 폴더에서 사용할 수 있습니다. 계속해서 번들 실행 db : migrate를 실행하십시오. 병합 시스템의 데이터베이스를 모든 변경 사항과 동일하게 가져옵니다. 또한 schema.rb를 재생성합니다.

  4. 모든 저장소 (원격 및 원격 인 원격 저장소)에 변경 사항을 적용하고 적용하십시오. 완료해야합니다!


3

이것이 내가 한 일이며 모든 기초를 다뤘는지 확실하지 않습니다.

개발 중 (postgresql 사용) :

  • sql_dump db_name> tmp / branch1.sql
  • 자식 체크 아웃 branch2
  • dropdb db_name
  • createdb db_name
  • psql db_name <tmp / branch2.sql # (이전 분기 스위치에서)

약 50K 레코드가있는 데이터베이스의 레이크 유틸리티보다 훨씬 빠릅니다.

프로덕션의 경우 마스터 분기를 성사로 유지하고 모든 마이그레이션이 체크인되고 shema.rb가 올바르게 병합됩니다. 표준 업그레이드 절차를 수행하십시오.


데이터베이스 크기가 작고 지점을 체크 아웃 할 때 백그라운드 에서이 작업을 수행하는 것은 매우 좋은 솔루션처럼 보입니다.
코스타스

2

분기당 "db 환경"을 유지하려고합니다. 얼룩 / 깨끗한 스크립트를보고 다른 인스턴스를 가리 킵니다. DB 인스턴스가 부족한 경우 스크립트가 임시 인스턴스를 분리하도록하여 새 브랜치로 전환하면 이미 존재하며 스크립트로 이름을 변경하면됩니다. 테스트를 실행하기 직전에 DB 업데이트를 실행해야합니다.

도움이 되었기를 바랍니다.


이 솔루션은 "임시"분기에만 적합합니다. 예를 들어 분기 "에지"가 있다면 모든 종류의 미친 물건을 테스트하고 (아마도 다른 하위 지점과 함께) 때때로 마스터에 병합하면 두 데이터베이스가 분리됩니다 (데이터는 그렇지 않습니다) 동일).
Kostas

이 솔루션은 정반대에 좋습니다. 데이터베이스 버전 스크립트를 버전 화하는 경우 매우 유용한 솔루션입니다.
Adam Dymitruk

2

나는 당신이 여기있는 피타를 완전히 경험합니다. 내가 생각할 때 실제 문제는 모든 지점에 특정 지점을 롤백하는 코드가 없다는 것입니다. 나는 장고 세계에 있기 때문에 잘 갈퀴를 모른다. 마이그레이션이 분기되지 않는 자체 리포지토리에 살고 있다는 아이디어와 함께 놀았습니다 (최근에 배운 git-submodule). 이렇게하면 모든 지점에 모든 마이그레이션이 있습니다. 끈적 끈적한 부분은 각 지점이 관심있는 마이그레이션으로 만 제한되도록하는 것입니다. 수동으로 추적하거나 추적하면 오류가 발생하기 쉽습니다. 그러나이를위한 마이그레이션 도구는 없습니다. 그것이 내가 앞으로 나아갈 수없는 시점입니다.


이것은 좋은 생각이지만 지점의 열 이름이 바뀌면 어떻게됩니까? 나머지 지점은 깨진 테이블을 볼 것입니다.
코스타스

음-그것은 끈적 끈적한 부분입니다-어느 지점이 어떤 이주를 신경 쓰는지. "동기화"로 이동하여 "이 마이그레이션 되돌리기"를 알고 열이 되돌아갑니다.
JohnO

1

두 가지 옵션 중 하나를 제안합니다.

옵션 1

  1. 에 데이터를 입력하십시오 seeds.rb. 좋은 방법은 FactoryGirl / Fabrication gem을 통해 시드 데이터를 생성하는 것입니다. 이런 식으로 팩토리가 열 추가 / 제거와 함께 업데이트된다고 가정하면 데이터가 코드와 동기화되도록 보장 할 수 있습니다.
  2. 한 지점에서 다른 지점으로 전환 한 후 run을 실행 rake db:reset하면 데이터베이스가 효과적으로 삭제 / 생성 / 시드됩니다.

옵션 2

수동으로 항상 실행하여 데이터베이스의 상태를 유지 rake db:rollback/ rake db:migrate분기 체크 아웃 후 전에 /. 경고는 모든 마이그레이션을 되돌릴 수 있어야한다는 것입니다. 그렇지 않으면 작동하지 않습니다.


0

개발 환경에서 :

rake db:migrate:redo스크립트가 가역적인지 테스트하기 위해 함께 작업해야 하지만 항상 seed.rb데이터 채우기와 관련 이 있어야합니다 .

git으로 작업한다면, seed.rb는 마이그레이션 변경과 함께 db:migrate:redo시작해야하고 시작 을 위한 실행 (다른 머신이나 새로운 데이터베이스에 새로운 개발을위한 데이터를로드)

'change'외에도, up 및 down 메소드를 사용하면 코드는 항상이 순간과 0에서 시작할 때 "change"에 대한 시나리오를 다루게됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.