2016-02-10 44 views
6

Theo sự hiểu biết của tôi về git pull --rebase origin master, nó phải là tương đương với việc chạy các lệnh sau:Hiểu "git pull --rebase" vs "git rebase"

(from branch master): $ git fetch origin 
(from branch master): $ git rebase origin/master 

tôi dường như đã tìm thấy một số trường hợp này không hoạt động như mong đợi. Trong không gian làm việc của tôi, tôi có các thiết lập sau:

  • chi nhánh tài liệu tham khảo origin/master chi nhánh master trên xa origin
  • chi nhánh master được thiết lập để theo dõi origin/master, và là đằng sau bậc thầy của một số cam kết.
  • chi nhánh feature được thiết lập để theo dõi chi nhánh địa phương mastertrước của master bởi một số cam kết.

Đôi khi, tôi sẽ mất cam kết bằng cách chạy trình tự các bước như sau

(from branch master): $ git pull --rebase 
(from branch master): $ git checkout feature 
(from branch feature): $ git pull --rebase 

Tại thời điểm này, những cam kết trước tôi đã được trên feature hiện nay bị mất. Bây giờ, nếu tôi lập lại vị trí của mình, và thay vào đó làm như sau:

(from branch feature): $ git reset --hard [email protected]{2} # rewind to before second git pull 
(from branch feature): $ git rebase master 

Các cam kết đã được áp dụng một cách chính xác và cam kết mới của tôi trên feature vẫn còn hiện hữu. Điều này dường như mâu thuẫn trực tiếp với sự hiểu biết của tôi về cách hoạt động của git pull, trừ khi git fetch . làm điều gì đó xa lạ hơn tôi mong đợi.

Thật không may, điều này không thể tái sản xuất 100% cho tất cả các cam kết. Khi nó hoạt động cho một cam kết, mặc dù, nó hoạt động mọi lúc.

Lưu ý: My git pull --rebase tại đây thực sự nên được đọc là --rebase=preserve, nếu điều đó quan trọng. Tôi đã điều sau đây trong ~/.gitconfig tôi:

[pull] 
    rebase = preserve 
+0

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. –

+1

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

+0

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

Trả lời

8

(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..)

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 AB 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 fetchI 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- 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 masterB-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 --ontomaster đó 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ì.

+2

Chà. Đó là một câu trả lời tuyệt vời và cung cấp nhiều thông tin chi tiết về những gì đang diễn ra ở đây. Tôi sẽ dành một chút thời gian cố gắng tái tạo lại hoàn cảnh dẫn đến vấn đề này, và xem câu trả lời này có làm sáng tỏ những gì đang xảy ra không. Tôi nghi ngờ điều này sẽ dẫn tôi trực tiếp đến nó, nhưng tôi rất muốn tìm ra lý do chính xác. Tôi sẽ cho bạn biết nếu tôi tìm thấy bất cứ điều gì. – ashays

+0

Sau khi dành một chút thời gian đào sâu vào điều này, tôi đã không thể luôn luôn tái tạo lại một trường hợp sử dụng (nhưng tôi lại tìm thấy nó một cách tự nhiên). Điều này chắc chắn có vẻ là vấn đề! Cảm ơn bạn đã dành thời gian cho câu trả lời tuyệt vời này. Nó chắc chắn thêm vào sự hiểu biết của tôi về git. – ashays

+0

@ashays Vấn đề cơ bản khiến bạn mất cam kết là gì? – mschuett