วิธีทำงานกับสาขา Git และการย้ายข้อมูล Rails


131

ฉันกำลังทำงานกับแอพพลิเคชั่นรางที่มี git branch ค่อนข้างน้อยและหลาย ๆ แอพมีการโอนย้ายฐานข้อมูล เราพยายามระมัดระวัง แต่บางครั้งโค้ดบางส่วนในมาสเตอร์จะขอคอลัมน์ที่ถูกลบ / เปลี่ยนชื่อในสาขาอื่น

  1. อะไรคือทางออกที่ดีสำหรับ "คู่" สาขา git ที่มีสถานะ 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 บางชนิดที่ได้รับการบันทึกเมื่อย้ายออกจากสาขาและอีกอันที่โหลดเมื่อย้ายไปยังสาขาอื่น แพตช์ควรมีเฉพาะข้อมูลที่จะสูญหายระหว่างทางลง (การย้ายข้อมูล)
Kostas

4
หากคุณต้องการโหลดข้อมูลให้ใช้db/seeds.rb มันไม่ควรทำลายฐานข้อมูลการพัฒนาของคุณมากเกินไปหากคุณตั้งค่าข้อมูลเมล็ดพันธุ์ที่เหมาะสมไว้ที่นั่น
Andy Lindeman

ไม่จำเป็นต้องตกใจอะไร ดูวิธีแก้ปัญหาของฉันด้านล่าง โปรดทราบว่าคุณจะมีอินสแตนซ์มากมายและเมื่อคุณเปลี่ยนสาขาข้อมูลจะไม่อยู่ที่นั่น นี่เป็นเรื่องปกติหากคุณกำลังพัฒนาด้วยการทดสอบ
Adam Dymitruk

ขอบคุณแอนดี้คำตอบนี้ยังเป็นคำถามของฉัน และตกลงที่จะใช้db/seeds.rbเพื่อเพิ่มปริมาณข้อมูลฐานข้อมูลที่สูญหาย
pastullo

สำหรับแอปพลิเคชันที่ซับซ้อนขนาดใหญ่ที่คุณต้องสร้างจุดบกพร่องในชีวิตจริงขึ้นมาใหม่คุณไม่สามารถใช้ไฟล์ seed ได้อย่างแน่นอนคุณต้องใช้ข้อมูลจริงจากการผลิต (หรือการจัดเตรียม) และการกู้คืนฐานข้อมูลอาจใช้เวลาสักครู่ดังนั้นจึงไม่ใช่วิธีแก้ปัญหาที่ดีสำหรับกรณีของฉัน
Joel_Blum

21

หากคุณมีฐานข้อมูลขนาดใหญ่ที่ไม่สามารถทำซ้ำได้อย่างง่ายดายขอแนะนำให้ใช้เครื่องมือการย้ายข้อมูลปกติ หากคุณต้องการขั้นตอนง่ายๆนี่คือสิ่งที่ฉันแนะนำ:

  • ก่อนที่จะเปลี่ยนสาขาให้ย้อนกลับ ( rake db:rollback) ไปยังสถานะก่อนจุดสาขา db:migrateจากนั้นหลังจากที่สาขาสลับวิ่ง สิ่งนี้ถูกต้องตามหลักคณิตศาสตร์และตราบใดที่คุณเขียนdownสคริปต์มันก็จะใช้ได้
  • หากคุณลืมทำสิ่งนี้ก่อนเปลี่ยนสาขาโดยทั่วไปคุณสามารถสลับย้อนกลับย้อนกลับและสลับอีกครั้งได้อย่างปลอดภัยดังนั้นฉันคิดว่าเป็นขั้นตอนการทำงานก็เป็นไปได้
  • หากคุณมีการพึ่งพาระหว่างการย้ายข้อมูลในสาขาต่างๆ ... คุณจะต้องคิดหนัก

2
คุณต้องทราบว่าการย้ายข้อมูลทั้งหมดไม่สามารถย้อนกลับได้ดังที่กล่าวไว้ขั้นตอนแรกที่แนะนำจะไม่รับประกันว่าจะประสบความสำเร็จ ฉันคิดว่าในสภาพแวดล้อมการพัฒนาควรใช้ความคิดที่ดีrake db:schema:loadและrake db:seedตามที่ @noodl ได้กล่าวไว้
pisaruk

@pisaruk ฉันรู้ว่าคุณตอบเมื่อหกปีที่แล้ว แต่อ่านแล้วฉันอยากรู้ว่าตัวอย่างของการย้ายถิ่นที่ไม่สามารถย้อนกลับได้จะเป็นอย่างไร ฉันมีช่วงเวลาที่ยากลำบากในการจินตนาการถึงสถานการณ์ ฉันเดาว่าวิธีที่ง่ายที่สุดน่าจะเป็นคอลัมน์ที่ดร็อปซึ่งมีข้อมูลจำนวนมาก แต่อาจ "ย้อนกลับ" เพื่อให้มีคอลัมน์ว่างหรือคอลัมน์ที่มีค่าเริ่มต้น คุณกำลังคิดถึงกรณีอื่น ๆ หรือไม่?
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

คำสั่ง git_rails วิวัฒนาการของ @ brysgo ( github.com/brysgo/git-rails ) ใช้งานได้ดี ขอบคุณคุณจอน :)
Zia Ul Rehman Mughal

7

แยกฐานข้อมูลสำหรับแต่ละสาขา

มันเป็นวิธีเดียวที่จะบิน

อัปเดตวันที่ 16 ตุลาคม 2017

ฉันกลับมาที่นี่หลังจากผ่านไประยะหนึ่งและทำการปรับปรุงบางอย่าง:

  • ฉันได้เพิ่มงานคราดเนมสเปซอื่นเพื่อสร้างสาขาและโคลนฐานข้อมูลในคราวเดียวด้วยbundle exec rake git:branch.
  • ตอนนี้ฉันรู้แล้วว่าการโคลนนิ่งจากต้นแบบไม่ใช่สิ่งที่คุณต้องการทำเสมอไปดังนั้นฉันจึงทำให้ชัดเจนมากขึ้นว่าdb:clone_from_branchงานต้องใช้SOURCE_BRANCHและTARGET_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

นี่คืองาน Rake เพื่อโคลนฐานข้อมูลของคุณจากสาขาหนึ่งไปยังอีกสาขาหนึ่งได้อย่างง่ายดาย สิ่งนี้ใช้ตัวแปรSOURCE_BRANCHและTARGET_BRANCHสภาพแวดล้อม ตามออกของ@spalladinoงาน 's

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

ตอนนี้สิ่งที่คุณต้องทำคือเรียกใช้bundle exec git:branchป้อนชื่อสาขาใหม่และเริ่มฆ่าซอมบี้


4

บางทีคุณควรใช้เป็นคำใบ้ว่าฐานข้อมูลการพัฒนาของคุณใหญ่เกินไป? หากคุณสามารถใช้ db / seeds.rb และชุดข้อมูลขนาดเล็กสำหรับการพัฒนาปัญหาของคุณสามารถแก้ไขได้อย่างง่ายดายโดยใช้ schema.rb และ seeds.rb จากสาขาปัจจุบัน

ที่ถือว่าคำถามของคุณเกี่ยวข้องกับการพัฒนา ฉันนึกไม่ออกว่าทำไมคุณต้องเปลี่ยนสาขาในการผลิตเป็นประจำ


ฉันไม่รู้db/seeds.rbฉันจะตรวจสอบมัน
Kostas

3

ฉันกำลังดิ้นรนกับปัญหาเดียวกัน นี่คือทางออกของฉัน:

  1. ตรวจสอบว่าทั้ง schema.rb และการย้ายข้อมูลทั้งหมดถูกตรวจสอบโดยนักพัฒนาทั้งหมด

  2. ควรมีหนึ่งคน / เครื่องจักรสำหรับการปรับใช้กับการผลิต เรียกเครื่องนี้ว่าเครื่องผสาน เมื่อการเปลี่ยนแปลงถูกดึงไปยังเครื่องผสานการผสานอัตโนมัติสำหรับ schema.rb จะล้มเหลว ไม่มีปัญหา เพียงแค่แทนที่เนื้อหาด้วยเนื้อหาก่อนหน้าของ schema.rb (คุณสามารถวางสำเนาไว้ข้าง ๆ หรือรับจาก github หากคุณใช้ ... )

  3. นี่คือขั้นตอนที่สำคัญ ตอนนี้การย้ายข้อมูลจากนักพัฒนาทั้งหมดจะพร้อมใช้งานในโฟลเดอร์ db / migrate ไปข้างหน้าและเรียกใช้บันเดิล exec rake db: migrate มันจะนำฐานข้อมูลบนเครื่องผสานมาพร้อมกับการเปลี่ยนแปลงทั้งหมด นอกจากนี้ยังจะสร้าง schema.rb ขึ้นมาใหม่

  4. ยอมรับและผลักดันการเปลี่ยนแปลงออกไปยังที่เก็บทั้งหมด (รีโมทและบุคคลซึ่งเป็นรีโมทด้วย) คุณควรจะทำ!


3

นี่คือสิ่งที่ฉันได้ทำและฉันไม่ค่อยแน่ใจว่าฉันได้ครอบคลุมทุกฐาน:

อยู่ระหว่างการพัฒนา (โดยใช้ postgresql):

  • sql_dump db_name> tmp / branch1.sql
  • git checkout branch2
  • dropdb db_name
  • createdb db_name
  • psql db_name <tmp / branch2.sql # (จากสวิตช์สาขาก่อนหน้า)

นี่เร็วกว่ายูทิลิตี้คราดบนฐานข้อมูลที่มีเร็กคอร์ดประมาณ 50K มาก

สำหรับการผลิตให้ดูแลสาขาหลักเป็นสิ่งศักดิ์สิทธิ์และการย้ายข้อมูลทั้งหมดจะถูกตรวจสอบใน shema.rb รวมเข้าด้วยกันอย่างเหมาะสม ทำตามขั้นตอนการอัพเกรดมาตรฐานของคุณ


สำหรับขนาดฐานข้อมูลที่เล็กพอและทำในพื้นหลังเมื่อตรวจสอบสาขาดูเหมือนจะเป็นทางออกที่ดีมาก
Kostas

2

คุณต้องการรักษาสภาพแวดล้อม "db" ต่อสาขา ดูที่ smudge / clean script เพื่อชี้ไปที่อินสแตนซ์ต่างๆ หากคุณใช้อินสแตนซ์ db หมดให้สคริปต์แยกอินสแตนซ์ชั่วคราวออกดังนั้นเมื่อคุณเปลี่ยนไปใช้สาขาใหม่จะมีอยู่แล้วและจำเป็นต้องเปลี่ยนชื่อโดยสคริปต์ การอัปเดต DB ควรรันก่อนที่คุณจะดำเนินการทดสอบ

หวังว่านี่จะช่วยได้


โซลูชันนี้ใช้ได้ดีสำหรับสาขา "ชั่วคราว" เท่านั้น ตัวอย่างเช่นถ้าเรามี "edge" สาขาคือเราทดสอบสิ่งที่บ้าคลั่งทุกประเภท (อาจเป็นกับสาขาย่อยอื่น ๆ ) แล้วรวมเข้ากับหลักเป็นครั้งคราวฐานข้อมูลทั้ง 2 จะแยกออกจากกัน (ข้อมูลจะไม่ เหมือนเดิม).
Kostas

วิธีนี้ดีสำหรับตรงกันข้าม นี่เป็นทางออกที่ดีมากหากคุณกำหนดเวอร์ชันสคริปต์เวอร์ชันฐานข้อมูลของคุณ
Adam Dymitruk

2

ฉันได้สัมผัสกับไฟลนก้นที่คุณมีที่นี่โดยสิ้นเชิง อย่างที่ฉันคิดเกี่ยวกับเรื่องนี้ปัญหาที่แท้จริงคือสาขาทั้งหมดไม่มีรหัสในการย้อนกลับบางสาขา ฉันอยู่ในโลกของ django ดังนั้นฉันจึงไม่รู้จักคราดนั้นดี ฉันคิดว่าการย้ายข้อมูลอาศัยอยู่ใน repo ของตัวเองที่ไม่ได้แยกส่วน (git-submodule ซึ่งฉันเพิ่งเรียนรู้) ด้วยวิธีนี้ทุกสาขามีการโยกย้ายทั้งหมด ส่วนที่เหนียวทำให้แน่ใจว่าแต่ละสาขาถูก จำกัด ไว้เฉพาะการย้ายข้อมูลที่พวกเขาสนใจ การทำ / ติดตามสิ่งนั้นด้วยตนเองอาจเป็นไฟลนก้นและมีแนวโน้มที่จะเกิดข้อผิดพลาด แต่ไม่มีเครื่องมือการย้ายข้อมูลใดที่สร้างขึ้นสำหรับสิ่งนี้ นั่นคือจุดที่ฉันไร้หนทางไปข้างหน้า


นี่เป็นความคิดที่ดี แต่จะเกิดอะไรขึ้นเมื่อสาขาเปลี่ยนชื่อคอลัมน์ กิ่งที่เหลือจะมองไปที่โต๊ะหัก
Kostas

อืม - นั่นคือส่วนที่เหนียว - ซึ่งสาขาให้ความสำคัญกับการย้ายถิ่น เพื่อให้คุณสามารถ "ซิงค์" และทราบว่า "เปลี่ยนกลับการย้ายข้อมูลนี้" คอลัมน์จึงย้อนกลับไป
JohnO

1

ฉันขอแนะนำหนึ่งในสองตัวเลือก:

ตัวเลือกที่ 1

  1. seeds.rbใส่ข้อมูลของคุณใน ตัวเลือกที่ดีคือการสร้างข้อมูลเมล็ดพันธุ์ของคุณผ่านอัญมณี FactoryGirl / Fabrication ด้วยวิธีนี้คุณสามารถรับประกันได้ว่าข้อมูลจะซิงค์กับรหัสหากเราถือว่าโรงงานได้รับการอัปเดตพร้อมกับการเพิ่ม / ลบคอลัมน์
  2. หลังจากเปลี่ยนจากสาขาหนึ่งไปยังอีกสาขาหนึ่งให้เรียกใช้rake db:resetซึ่งลด / สร้าง / วางฐานข้อมูลได้อย่างมีประสิทธิภาพ

ทางเลือกที่ 2

รักษาสถานะของฐานข้อมูลด้วยตนเองโดยเรียกใช้rake db:rollback/ rake db:migrateก่อน / หลังการชำระเงินสาขาเสมอ ข้อแม้คือการย้ายข้อมูลทั้งหมดของคุณต้องย้อนกลับได้มิฉะนั้นจะไม่ได้ผล


0

เกี่ยวกับสภาพแวดล้อมการพัฒนา:

คุณควรดำเนินการrake db:migrate:redoเพื่อทดสอบว่าสคริปต์ของคุณสามารถย้อนกลับได้หรือไม่ แต่โปรดทราบว่าควรมี a seed.rbกับประชากรข้อมูลเสมอ

หากคุณทำงานกับคอมไพล์คุณ seed.rb ควรเปลี่ยนแปลงด้วยการเปลี่ยนแปลงการโอนย้ายและการดำเนินการdb:migrate:redoสำหรับการเริ่มต้น (โหลดข้อมูลสำหรับการพัฒนาใหม่บนเครื่องอื่นหรือฐานข้อมูลใหม่)

นอกเหนือจาก´change´ ด้วยวิธีการขึ้นและลงของรหัสของคุณจะเป็นสถานการณ์ที่ครอบคลุมสำหรับ "การเปลี่ยนแปลง" ในช่วงเวลานี้และเมื่อเริ่มต้นจากศูนย์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.