การอ้างเหตุผลว่าทำไมการรวมกันใน DVCS ที่ดีกว่าในการโค่นล้มส่วนใหญ่มาจากการทำงานของการรวมสาขาและการผสานในการโค่นล้มเมื่อนานมาแล้ว การโค่นล้มก่อนหน้า1.5.0ไม่ได้เก็บข้อมูลใด ๆ เกี่ยวกับเวลาที่มีการรวมสาขาดังนั้นเมื่อคุณต้องการผสานคุณจะต้องระบุช่วงของการแก้ไขที่ต้องรวมเข้าด้วยกัน
เหตุใดการโค่นล้มจึงผสานดูด ?
ไตร่ตรองตัวอย่างนี้:
1 2 4 6 8
trunk o-->o-->o---->o---->o
\
\ 3 5 7
b1 +->o---->o---->o
เมื่อเราต้องการรวมการเปลี่ยนแปลงของ b1 เข้ากับ trunk เราจะออกคำสั่งต่อไปนี้ในขณะที่ยืนอยู่บนโฟลเดอร์ที่มี trunk ตรวจสอบแล้ว:
svn merge -r 2:7 {link to branch b1}
... ซึ่งจะพยายามรวมการเปลี่ยนแปลงจากb1
ในไดเรกทอรีทำงานท้องถิ่นของคุณ จากนั้นคุณยอมรับการเปลี่ยนแปลงหลังจากที่คุณแก้ไขข้อขัดแย้งและทดสอบผลลัพธ์ เมื่อคุณส่งแผนผังการแก้ไขจะมีลักษณะเช่นนี้:
1 2 4 6 8 9
trunk o-->o-->o---->o---->o-->o "the merge commit is at r9"
\
\ 3 5 7
b1 +->o---->o---->o
อย่างไรก็ตามวิธีนี้ในการระบุช่วงของการแก้ไขได้อย่างรวดเร็วเมื่อต้นไม้เวอร์ชันเติบโตขึ้นเนื่องจากการโค่นล้มไม่ได้มีข้อมูลเมตาใด ๆ เมื่อใดและการแก้ไขใดที่ผสานเข้าด้วยกัน ไตร่ตรองว่าจะเกิดอะไรขึ้นในภายหลัง:
12 14
trunk …-->o-------->o
"Okay, so when did we merge last time?"
13 15
b1 …----->o-------->o
นี่เป็นปัญหาส่วนใหญ่จากการออกแบบพื้นที่เก็บข้อมูลที่มีการโค่นล้มเพื่อสร้างสาขาที่คุณต้องการในการสร้างไดเรกทอรีเสมือนใหม่ในพื้นที่เก็บข้อมูลซึ่งจะเก็บสำเนาของลำต้น แต่มันไม่เก็บข้อมูลใด ๆ เกี่ยวกับเวลาและสิ่งที่ สิ่งต่าง ๆ ได้รับการผสานกลับเข้ามาซึ่งจะนำไปสู่ความขัดแย้งที่น่ารังเกียจในบางครั้ง สิ่งที่เลวร้ายยิ่งกว่านั้นคือการโค่นล้มใช้การรวมแบบสองทางโดยค่าเริ่มต้นซึ่งมีข้อ จำกัด ที่ทำให้หมดอำนาจในการรวมอัตโนมัติเมื่อหัวสาขาสองหัวไม่เปรียบเทียบกับบรรพบุรุษร่วมกัน
เพื่อลดการโค่นล้มนี้ตอนนี้เก็บข้อมูล meta สำหรับสาขาและผสาน นั่นจะแก้ปัญหาทั้งหมดใช่ไหม
และโอ้โดยวิธีการโค่นล้มยังคงดูด ...
บนระบบรวมศูนย์เช่นการโค่นล้มไดเรกทอรีเสมือนดูด ทำไม? เพราะทุกคนมีสิทธิ์เข้าถึงเพื่อดู ... แม้กระทั่งกลุ่มทดลองขยะ การแตกแขนงเป็นสิ่งที่ดีถ้าคุณต้องการที่จะทดสอบแต่คุณไม่ต้องการที่จะเห็นทุกคนและป้าของพวกเขาทดลอง นี่คือเสียงทางปัญญาที่รุนแรง ยิ่งคุณเพิ่มสาขามากเท่าไหร่คุณก็จะยิ่งเห็นอึมากขึ้นเท่านั้น
ยิ่งสาขาสาธารณะของคุณมีพื้นที่เก็บข้อมูลมากเท่าไหร่ก็ยิ่งยากที่จะติดตามสาขาที่แตกต่างกันทั้งหมด ดังนั้นคำถามที่คุณจะได้คือถ้าสาขายังอยู่ในระหว่างการพัฒนาหรือถ้ามันตายจริง ๆ ซึ่งยากที่จะบอกในระบบควบคุมเวอร์ชันรวมศูนย์
ส่วนใหญ่แล้วจากสิ่งที่ฉันได้เห็นองค์กรจะเริ่มต้นใช้สาขาขนาดใหญ่หนึ่งสาขาต่อไป ซึ่งเป็นความอัปยศเพราะในทางกลับกันจะเป็นการยากที่จะติดตามการทดสอบและปล่อยรุ่นและสิ่งอื่น ๆ ที่ดีมาจากการแตกแขนง
เหตุใด DVCS เช่น Git, Mercurial และ Bazaar จึงดีกว่าการโค่นล้มในการแยกและรวม?
มีเหตุผลง่าย ๆ ว่าทำไม: การแยกเป็นแนวคิดชั้นหนึ่ง ขณะนี้ไม่มีไดเรกทอรีเสมือนโดยการออกแบบและสาขาวัตถุแข็งใน DVCS ซึ่งจะต้องมีการดังกล่าวเพื่อไปทำงานก็มีการประสานของที่เก็บ (เช่นการผลักดันและดึง )
สิ่งแรกที่คุณทำเมื่อคุณทำงานกับ DVCS คือการโคลนที่เก็บข้อมูล (git's clone
, hg's clone
และ bzr's branch
) การโคลนเป็นแนวคิดเหมือนกับการสร้างสาขาในการควบคุมเวอร์ชัน บางคนเรียกว่าฟอร์กกิ้งหรือการแตกกิ่ง (แม้ว่าอันหลังมักจะใช้เพื่ออ้างถึงสาขาที่อยู่ร่วมกัน) แต่มันก็เป็นสิ่งเดียวกัน ผู้ใช้ทุกคนใช้ที่เก็บข้อมูลของตัวเองซึ่งหมายความว่าคุณมีสาขาต่อผู้ใช้ที่เกิดขึ้น
โครงสร้างเวอร์ชันไม่ใช่แผนผังแต่เป็นกราฟแทน โดยเฉพาะอย่างยิ่งกราฟ acyclic โดยตรง (DAG หมายถึงกราฟที่ไม่มีรอบใด ๆ ) คุณไม่จำเป็นต้องอาศัยข้อมูลเฉพาะของ DAG นอกเหนือจากการกระทำแต่ละอย่างมีการอ้างอิงผู้ปกครองตั้งแต่หนึ่งรายการขึ้นไป (ซึ่งการกระทำนั้นอิงตาม) ดังนั้นกราฟต่อไปนี้จะแสดงลูกศรระหว่างการแก้ไขแบบย้อนกลับเนื่องจากสิ่งนี้
ตัวอย่างง่ายๆของการรวมจะเป็นนี้ ลองนึกภาพที่เก็บส่วนกลางเรียกว่าorigin
และผู้ใช้อลิซโคลนที่เก็บไปยังเครื่องของเธอ
a… b… c…
origin o<---o<---o
^master
|
| clone
v
a… b… c…
alice o<---o<---o
^master
^origin/master
สิ่งที่เกิดขึ้นในระหว่างการโคลนนิ่งคือการแก้ไขทุกครั้งจะถูกคัดลอกไปยังอลิซเหมือนกับที่เคยทำ (ซึ่งตรวจสอบโดยรหัสแฮช - ไอดีที่ไม่ซ้ำกัน) และทำเครื่องหมายว่ากิ่งของต้นกำเนิดอยู่ที่ใด
อลิซทำงานกับ repo ของเธอกระทำในที่เก็บของเธอและตัดสินใจที่จะผลักดันการเปลี่ยนแปลงของเธอ:
a… b… c…
origin o<---o<---o
^ master
"what'll happen after a push?"
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
วิธีแก้ปัญหาค่อนข้างง่ายสิ่งเดียวที่origin
พื้นที่เก็บข้อมูลต้องทำคือนำการแก้ไขใหม่ทั้งหมดและย้ายสาขาไปเป็นการแก้ไขล่าสุด (ซึ่ง git เรียกว่า "กรอไปข้างหน้า"):
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
กรณีการใช้งานที่ฉันแสดงไว้ด้านบนไม่จำเป็นต้องรวมอะไรเลยด้วยซ้ำ ดังนั้นปัญหาไม่ได้เกิดจากการผสานอัลกอริธึมเนื่องจากอัลกอริธึมผสานสามทางนั้นค่อนข้างเหมือนกันระหว่างระบบควบคุมเวอร์ชันทั้งหมด ปัญหาคือโครงสร้างเกี่ยวกับมากกว่าสิ่งใด
แล้วคุณจะแสดงตัวอย่างที่มีการผสานที่แท้จริงได้อย่างไร
ตัวอย่างข้างต้นเป็นที่ยอมรับว่าเป็นกรณีการใช้งานที่ง่ายมากดังนั้นให้ลองทำสิ่งที่บิดเป็นเกลียวมากขึ้นแม้ว่าจะเป็นเรื่องธรรมดา จำได้ไหมว่าorigin
เริ่มด้วยการแก้ไขสามครั้ง? เอ่อคนที่เป็นคนพวกนั้นเรียกเขาว่าบ็อบทำงานด้วยตัวเขาเองและทำหน้าที่เก็บข้อมูลของตัวเอง:
a… b… c… f…
bob o<---o<---o<---o
^ master
^ origin/master
"can Bob push his changes?"
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
ตอนนี้บ๊อบไม่สามารถผลักดันการเปลี่ยนแปลงของเขาโดยตรงไปยังที่origin
เก็บ วิธีที่ระบบตรวจพบสิ่งนี้คือการตรวจสอบว่าการแก้ไขของบ๊อบสืบทอดโดยตรงจากorigin
ของซึ่งในกรณีนี้จะไม่ ความพยายามใด ๆ ที่จะผลักดันจะส่งผลให้ระบบพูดอะไรบางอย่างที่คล้ายกับ " เอ่อ ... ฉันเกรงว่าคุณจะไม่ทำอย่างนั้นกับบ๊อบ "
ดังนั้น Bob ต้องดึงและรวมการเปลี่ยนแปลง (กับ git's pull
; หรือ hg's pull
and merge
; หรือ bzr's merge
) นี่เป็นกระบวนการสองขั้นตอน Bob คนแรกต้องดึงข้อมูลการแก้ไขใหม่ซึ่งจะคัดลอกมันจากที่origin
เก็บ ตอนนี้เราสามารถเห็นได้ว่ากราฟเบี่ยงเบน:
v master
a… b… c… f…
bob o<---o<---o<---o
^
| d… e…
+----o<---o
^ origin/master
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
ขั้นตอนที่สองของกระบวนการดึงคือการรวมเคล็ดลับการแยกและทำการกระทำของผล:
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
^ origin/master
หวังว่าการผสานจะไม่เกิดข้อขัดแย้ง (หากคุณคาดว่าจะรวมคุณสามารถทำสองขั้นตอนด้วยตนเองในคอมไพล์ด้วยfetch
และmerge
) สิ่งที่ต้องทำในภายหลังคือการผลักดันการเปลี่ยนแปลงเหล่านั้นอีกครั้งorigin
ซึ่งจะส่งผลให้มีการผสานไปข้างหน้าอย่างรวดเร็วเนื่องจากการรวมการกระทำเป็นผู้สืบทอดโดยตรงของorigin
แหล่งข้อมูลล่าสุดในที่เก็บ:
v origin/master
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
v master
a… b… c… f… 1…
origin o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
มีอีกตัวเลือกในการรวมเป็น git และ hg เรียกว่าrebaseซึ่งจะย้ายการเปลี่ยนแปลงของ Bob ไปหลังจากการเปลี่ยนแปลงล่าสุด เนื่องจากฉันไม่ต้องการคำตอบนี้อีกต่อไปฉันจะให้คุณอ่านgit , mercurialหรือbazaar docs เกี่ยวกับสิ่งนั้นแทน
เป็นแบบฝึกหัดสำหรับผู้อ่านลองวาดว่ามันจะทำงานอย่างไรกับผู้ใช้รายอื่นที่เกี่ยวข้อง มันทำแบบเดียวกับตัวอย่างด้านบนกับ Bob การผสานระหว่างที่เก็บข้อมูลนั้นง่ายกว่าที่คุณคิดเพราะการแก้ไข / การกระทำทั้งหมดนั้นสามารถระบุตัวตนได้อย่างมีเอกลักษณ์
นอกจากนี้ยังมีปัญหาในการส่งแพตช์ระหว่างผู้พัฒนาแต่ละรายซึ่งเป็นปัญหาใหญ่ในการโค่นล้มซึ่งบรรเทาลงใน git, hg และ bzr โดยการแก้ไขที่ไม่ซ้ำกัน เมื่อมีคนรวมการเปลี่ยนแปลงของเขา (เช่นทำการรวมการกระทำ) และส่งให้ทุกคนในทีมกินโดยการกดไปที่ที่เก็บส่วนกลางหรือส่งแพตช์จากนั้นพวกเขาไม่ต้องกังวลเกี่ยวกับการผสานเพราะมันเกิดขึ้นแล้ว . ฟาวเลอร์มาร์ตินเรียกวิธีการทำงานนี้บูรณาการสำส่อน
เนื่องจากโครงสร้างแตกต่างจากการโค่นล้มโดยแทนที่จะใช้ DAG จึงช่วยให้การแยกและการผสานทำได้ง่ายขึ้นไม่เพียง แต่สำหรับระบบ แต่สำหรับผู้ใช้ด้วยเช่นกัน