(Chỉnh sửa, ngày 30 Tháng 11 năm 2016: xem thêm this answer-Why is git rebase discarding my commits? Bây giờ là hầu như chắc chắn rằng đó là do tùy chọn ngã ba điểm..)
Có là một vài khác biệt giữa hướng dẫn sử dụng và pull
dựa trên git rebase
(ít hơn bây giờ ở 2,7 so với các phiên bản của git trước tùy chọn --fork-point
trong git merge-base
). Và, tôi nghi ngờ việc bảo vệ tự động-hợp nhất của bạn có thể được tham gia. Đó là một chút khó khăn để chắc chắn nhưng thực tế là chi nhánh địa phương của bạn sau chi nhánh địa phương khác của bạn mà là nhận được rebased là khá khêu gợi. Trong khi đó, tập lệnh cũ git pull
cũng được viết lại trong C gần đây, vì vậy khó có thể xem nó làm gì (mặc dù bạn có thể đặt biến môi trường GIT_TRACE
thành 1
để làm cho lệnh git hiển thị cho bạn khi chạy chúng trong nội bộ).
Trong mọi trường hợp, có hai hoặc ba mục chủ chốt ở đây (tùy thuộc vào cách bạn đếm và chia này lên, tôi sẽ làm cho nó thành 3):
git pull
chạy git fetch
, sau đó một trong hai git merge
hoặc git rebase
cho mỗi hướng dẫn, nhưng khi nó chạy git rebase
, nó sử dụng máy móc điểm ngã ba mới để "phục hồi từ một lần khởi động ngược dòng".
Khi git rebase
được chạy mà không có đối số, nó có trường hợp đặc biệt yêu cầu máy móc điểm ngã ba. Khi chạy với các đối số, máy móc điểm ngắt được tắt trừ khi được yêu cầu một cách rõ ràng với --fork-point
.
Khi git rebase
được hướng dẫn để duy trì hợp nhất, nó sử dụng mã rebase tương tác (không tương tác). Tôi không chắc chắn điều này thực sự quan trọng ở đây (do đó "có thể được tham gia" ở trên). Thông thường nó flattens đi sáp nhập và chỉ có các kịch bản rebase tương tác có mã để bảo tồn chúng (mã này thực sự lại làm việc sáp nhập vì không có cách nào khác để đối phó với họ).
Mục quan trọng nhất ở đây (chắc chắn) là mã điểm ngã ba. Mã này sử dụng reflog để xử lý các trường hợp tốt nhất được hiển thị bằng cách vẽ một phần của biểu đồ cam kết.
Trong một (không có công cụ điểm ngã ba cần thiết) bình thường rebase trường hợp bạn có một cái gì đó như thế này:
... - A - B - C - D - E <-- origin/foo
\
I - J - K <-- foo
nơi A
và B
là cam kết bạn có khi bạn bắt đầu chi nhánh của bạn (để B
là merge- cơ sở), C
thông qua E
là các cam kết mới mà bạn đã chọn từ xa qua git fetch
và I
thông qua K
là các cam kết của riêng bạn. Mã rebase sao chép I
thông qua K
, đính kèm bản sao đầu tiên vào E
, bản sao thứ hai tới bản sao-of-I
và thứ ba tới bản sao-of-J
.
Git số liệu ngoài hoặc sử dụng để, anyway- mà cam kết để sao chép sử dụng git rev-list origin/foo..foo
, ví dụ, bằng cách sử dụng tên của chi nhánh hiện tại của bạn (foo
) để tìm K
và làm việc ngược, và tên của thượng nguồn của nó (origin/foo
) để tìm số E
và làm việc ngược. Các ngược diễu hành dừng lại ở cơ sở hợp nhất, trong trường hợp này B
, và kết quả sao chép trông như thế này:
... - A - B - C - D - E <-- origin/foo
\ \
\ I' - J' - K' <-- foo
\
I - J - K [[email protected]{1}: reflog for foo]
Vấn đề với phương pháp này xảy ra khi upstream- origin/foo
đây-được tự rebased. Ví dụ: giả sử trên origin
ai đó bị đẩy buộc sao cho B
được thay thế bằng một bản sao mới B'
với từ ngữ cam kết khác nhau (và có thể là một cây khác nữa, nhưng, chúng tôi hy vọng, không có gì ảnh hưởng đến số I
-through- K
của chúng tôi).Điểm khởi đầu bây giờ trông như thế này:
B' - C - D - E <-- origin/foo
/
... - A - B <-- [origin/[email protected]{n}]
\
I - J - K <-- foo
Sử dụng git rev-list origin/foo..foo
, chúng tôi chọn cam kết B
, I
, J
, và K
được sao chép, và cố gắng để dán chúng vào sau E
như thường lệ; nhưng chúng tôi không muốn để sao chép B
vì nó thực sự đến từ origin
và đã được thay thế bằng bản sao của riêng mình B'
.
Mã điểm ngã ba là xem xét việc chỉnh sửa cho origin
để xem có thể truy cập vào B
một lúc nào đó không. Nghĩa là, nó không kiểm tra chỉ origin/master
(tìm E
và quét lại B'
và sau đó A
), nhưng cũng origin/[email protected]{1}
(chỉ trực tiếp đến B
, có lẽ, tùy thuộc vào mức độ thường xuyên bạn chạy git fetch
), origin/[email protected]{2}
, và vân vân. Mọi cam kết trên foo
có thể truy cập được từ bất kỳorigin/[email protected]{n}
nào được xem xét để tìm nút tổ tiên chung thấp nhất trong biểu đồ (nghĩa là tất cả chúng được coi là tùy chọn để trở thành cơ sở hợp nhất git merge-base
in ra).
(Điều đáng chú ý là có lỗi ở đây: phát hiện điểm ngã ba tự động này chỉ có thể tìm thấy các cam kết có thể truy cập được cho thời gian mà mục nhập reflog được duy trì, trong trường hợp này mặc định là 30 ngày. liên quan đến vấn đề của bạn)
Trong trường hợp của bạn, bạn có ba tên chi nhánh (và do đó ba reflogs) tham gia:.
origin/master
, được cập nhật bởi.210 (bước đầu tiên của git pull
của bạn trong khi chi nhánh master
)
master
, được cập nhật bởi cả hai bạn (thông qua cam kết bình thường) và git rebase
(bước thứ hai của git pull
của bạn), và
feature
, được cập nhật bởi cả hai bạn (thông qua các cam kết thông thường) và git rebase
(bước thứ hai của giây thứ haigit pull
: bạn "tìm nạp" từ chính bạn, không có op, sau đó rebase feature
trên master
).
Cả rebases đang chạy với --preserve-merges
(do đó không tương tác chế độ tương tác) và --onto new-tipfork-point
, nơi fork-point
cam ID được tìm thấy bằng cách chạy git merge-base --fork-point upstream-name HEAD
. Các upstream-name
cho rebase đầu tiên là origin/master
(tốt, refs/remotes/origin/master
) và upstream-name
cho rebase thứ hai là master
(refs/heads/master
).
Điều này nên tất cả chỉ hoạt động.Nếu bạn cam kết đồ thị vào lúc bắt đầu của toàn bộ quá trình là một cái gì đó giống như những gì bạn đã mô tả:
... - A - B <-- master, origin/master
\
I - J - K <-- feature
thì fetch
đầu tiên mang lại một số cam kết và làm cho origin/master
điểm đến đầu mới:
C - D - E <-- origin/master
/
... - A - B <-- master, origin/[email protected]{1}
\
I - J - K <-- feature
và rebase đầu tiên sau đó thấy không có gì để sao chép (hợp nhất-base của master
và B
-B
= nĩa-point (thạc sĩ, nguồn gốc/master) chỉ -là B
nên không có gì để sao chép là), cho:
C - D - E <-- master, origin/master
/
... - A - B <-- [email protected]{1}, origin/[email protected]{1}
\
I - J - K <-- feature
Lần tìm nạp thứ hai là từ bản thân bạn và hoàn toàn không có/bỏ/bỏ qua, để điều này làm đầu vào cho lần rebase thứ hai. Mục tiêu --onto
là master
đó là cam kết E
và ngã ba điểm của HEAD
(feature
) và master
cũng được cam kết B
, để lại cam kết I
qua K
để sao chép sau E
như bình thường.
Nếu một số (các) cam kết đang bị loại bỏ, có điều gì đó sai trong quá trình này, nhưng tôi không thể nhìn thấy gì.
Bạn không nên tự khởi động lại nhánh theo dõi từ xa 'origin/master', thay vào đó bạn đang cố gắng chuyển tiếp điều đó, mà không ảnh hưởng đến bản sao (theo dõi) của từ xa đó. Tôi đã không kiểm tra các hướng dẫn sử dụng cho các invocations đúng nhưng có lẽ checkout-chi nhánh đó theo một tên tạm thời mới, và rebase rằng chi nhánh tạm thời vào HEAD hiện tại của bạn. –
Tôi nghĩ có một số rắc rối ở đây. Tôi không rebasing 'origin/master', nhưng rebasing nhánh hiện tại' master' vào 'origin/master'. Điều này, theo sự hiểu biết của tôi, về cơ bản là đưa 'HEAD' vào đầu của' origin/master', và áp dụng lại các commit đã ở đầu của 'master' trở lại nhánh. Về cơ bản, viết lại các commit mới trên 'master' như thể chúng xảy ra sau những thay đổi từ' origin/master'. – ashays
Phiên bản git (cục bộ) của bạn là gì? Tôi hỏi vì tôi nhớ lại (hơi mơ hồ và chưa quay trở lại để kiểm tra) rằng có một lỗi rebase điểm tự động trong 'git pull' cho một số bản phát hành 2.x và biết phiên bản có thể làm cho việc kiểm tra đó dễ dàng hơn một chút. – torek