มาต่อจากตอนที่แล้วด้วยเรื่องของ Branch และ Merge ครับ เรื่องนี้เป็นหัวใจของระบบ Version Control เลยนะเนี่ย
การจัด Repository
มาพูดถึงการจัด Repository ก่อนครับ โดยปกติแล้วเค้าจะจัดกันแบบนี้RepoRoot
--- ProjectA
--- --- trunk
--- --- branches
--- --- --- branchAถ้าใช้ TortoiseSVN อยู่ คำว่า “จัด” ที่ผมใช้นีหมายถึงให้เปิด Repo Browser ขึ้นมาแล้วสร้าง Structure ตามตัวหนาด้านบนนะครับ
--- --- --- branchB
--- --- tags
--- --- --- 1.0.0
--- --- --- 1.1.5
--- ProjectB
ส่วนที่สำคัญคือ 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 อะไรแบบนี้ก็ได้จะได้ไม่มากันทั้งครอบครัว
รูปแบบการทำงานร่วมกันบน Subversion
เท่าที่อ่านมามี 3 แบบ-
- ไม่ต้องแตก branch ทำบน trunk อย่างเดียว อันนี้ก็อาจจะมีปัญหาในกรณีที่แก้ไขโดยคนใดคนหนึ่งแล้วมันเจ๊ง ทำให้ trunk ไม่เสถียรเท่าไหร่
- แตก branch เป็นกรณีพิเศษ แต่งานส่วนใหญ่ยังทำกันบน trunk ในกรณีที่ต้องแก้ไขอะไรยิ่งใหญ่มากจนอาจทำให้ trunk ไม่เสถียร (คอมไพล์ไม่ผ่าน รันเทสต์แล้วเน่า) เสถียรมากกว่าแบบแรก
- แตก branch ให้หมด ส่วน trunk เหมือนเป็นที่ share ไฟล์ระหว่าง developer เท่านั้น ใครทำอะไรใหม่มาก็เอามา merge เข้า trunk แล้วก็มีการ sync branch ของตัวเองกับ trunk เป็นระยะๆ โดยการ merge จาก trunk เข้ามาที่ branch แล้วก็ต้องทำการทดสอบด้วย (อาจจะคอมไพล์ และรันเทสต์) เป็นแบบที่เสถียรสุด
การ 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 มักทำตามขั้นตอนต่อไปนี้ครับ
-
- ทำการ 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 ยังคงเหมือนเดิม
- ทำการ sync trunk เข้ากับ branch ของเรา ทำได้ไม่ยาก โดยการหาผลต่างระหว่าง branch ของเราที่ rev HEAD กับ trunk ที่ rev HEAD แล้วทำการ merge เข้าไปที่ trunk ทดสอบให้เรียบร้อย แล้วทำการ commit อย่าลืม log message ไว้ให้ดีด้วย ว่า merge กับ branch อะไรยังไง ทำเสร็จแล้วเกิดการแก้ไขที่ trunk โดย branch เหมือนเดิม
- ลบ branch ทิ้ง เพราะถ้าเป็นพวก Feature Branch ก็คงไม่ต้องใช้ทำอะไรอีกแล้ว
ความวุ่นวายจะเกิดตอนจะรวม Feature Branch หลายๆอันเข้าไปใน trunk มากกว่า ต้องทำการ sync กับ trunk ก่อนทุกครั้ง คงเหนื่อยเอาเรื่อง
สิ่งที่จะมีปัญหาคือการระบุ FROM และ TOในหน้าต่าง Merge มันอาจจะดูงงๆ เช่น ถ้าเราต้องการจะเอา branchของเราเข้าไปรวมใน trunk (sync trunk เข้ากับ branch) มันก็น่าจะเป็น FROMbranch TO trunk หรืออะไรทำนองนั้น แล้วทำไมมันถึงต้องใช้ FROM trunk TO branchเล่า ? ฟังดูงงยิ่งนัก
วิธีเข้าใจก็คือ ให้มองการใช้ FROM กับ TO เป็นการหาผลต่างครับเราต้องการหาผลต่างระหว่าง trunk ไปหา branch เพื่อที่จะเอาไปรวมกับ trunkโดยที่ FROM จะใส่อันที่เก่ากว่า และ TO ใส่อันที่ใหม่กว่าพูดแบบนี้เข้าใจมากกว่าครับ และจะช่วยให้เข้าใจในกรณีของการ sync branchเข้ากับ trunk ด้วย ว่าทำไมทั้ง FROM ทั้ง TO ถึงต้องใส่ trunk ทั้งคู่เลย
Comments
Post a Comment