วิธีการใช้ Subversion (SVN) – Branch และ Merge ใน Subversion

by m3rLinEz 5. April 2009 01:19

มาต่อจากตอนที่แล้วด้วยเรื่องของ Branch และ Merge ครับ เรื่องนี้เป็นหัวใจของระบบ Version Control เลยนะเนี่ย

การจัด Repository

มาพูดถึงการจัด Repository ก่อนครับ โดยปกติแล้วเค้าจะจัดกันแบบนี้

RepoRoot
--- ProjectA
--- --- trunk
--- ---
branches
--- --- --- branchA
--- --- --- branchB
--- --- tags
--- ---  --- 1.0.0
--- ---  --- 1.1.5
--- ProjectB

ถ้าใช้ TortoiseSVN อยู่ คำว่า “จัด” ที่ผมใช้นีหมายถึงให้เปิด Repo Browser ขึ้นมาแล้วสร้าง Structure ตามตัวหนาด้านบนนะครับ

ส่วนที่สำคัญคือ trunk, branches, และ tags ซึ่งจริงๆแล้วชื่อพวกนี้ไม่ได้เป็นข้อบังคับหรือว่าอะไรเลย ไม่ได้มีความหมายพิเศษต่อการทำงานของ SVN ด้วย แต่ว่าเป็นอะไรที่คนส่วนใหญ่เค้าทำกัน แต่ SVN ก็จะมองเป็นโฟลเดอร์ธรรมดาๆ ดังนั้นเราจึงควรทำอะไรตามคนส่วนใหญ่ จะได้สื่อสารกันได้ง่ายๆ :)

คำว่า trunk และ branches มาจากส่วนประกอบของต้นไม้ คือ ลำต้น และ กิ่ง โดยลำต้น หรือ trunk ก็จะหมายถึงสายการพัฒนากลางหรือสายพัฒนาหลัก และเวอร์ชันล่าสุดของโปรเจกก็มักจะอยู่ที่นี่ ถ้าอยากได้ซอร์สโค้ดของโปรเจคก็ควรจะ check out จากที่นี่ครับ และ trunk ก็จะถูกคาดหวังว่าจะสามารถนำไป build และ compile เพื่อใช้งานได้

ส่วน branch เป็นกิ่งก้านสาขาที่แตกออกมา (ในกรณีปกติก็คงแตกออกมาจาก trunk นั่นแหละ) อาจจะแตกออกมาเพื่อให้คนๆหนึ่งไปรับผิดชอบ feature หนึ่งๆ เพราะเกรงว่าการแก้ไขโดยคนนั้นๆทำไปทำมาจะทำให้ trunk เจ๊ง (คอมไพล์ไม่ผ่าน) ก็เลยให้ไปทำใน branch ซะ แน่ใจว่าเสร็จแล้วค่อยเอามารวม (merge)

และ tag อันนี้แปลง่ายๆคือมันเป็น snapshot (ง่ายตรงไหนวะ …) คือเหมือนเป็นการบันทึก state ณ เวลาหนึ่งๆไว้นั่นเอง มักถูกใช้ในการเก็บเวอร์ชันต่างๆไว้ เป็นต้นว่า 1.0.0 ตอน release เวอร์ชัน 1.0.0 เป็นต้น ถ้าทำไปเรื่อยๆจนถึงเวอร์ชัน 2.1 แล้ว อยากดูโค้ดตอนที่ release 1.0.0 ว่าเป็นยังไงก็เปิด tags/1.0.0 ได้

ทั้งการทำ branch และ tag นั้น เป็นเพียงการ copy trunk ไปใส่ไว้ใน branches และ tags เท่านั้น ซึ่งการ copy ผ่าน SVN มันก็จะเป็นแค่การคัดลอก reference มา ดังนั้นจึงทำได้เร็วมาก และหายห่วงเรื่องไฟล์ซ้ำซ้อนได้เลย

เรื่องที่อาจจะงงอีกเรื่องคือ แล้วตอน check out ตัว ProjectA ออกมา มันจะมาทั้งยวงคือ trunk, tags, branches เลยรึเปล่า คำตอบคือ ปกติตอน check out ก็ต้องเลือกเอาว่าจะ check out ตัวไหนมาอ่ะครับ อาจจะเป็น ProjectA/trunk หรือ ProjectA/branches/gantbranch อะไรแบบนี้ก็ได้ จะได้ไม่มากันทั้งครอบครัว

Tree

(ผมวาดเอง เหมือนจะอาร์ต แต่ไม่อาร์ตแฮะ - -‘)

รูปแบบการทำงานร่วมกันบน Subversion

เท่าที่อ่านมามี 3 แบบ

    1. ไม่ต้องแตก branch ทำบน trunk อย่างเดียว อันนี้ก็อาจจะมีปัญหาในกรณีที่แก้ไขโดยคนใดคนหนึ่งแล้วมันเจ๊ง ทำให้ trunk ไม่เสถียรเท่าไหร่
    2. แตก branch เป็นกรณีพิเศษ แต่งานส่วนใหญ่ยังทำกันบน trunk ในกรณีที่ต้องแก้ไขอะไรยิ่งใหญ่มากจนอาจทำให้ trunk ไม่เสถียร (คอมไพล์ไม่ผ่าน รันเทสต์แล้วเน่า)  เสถียรมากกว่าแบบแรก
    3. แตก branch ให้หมด ส่วน trunk เหมือนเป็นที่ share ไฟล์ระหว่าง developer เท่านั้น ใครทำอะไรใหม่มาก็เอามา merge เข้า trunk แล้วก็มีการ sync branch ของตัวเองกับ trunk เป็นระยะๆ โดยการ merge จาก trunk เข้ามาที่ branch แล้วก็ต้องทำการทดสอบด้วย (อาจจะคอมไพล์ และรันเทสต์)  เป็นแบบที่เสถียรสุด

ผมคิดว่าบลอกนี้คงนำเสนอแบบที่ 3 เป็นหลัก เพราะครอบคลุมที่สุด แบบที่ 1 ก็น่าจะทำกันเป็นทั้งแต่ update และ commit เป็นแล้ว :)

การ Branch หรือการแตกกิ่ง

การแตก branch มักจะทำในลักษณะของ Release Branch หรือ Feature Branch โดย Release Branch เป็นการแตกออกมาเพื่อทดสอบครั้งสุดท้ายก่อนการ Release ในขณะที่ trunk ก็จะพัฒนาต่อไปเรื่อยๆ แต่ Feature Branch เป็นการแตกออกมาเพื่อเพิ่ม Feature ใหม่ๆลงไปใน Project เช่น อาจจะให้นาย ก ไปทำฟังก์ชันนึงของเครื่องคิดเลข นาย ข ไปทำอีกอันเป็นต้น สุดท้ายเอามา merge รวมกัน

Release Branch จะเก็บไว้เรื่อยๆเพื่อทำการ maintenance ครับ งานประเภทแก้ไขบัก เพิ่ม feature น้อยๆ ทำให้มีเวอร์ชันพวก 1.1 1.1.5 1.2 ออกมาในขณะที่เวอร์ชัน 2.0 ก็ออกมาแล้ว ส่วน Feature Branch หลัง merge แล้วก็อาจจะลบทิ้งไปได้เลย

การ Merge หรือการผสาน

ปกติการ merge มันหมายถึงการรวมไฟล์สองไฟล์เข้าด้วยกัน ซึ่งก็มักจะเป็นไฟล์ Text และคำที่มักจะมาคู่กันเสมอๆคือคำว่า diff ซึ่งแปลว่าส่วนต่างระหว่างไฟล์สองไฟล์ ซึ่งการ merge ในกรณีที่มีการแก้ไขเพิ่มเติมหรือลบออกจากไฟล์ต้นฉบับโดยคนคนเดียว มันก็ไม่ได้เป็นปัญหาอะไรมากมาย แต่กรณีที่มักจะเป็นปัญหาคือการแก้ไขโดยคนมากกว่า 1 คน ก็ต้องใช้ความสามารถมนุษย์เข้ามาช่วยในการ merge

แต่การ merge ที่ใช้กับ branch มันจะกระทำกับไฟล์หลายๆไฟล์พร้อมๆกัน และก็อาจเกิดกรณีที่เรียกว่า Conflict ขึ้นได้ด้วย (อ่านได้ในแนวคิดพื้นฐาน) ซึ่งการ merge การแก้ไขของ branch กลับเข้าไปที่ trunk มักทำตามขั้นตอนต่อไปนี้ครับ

    1. ทำการ sync branch ของเรากับ trunk ก่อน เป็นการนำการแก้ไขตั้งแต่เราแตก branch ออกมา จนถึง trunk ปัจจุบันเข้ามารวมกับ branch ของเรา สมมติว่าเราแตก branch มาตอน revision 11 แต่ตอนนี้ HEAD revision เป็น revision 23 เอาก็ต้องเอาผลต่างระหว่าง trunk ที่ rev HEAD กับ trunk ที่ rev 11 มารวมกับ branch ของเราก่อน แล้วทำการ merge เข้าไปที่ branch ทำเสร็จเรียบร้อยก็ทดสอบให้ดี แล้ว commit 1 ครั้ง แล้วอย่าลืมใส่ log message เข้าไปด้วยว่า sync กับ trunk ที่ revision เท่าไหร่ (ในกรณีนี้เป็น 23) ทำเสร็จแล้วจะเกิดการแก้ไขขึ้นที่ branch ของเราเท่านั้น โดย trunk ยังคงเหมือนเดิม
    2. ทำการ sync trunk เข้ากับ branch ของเรา ทำได้ไม่ยาก โดยการหาผลต่างระหว่าง branch ของเราที่ rev HEAD กับ trunk ที่ rev HEAD แล้วทำการ merge เข้าไปที่ trunk ทดสอบให้เรียบร้อย แล้วทำการ commit อย่าลืม log message ไว้ให้ดีด้วย ว่า merge กับ branch อะไรยังไง ทำเสร็จแล้วเกิดการแก้ไขที่ trunk โดย branch เหมือนเดิม
    3. ลบ branch ทิ้ง เพราะถ้าเป็นพวก Feature Branch ก็คงไม่ต้องใช้ทำอะไรอีกแล้ว

Tool ดีๆแบบพวก TortoiseSVN จะทำให้เรื่องพวกนี้ทำได้ไม่ยากมากครับ อยู่ในเมนูส่วน Branch/Tag และ Merge ถ้ามีปัญหาก็ Revert กลับไปได้เสมอ รวมถึงการทำ Dry-run ก็สามารถดูผลลัพธ์ก่อนที่จะทำการปฎิบัติการจริงๆได้ และยังมี Diff ช่วยในการดูความแตกต่างของแต่ละไฟล์ที่จะทำการ merge กันได้อีกด้วย

ความวุ่นวายจะเกิดตอนจะรวม Feature Branch หลายๆอันเข้าไปใน trunk มากกว่า ต้องทำการ sync กับ trunk ก่อนทุกครั้ง คงเหนื่อยเอาเรื่อง

สิ่งที่จะมีปัญหาคือการระบุ FROM และ TO ในหน้าต่าง Merge มันอาจจะดูงงๆ เช่น ถ้าเราต้องการจะเอา branch ของเราเข้าไปรวมใน trunk (sync trunk เข้ากับ branch) มันก็น่าจะเป็น FROM branch TO trunk หรืออะไรทำนองนั้น แล้วทำไมมันถึงต้องใช้ FROM trunk TO branch เล่า ? ฟังดูงงยิ่งนัก

วิธีเข้าใจก็คือ ให้มองการใช้ FROM กับ TO เป็นการหาผลต่างครับ เราต้องการหาผลต่างระหว่าง trunk ไปหา branch เพื่อที่จะเอาไปรวมกับ trunk โดยที่ FROM จะใส่อันที่เก่ากว่า และ TO ใส่อันที่ใหม่กว่า พูดแบบนี้เข้าใจมากกว่าครับ และจะช่วยให้เข้าใจในกรณีของการ sync branch เข้ากับ trunk ด้วย ว่าทำไมทั้ง FROM ทั้ง TO ถึงต้องใส่ trunk ทั้งคู่เลย

Merge with Square

(ตัวอย่างหน้าต่าง merge ใน TSVN)

อ่านต่อ

ส่วนใหญ่ก็อ่านมาจาก Manual ของ SVN ลองไปอ่านดู

ตอนต่อไป

จะเอา project SimpleCalculator ที่ลองแตก feature branch ไปสองอัน และลองเล่น merge มาให้ดูครับ (screen shot พร้อมแล้ว) อันนี้จริงๆก็ไม่ได้ว่างมาก แต่อยากมีประสบการณ์ด้านนี้ซักหน่อย เพราะงานที่ทำอยู่คงไม่ได้ใช้ Dev Tool เจ๋งๆพวกนี้ไปซักพักเลยแฮะ ^ ^’

Tags: , , Category: Subversion Tutorials

Comments (6) -

teerapap
teerapap
4/5/2009 3:15:56 AM #

บทความนี้ดี ครับผม

ปล. หญ้านี้มัน Illust. นี่หว่า

teerapap
teerapap
4/5/2009 3:18:04 AM #

อ่อ อีกเรื่อง

จะบอกว่า ที่ทำ Robocup พวกเราไม่มีการแตก branch
ทุกคนทำที่ trunk รวมกันหมด Tong  (เหมือนจะมีข้อดีคือ คนอื่นช่วยเทสต์บั๊กให้ด้วย)

wiennat
wiennat
4/5/2009 10:39:26 AM #

ใช้ git อะ

m3rlinez
m3rlinez
4/5/2009 7:08:56 PM #

@teerapap แปลว่าบทความอื่นไม่ดีเหรอครับ Smile
@wienat git ดี .. ยังไง 555+

wiennat
wiennat
4/6/2009 12:16:37 PM #

offline commit อะ

แล้วมันทำ branch สะดวกมาก

m3rlinez
m3rlinez
4/6/2009 12:17:40 PM #

สงสัยต้องลอง branch ในพวก DVCS ซะแล้ว สรรพคุณหนาหูเหลือเกิน o__O

Add comment




biuquote
  • Comment
  • Preview
Loading






Most comments

khimkhim khimkhim
1 comments
weaw weaw
1 comments
domehuhu domehuhu
1 comments

RecentComments

Comment RSS