บางครั้งมันเป็นไปไม่ได้อย่างมีประสิทธิภาพ (ยกเว้นบางตำแหน่งที่คุณอาจโชคดีที่มีข้อมูลเพิ่มเติม) และวิธีแก้ปัญหาที่นี่จะไม่ทำงาน
Git ไม่เก็บประวัติการอ้างอิง (ซึ่งรวมถึงสาขา) มันเก็บเฉพาะตำแหน่งปัจจุบันสำหรับแต่ละสาขา (หัว) ซึ่งหมายความว่าคุณสามารถสูญเสียประวัติสาขาบางส่วนในคอมไพล์เมื่อเวลาผ่านไป เมื่อใดก็ตามที่คุณแยกสาขามันจะหายไปทันทีว่าสาขาไหนเป็นสาขาดั้งเดิม สาขาทั้งหมดทำคือ:
git checkout branch1 # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1
คุณอาจสันนิษฐานว่าคนแรกที่ยอมรับคือสาขา เรื่องนี้มีแนวโน้มที่จะเป็นอย่างนั้น แต่ก็ไม่เสมอไป ไม่มีอะไรหยุดยั้งคุณไม่ให้ผูกมัดกับสาขาใดสาขาแรกหลังจากการดำเนินการด้านบน นอกจากนี้การประทับเวลา Git ไม่รับประกันว่าจะเชื่อถือได้ มันไม่ได้จนกว่าคุณจะผูกพันกับทั้งสองว่าพวกเขากลายเป็นสาขาที่มีโครงสร้างอย่างแท้จริง
ในขณะที่อยู่ในไดอะแกรมเรามักจะให้ความสำคัญกับความคิดรวบยอด แต่คอมไพล์ไม่มีแนวคิดของลำดับที่มั่นคงจริง ๆ เมื่อกระทำกิ่งไม้กิ่ง ในกรณีนี้คุณสามารถสันนิษฐานได้ว่าตัวเลข (ลำดับที่ระบุ) ถูกกำหนดโดยการประทับเวลา (มันอาจสนุกที่ได้เห็นว่า git UI จัดการสิ่งต่าง ๆ อย่างไรเมื่อคุณตั้งค่าการประทับเวลาทั้งหมดเป็นแบบเดียวกัน)
นี่คือสิ่งที่มนุษย์คาดหวังตามแนวคิด:
After branch:
C1 (B1)
/
-
\
C1 (B2)
After first commit:
C1 (B1)
/
-
\
C1 - C2 (B2)
นี่คือสิ่งที่คุณได้รับ:
After branch:
- C1 (B1) (B2)
After first commit (human):
- C1 (B1)
\
C2 (B2)
After first commit (real):
- C1 (B1) - C2 (B2)
คุณจะถือว่า B1 เป็นสาขาดั้งเดิม แต่มันอาจเป็นเพียงแรงผลักดันให้เป็นสาขาที่ตายแล้ว (มีคนชำระเงิน -b แต่ไม่เคยมุ่งมั่นที่จะทำมัน) มันไม่ได้จนกว่าคุณจะยอมรับทั้งคู่ว่าคุณได้รับโครงสร้างสาขาที่ถูกต้องภายใน git
Either:
/ - C2 (B1)
-- C1
\ - C3 (B2)
Or:
/ - C3 (B1)
-- C1
\ - C2 (B2)
คุณรู้อยู่เสมอว่า C1 มาก่อน C2 และ C3 แต่คุณไม่เคยรู้อย่างน่าเชื่อถือว่า C2 มาก่อน C3 หรือ C3 มาก่อน C2 (เพราะคุณสามารถตั้งเวลาบนเวิร์กสเตชันของคุณเป็นอะไรก็ได้) B1 และ B2 ก็สร้างความเข้าใจผิดด้วยเพราะคุณไม่รู้ว่าสาขาไหนมาก่อน คุณสามารถคาดเดาได้ดีมากและแม่นยำในหลายกรณี มันเป็นเหมือนสนามแข่ง โดยทั่วไปทุกอย่างเท่าเทียมกันกับรถยนต์คุณสามารถสมมติได้ว่ารถยนต์ที่มาในรอบหลังเริ่มตักข้างหลัง นอกจากนี้เรายังมีการประชุมที่มีความน่าเชื่อถือมากตัวอย่างเช่นท่านอาจารย์จะเป็นตัวแทนของกิ่งไม้ที่มีอายุยืนยาวที่สุดแม้จะเศร้าที่ฉันเห็นกรณีที่แม้จะไม่เป็นเช่นนั้นก็ตาม
ตัวอย่างที่ให้ไว้ที่นี่เป็นตัวอย่างการรักษาประวัติ:
Human:
- X - A - B - C - D - F (B1)
\ / \ /
G - H ----- I - J (B2)
Real:
B ----- C - D - F (B1)
/ / \ /
- X - A / \ /
\ / \ /
G - H ----- I - J (B2)
ความจริงที่นี่ยังทำให้เข้าใจผิดเพราะเราในขณะที่มนุษย์อ่านจากซ้ายไปขวารูตไปยังใบ (อ้างอิง) Git ไม่ทำเช่นนั้น จุดที่เราทำ (A-> B) ในหัวคอมไพล์ทำ (A <-B หรือ B-> A) มันอ่านได้จากการอ้างอิงถึงรูต การอ้างอิงสามารถอยู่ที่ใดก็ได้ แต่มีแนวโน้มที่จะเป็นใบไม้ การอ้างอิงชี้ไปที่การคอมมิทและการคอมมิชชันมีเพียงสิ่งที่ชอบไปยังผู้ปกครองไม่ใช่เพื่อลูก ๆ ของพวกเขา เมื่อคอมมิชชันเป็นการรวมการคอมมิทมันจะมีพาเรนต์มากกว่าหนึ่งอัน พาเรนต์แรกจะเป็นคอมมิทดั้งเดิมที่ถูกรวมเข้าด้วยเสมอ ผู้ปกครองคนอื่น ๆ มักจะกระทำที่ถูกรวมเข้ากับการกระทำเดิม
Paths:
F->(D->(C->(B->(A->X)),(H->(G->(A->X))))),(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
J->(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
นี่ไม่ใช่การแสดงที่มีประสิทธิภาพมากนัก แต่เป็นการแสดงออกของเส้นทางทั้งหมดที่สามารถนำมาจากการอ้างอิงแต่ละครั้ง (B1 และ B2)
ที่เก็บข้อมูลภายในของ Git มีลักษณะดังนี้ (ไม่ใช่ว่า A ในฐานะผู้ปกครองปรากฏสองครั้ง):
F->D,I | D->C | C->B,H | B->A | A->X | J->I | I->H,C | H->G | G->A
หากคุณถ่ายโอนคอมไพล์คอมมิตคุณจะเห็นฟิลด์หลักเป็นศูนย์หรือมากกว่า หากมีศูนย์ก็หมายความว่าไม่มีผู้ปกครองและการกระทำที่เป็นราก (คุณสามารถมีได้หลายราก) หากมีอย่างใดอย่างหนึ่งก็หมายความว่าไม่มีการผสานและไม่ใช่การผูกมัด หากมีมากกว่าหนึ่งแสดงว่าการคอมมิชชันนั้นเป็นผลของการรวมและผู้ปกครองทั้งหมดหลังจากการรวมครั้งแรกเป็นคอมมิท
Paths simplified:
F->(D->C),I | J->I | I->H,C | C->(B->A),H | H->(G->A) | A->X
Paths first parents only:
F->(D->(C->(B->(A->X)))) | F->D->C->B->A->X
J->(I->(H->(G->(A->X))) | J->I->H->G->A->X
Or:
F->D->C | J->I | I->H | C->B->A | H->G->A | A->X
Paths first parents only simplified:
F->D->C->B->A | J->I->->G->A | A->X
Topological:
- X - A - B - C - D - F (B1)
\
G - H - I - J (B2)
เมื่อทั้งคู่โดนโซ่ของพวกเขาจะเหมือนกันก่อนหน้านั้นโซ่ของพวกเขาจะแตกต่างกันอย่างสิ้นเชิง คนแรกที่กระทำร่วมกันอีกสองคนคือการกระทำร่วมกันคือบรรพบุรุษและจากที่พวกเขาแยกออกจากกัน อาจมีความสับสนระหว่างคำที่กระทำสาขาและผู้อ้างอิง ในความเป็นจริงคุณสามารถรวมการกระทำ นี่คือสิ่งที่ผสานกันจริงๆ การอ้างอิงเพียงชี้ไปที่การคอมมิชชันและสาขาจะไม่มีอะไรมากไปกว่าการอ้างอิงในโฟลเดอร์. git / refs / heads ตำแหน่งของโฟลเดอร์คือสิ่งที่กำหนดว่าการอ้างอิงนั้นเป็นสาขามากกว่าสิ่งอื่นเช่นแท็ก
ที่ที่คุณสูญเสียประวัติคือการผสานจะทำหนึ่งในสองสิ่งนี้ขึ้นอยู่กับสถานการณ์
พิจารณา:
/ - B (B1)
- A
\ - C (B2)
ในกรณีนี้การผสานในทิศทางใดทิศทางหนึ่งจะสร้างการคอมมิทใหม่กับพาเรนต์แรกที่คอมมิชชันชี้ไปที่สาขาปัจจุบันที่เช็คเอาท์และพาเรนต์ที่สองเป็นการกระทำที่ปลายสุดของสาขาที่คุณผสานเข้ากับสาขาปัจจุบันของคุณ มันต้องสร้างการคอมมิชชันใหม่เนื่องจากทั้งสองสาขามีการเปลี่ยนแปลงตั้งแต่บรรพบุรุษร่วมที่ต้องรวมเข้าด้วยกัน
/ - B - D (B1)
- A /
\ --- C (B2)
ณ จุดนี้ D (B1) ตอนนี้มีการเปลี่ยนแปลงทั้งสองชุดจากทั้งสองสาขา (ตัวเองและ B2) อย่างไรก็ตามสาขาที่สองไม่มีการเปลี่ยนแปลงจาก B1 หากคุณรวมการเปลี่ยนแปลงจาก B1 เป็น B2 เพื่อให้ซิงค์แล้วคุณอาจคาดหวังสิ่งที่มีลักษณะเช่นนี้ (คุณสามารถบังคับให้รวม git ทำเช่นนี้ได้อย่างไรก็ตามด้วย --no-ff):
Expected:
/ - B - D (B1)
- A / \
\ --- C - E (B2)
Reality:
/ - B - D (B1) (B2)
- A /
\ --- C
คุณจะได้รับแม้ว่า B1 จะมีภาระผูกพันเพิ่มเติมก็ตาม ตราบใดที่ไม่มีการเปลี่ยนแปลงใน B2 ที่ B1 ไม่มีทั้งสองสาขาจะถูกรวมเข้าด้วยกัน มันจะกรอไปข้างหน้าซึ่งเป็นเหมือน rebase (rebases ยังกินหรือเป็นประวัติเชิงเส้น) ยกเว้นการ rebase เนื่องจากมีเพียงสาขาเดียวเท่านั้นที่มีชุดการเปลี่ยนแปลงจึงไม่จำเป็นต้องใช้เซ็ตการแก้ไขจากสาขาหนึ่งที่อยู่ด้านบนของอีกสาขาหนึ่ง
From:
/ - B - D - E (B1)
- A /
\ --- C (B2)
To:
/ - B - D - E (B1) (B2)
- A /
\ --- C
หากคุณหยุดทำงานใน B1 สิ่งต่าง ๆ เป็นเรื่องปกติสำหรับการรักษาประวัติศาสตร์ในระยะยาว โดยทั่วไปแล้วจะมีเพียง B1 (ซึ่งอาจเป็นผู้เชี่ยวชาญ) ดังนั้นตำแหน่งของ B2 ในประวัติของ B2 จะแสดงถึงจุดที่ถูกรวมเข้ากับ B1 ได้สำเร็จ นี่คือสิ่งที่ git คาดหวังให้คุณทำกับสาขา B จาก A จากนั้นคุณสามารถรวม A เป็น B ได้มากเท่าที่คุณชอบเมื่อมีการเปลี่ยนแปลงสะสม แต่เมื่อรวม B กลับเข้า A คุณไม่คาดหวังว่าคุณจะทำงานกับ B และต่อไป . หากคุณยังคงทำงานในสาขาของคุณหลังจากการส่งต่ออย่างรวดเร็วไปยังสาขาที่คุณกำลังดำเนินการอยู่ดังนั้นประวัติของคุณที่ลบ B ในแต่ละครั้ง คุณกำลังสร้างสาขาใหม่ในแต่ละครั้งหลังจากส่งต่ออย่างรวดเร็วไปยังแหล่งที่มาแล้วส่งไปยังสาขา
0 1 2 3 4 (B1)
/-\ /-\ /-\ /-\ /
---- - - - -
\-/ \-/ \-/ \-/ \
5 6 7 8 9 (B2)
1 ถึง 3 และ 5 ถึง 8 เป็นกิ่งที่มีโครงสร้างที่ปรากฏขึ้นหากคุณตามประวัติศาสตร์ทั้ง 4 หรือ 9 ไม่มีทางใดที่จะรู้ได้ว่าสาขาโครงสร้างที่ไม่มีชื่อและไม่อ้างอิงนั้นเป็นของสาขาที่มีชื่อและการอ้างอิงเป็น ในตอนท้ายของโครงสร้าง คุณอาจสมมุติจากภาพวาดนี้ว่า 0 ถึง 4 เป็นของ B1 และ 4 ถึง 9 เป็นของ B2 แต่นอกเหนือจาก 4 และ 9 ไม่สามารถรู้ได้ว่าสาขาไหนเป็นสาขาใดฉันก็วาดมันในแบบที่ให้ ภาพลวงตาของสิ่งนั้น 0 อาจเป็นของ B2 และ 5 อาจเป็นของ B1 มีกรณีที่แตกต่างกัน 16 กรณีในกรณีนี้ซึ่งชื่อสาขาแต่ละสาขาโครงสร้างอาจเป็นของ
มีกลยุทธ์ git จำนวนหนึ่งที่สามารถแก้ไขได้ คุณสามารถบังคับให้คอมไพล์ผสานไม่ต้องกรอไปข้างหน้าและสร้างสาขาที่ผสานอยู่เสมอ วิธีที่น่ากลัวในการรักษาประวัติสาขาคือใช้แท็กและ / หรือสาขา (แนะนำให้ใช้แท็กจริงๆ) ตามแบบแผนที่คุณเลือก ฉันไม่อยากจะแนะนำการกระทำที่ไร้สาระในสาขาที่คุณกำลังรวมเข้าด้วยกัน หลักการทั่วไปคือการไม่รวมเข้ากับสาขาบูรณาการจนกว่าคุณจะต้องการปิดสาขาของคุณอย่างแท้จริง นี่คือการปฏิบัติที่ผู้คนควรพยายามยึดถือเป็นอย่างอื่นคุณกำลังทำงานในจุดที่มีสาขา อย่างไรก็ตามในโลกแห่งความเป็นจริงอุดมคติไม่ได้มีความหมายในทางปฏิบัติเสมอไปการทำสิ่งที่ถูกต้องนั้นไม่สามารถใช้ได้กับทุกสถานการณ์ ถ้าคุณเป็นอะไร