Điều này là do giới hạn của thuật toán ban đầu. Khi xử lý các cam kết hợp nhất, thuật toán ban đầu sử dụng tiêu chí đơn giản hóa để cắt bỏ các cha mẹ không liên quan. Đặc biệt, nó kiểm tra, nếu có cha mẹ, có cùng một cây. Nếu tìm thấy cha mẹ như vậy, nó sẽ thu gọn cam kết hợp nhất và thay vào đó sử dụng cam kết cha, giả sử rằng các cha mẹ khác có những thay đổi không liên quan đến cây con. Trong một số trường hợp, điều này sẽ dẫn đến việc loại bỏ các phần của lịch sử, có những thay đổi thực tế đối với cây con. Cụ thể, nó sẽ thả các chuỗi cam kết, điều này sẽ chạm vào một cây con, nhưng dẫn đến cùng một giá trị cây con.
Hãy xem một ví dụ (mà bạn có thể dễ dàng tái tạo) để hiểu rõ hơn về cách hoạt động của điều này. Hãy xem xét lịch sử sau (định dạng dòng là: chủ đề commit [tree]):
% 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
Trong ví dụ này, chúng tôi đang chia nhỏ dir
. Các cam kết D
và E
có cùng một cây z
, bởi vì chúng tôi có cam kết C
, mà hoàn tác cam kết B
, vì vậy B-C
trình tự không làm gì dir
cả mặc dù nó có những thay đổi đối với nó.
Bây giờ chúng ta hãy thực hiện tách. Đầu tiên, chúng tôi phân chia về cam kết 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
Tiếp theo, chúng tôi chia về cam kết E
.
% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt
Vâng, chúng tôi đã mất hai cam kết. Điều này dẫn đến lỗi khi cố gắng đẩy phần tách thứ hai, vì nó không có hai cam kết đó, mà đã có nguồn gốc.
Thông thường, bạn có thể chịu đựng lỗi này bằng cách sử dụng push --force
, vì các cam kết bị bỏ thường sẽ không có thông tin quan trọng trong đó. Về lâu dài, lỗi cần được sửa, vì vậy lịch sử phân tách sẽ thực sự có tất cả các cam kết dir
, như mong đợi. Tôi hy vọng bản sửa lỗi sẽ bao gồm phân tích sâu hơn về các cam kết của phụ huynh đối với các phần phụ thuộc ẩn.
Để tham khảo, đây là phần của mã gốc, chịu trách nhiệm về hành vi.
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