Tuesday, April 03, 2007

SVN Branching and Merging

ใช้ version control มานานแล้ว
แต่ยังไม่เคยได้ทำ branch & merge operation สักที
หลักๆก็ใช้อยู่แค่ trunk เส้นเดียว
ไม่เคยสร้าง branches ให้ปวดหัว
(ไม่สร้าง branches ก็ปวดหัวไปอีกแบบ)

ปกติในการใช้ version control,
เราสามารถแบ่งลักษณะการใช้ออกเป็น 3 กลุ่ม ตามลักษณะการใช้ branches ได้ดังนี้

พวกที่ 1 ไม่ใช้ branches เลย มีแต่ trunk อย่างเดียว
(บริษัทผม จัดอยู่ในกลุ่มนี้)

พวกที่ 2 พวกนี้สุดโต่งไปอีกด้านหนึ่งก็คือ ใช้แต่ branches เป็นหลัก
trunk เก็บเฉพาะ code ที่ verify ว่า stable แล้วเท่านั้น
ในพวกนี้ เวลาใครจะทำอะไรกับ code ไม่ว่าจะ bug ตัวจิ๊บจ๊อย ก็ต้องสร้าง branch ออกไป
เมื่อเสร็จแล้วจึงจะ merge เข้า trunk

พวกที่ 3 เป็นพวกผสมระหว่าง 1 และ 2 นั่นคือ
การเปลี่ยนแปลงเล็กๆน้อยๆ ก็ใช้ trunk ไป
แต่ถ้าเปลี่ยนเยอะหน่อยก็ใช้ branches

ปัจจุบัน ปัญหาที่ผมเจอก็คือ
เนื่องจาก methodology ที่ผมใช้พัฒนา software ในปัจจุบัน
ใช้เทคนิคแบบ Agile ในการพัฒนา (ทำไปคิดไป)
ซึ่งพอถึงจุดหนึ่ง มันจะเกิด refactor ขนานใหญ่ขึ้น
ก็เลยหลีกเลี่ยงไม่ได้ที่จะต้องทำ branches เพื่อแยกไป refactor ต่างหาก
(แยกไปลองผิดลองถูกต่างหาก จะได้ไม่กระทบคนอื่นเขา)

ใน svn, เวลาเราจะทำ branches
เราสามารถทำได้ง่ายๆ ดังนี้
  • สร้าง directory ที่จะใช้เก็บ branches ของเรา
    มาตรฐานแบบไม่เป็นทางการของ svn กำหนดว่า directory branches ให้เราอยู่แล้ว
    ดังนี้เราก็เลือกสร้าง folder ใต้ directory นี้
    ความยากในขั้นนี้ก็คือการตั้งชื่อ folder

    สมมติเราต้องการสร้าง folder mybranche-1 ก็ให้ใช้คำสั่งนี้

    svn mkdir http://hostname/svn/yourproject/branches/mybranche-1

  • ใช้คำสั่ง copy เพื่อ copy จาก trunk ไปยัง folder ที่เราสร้าง

    svn copy http://hostname/svn/yourproject/trunk \
    http://hostname/svn/yourproject/branches/mybranche-1


แค่นี้ ก็ได้ branche เส้นใหม่ ให้เราได้ลองยำเล่นสมใจแล้ว

ความยากขั้นถัดไป ก็คือการ merge
โดยการ merge จะเกิดได้ 2 กรณี
  • กรณีแรก เราทำ code เสร็จไว, ต้องการ commit change กลับเข้าไปยัง trunk
    การ merge ก็จะเกิดใน ทิศทางจาก branche มายัง trunk
  • กรณีที่สอง เราแก้ไข branch นานมาก จน trunk เปลี่ยนไปเยอะแล้ว,
    ถ้าเกิดเหตุการณ์แบบนี้ เราก็ควรจะ merge การเปลี่ยนแปลงจาก trunk ไปยัง branche เป็นระยะๆ
    เพื่อลดความแตกต่างระหว่าง code ลง


ข้อด้อยของ svn ก็คือ มันไม่มี feature ที่ช่วยจำว่า เรา merge อะไรไแล้วบ้าง
ดังนั้นจึงมีโอกาสที่เราจะ merge ซ้ำซ้อนลงไปได้
best practice ก็คือ ในการ commit การ merge แต่ละครั้ง
ให้เขียน log message ให้ชัดเจนว่า เป็นการ merge จาก revision ไหนถึงไหน
เพื่อจะได้ใช้เป็นข้อมูลเตือนความจำ

ลองดูตัวอย่างการใช้คำสั่ง merge จาก branch ไปยัง trunk
สมมติเรามีเส้นทางของ version เราดังนี้

#trunk# --- revision 149 ------- .... ------- >
\
\ branches
\
+ revision 150 ----- .... ----- revision 159 >


ขั้นตอนการ merge กลับ, เราจะใช้คำสั่ง svn merge เข้ามาช่วย
โดยเราจะสั่งคำสั่งดังนี้

ย้าย current directory ไปยัง working copy ของ trunk
แล้วทำการ update code ให้เป็น code ล่าสุดก่อน

$ cd $WORKING_COPY_OF_MAIN_TRUNK
$ svn update


trick ของการใช้ merge ก็คือ ประเด็นว่าเราจะต้องหาเลข revision เริ่มต้นให้ได้ก่อน (ในที่นี้คือ 150)
เพราะส่วนใหญ่มักจะลืมไม่ได้จดไว้
คำสั่งที่ช่วยหาเลขนี้ก็คือ

svn log -stop-on-copy

โดยมันจะ report log ไปจนถึง จุดที่เกิดการทำ branches (ใน svn การทำ branches คือการ copy)

เมื่อได้เลข revision มาแล้ว ก็สั่ง merge ส่วนเปลี่ยนแปลงที่เกิดขึ้นระหว่าง revision 150 ถึง 159 ใน branches
กับ working copy ของ trunk
โดยเริ่มแรกให้ใส่ parameter --dry-run เพื่อตรวจสอบความถูกต้องก่อน

$ svn merge --dry-run -r 150:159 http://domain/svn/branches/your_branches_name


ซึ่งหลังจากเกิดการ merge แล้ว,
เราก็ต้องตรวจ working copy ของเรา
ว่ามีอะไรผิดผลาดไหม เช่นพวก conflict
พอมั่นใจว่าถูกหมด ก็สั่ง commit ตามปกติ

ส่วนการ merge จาก trunk ไปยัง branch ก็ทำแบบเดียวกัน
เพียงแต่กลับทิศทางกัน

Related link from Roti

3 comments:

veer said...

มาเริ่มมาใช้ bzr รู้สึกเหมือน merge เป็นประจำ :-P

Pittaya said...

ปกติผมก็ใช้แต่ trunk เหมือนกัน
เป็นพวกที่ว่าพอใช้ svn เป็นในระดับหนึ่งแล้วจะขี้เกียจอ่าน document ของฟีเจอร์อื่น

polawat phetra said...

veer: ใช่ ลืมไปเลยว่า พวกตระกูล Distributed Version Control มันมีแต่ branch ไม่มี trunk

pittaya: ของผมหนักตรง พอจะต้องทำ merge ขึ้นมา
มันเกิดอาการเกร็ง กลัว merge ผิด, code หาย
สาเหตุ ก็คงเป็นเพราะอะไรที่เราไม่เข้าใจจริงๆ มันก็ไม่อยากยุ่ง