นี่เป็นเพราะข้อ จำกัด ของอัลกอริทึมดั้งเดิม เมื่อจัดการผสานคอมมิตอัลกอริทึมดั้งเดิมจะใช้เกณฑ์ที่เรียบง่ายในการตัดพาเรนต์ที่ไม่เกี่ยวข้องออกไป โดยเฉพาะอย่างยิ่งจะตรวจสอบว่ามีต้นแม่ที่มีต้นไม้เหมือนกันหรือไม่ หากพบพาเรนต์ดังกล่าวจะยุบคอมมิตการผสานและใช้พาเรนต์คอมมิตแทนโดยสมมติว่าพาเรนต์อื่นมีการเปลี่ยนแปลงที่ไม่เกี่ยวข้องกับซับทรี ในบางกรณีอาจส่งผลให้บางส่วนของประวัติลดลงซึ่งมีการเปลี่ยนแปลงโครงสร้างย่อยจริง โดยเฉพาะอย่างยิ่งมันจะทิ้งลำดับของการคอมมิตซึ่งจะแตะต้นไม้ย่อย แต่ส่งผลให้มีค่าต้นไม้ย่อยเหมือนกัน
มาดูตัวอย่าง (ซึ่งคุณสามารถทำซ้ำได้อย่างง่ายดาย) เพื่อทำความเข้าใจวิธีการทำงานให้ดียิ่งขึ้น พิจารณาประวัติต่อไปนี้ (รูปแบบบรรทัดคือ: กระทำ [ต้นไม้] เรื่อง):
% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
* E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
* A [w] add dir/file1.txt
ในตัวอย่างนี้เรากำลังแยกdir
ส่วน ยอมรับD
และE
มีทรีเดียวกันz
เนื่องจากเราได้กระทำC
ซึ่งยกเลิกการกระทำB
ดังนั้นB-C
ลำดับจึงไม่ทำอะไรเลยdir
แม้ว่าจะมีการเปลี่ยนแปลงก็ตาม
ตอนนี้มาแยกกัน C
ครั้งแรกที่เราแยกกระทำ
% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt
E
ต่อไปเราแยกกระทำ
% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt
ใช่เราสูญเสียการกระทำสองครั้ง สิ่งนี้ส่งผลให้เกิดข้อผิดพลาดเมื่อพยายามดันการแยกที่สองเนื่องจากไม่มีการคอมมิตทั้งสองนั้นซึ่งได้เข้าสู่จุดเริ่มต้นแล้ว
โดยปกติคุณสามารถยอมรับข้อผิดพลาดนี้ได้โดยใช้push --force
เนื่องจากการกระทำที่ตกหล่นโดยทั่วไปจะไม่มีข้อมูลที่สำคัญอยู่ในนั้น ในระยะยาวข้อบกพร่องจะต้องได้รับการแก้ไขดังนั้นประวัติการแบ่งจะมีการคอมมิตทั้งหมดซึ่งสัมผัสdir
ได้ตามที่คาดไว้ ฉันคาดหวังว่าการแก้ไขจะรวมการวิเคราะห์ที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับการกระทำของผู้ปกครองสำหรับการอ้างอิงที่ซ่อนอยู่
สำหรับการอ้างอิงนี่คือส่วนของรหัสต้นฉบับที่รับผิดชอบพฤติกรรม
copy_or_skip()
...
for parent in $newparents; do
ptree=$(toptree_for_commit $parent) || exit $?
[ -z "$ptree" ] && continue
if [ "$ptree" = "$tree" ]; then
identical="$parent"
else
nonidentical="$parent"
fi
...
if [ -n "$identical" ]; then
echo $identical
else
copy_commit $rev $tree "$p" || exit $?
fi