Git pull dẫn đến các thông báo "Hợp nhất chi nhánh" không liên quan trong nhật ký cam kết


110

Tôi đang làm việc với một nhà phát triển khác trong một dự án và chúng tôi đang sử dụng Github làm kho lưu trữ từ xa của mình. Tôi đang sử dụng máy Mac sử dụng git 1.7.7.3, anh ấy đang sử dụng Windows bằng git 1.7.6.

Đây là những gì đang xảy ra

  1. Một trong số chúng tôi (hãy gọi anh ấy là nhà phát triển A, nhưng không quan trọng là người nào) đẩy một tập hợp các cam kết lên GitHub.
  2. Người kia (nhà phát triển B) thực hiện một số cam kết cục bộ.
  3. B hiện a git pull.
  4. B hiện a git push.
  5. Nhìn vào nhật ký lịch sử cam kết, tôi thấy Merge branch 'master' của github.com:foo/bar

Nhật ký cam kết có nhiều thông báo "Hợp nhất chi nhánh" theo thời gian và cũng hiển thị nhà phát triển B là các thay đổi cam kết mà nhà phát triển A đã thực hiện. Cách duy nhất mà chúng tôi đã tìm ra để ngăn chặn vấn đề này là thực hiện git pull --rebaseở bước 3, nhưng tôi không biết việc giảm tác dụng phụ sẽ dẫn đến điều gì. Đây là lần đầu tiên tôi làm việc trên git repo nhiều nhà phát triển, vậy đây có phải là hành vi bình thường không? Bất kỳ suy nghĩ về cách giải quyết vấn đề này?


2
Bạn có thể xem nhật ký mà không hòa trộn vớigit log --no-merges
wjandrea

Câu trả lời:


88

Cam kết bạn đang thấy là hoàn toàn tốt. A pullchạy hiệu quả git fetchvà sau đó git mergehợp nhất thường xảy ra khi bạn chạy git pull.

Bạn có thể sử dụng phương pháp khôi phục thay vì hợp nhất thay thế, nhưng thông thường bạn nên tránh. Rebasing cho phép bạn giữ lịch sử tuyến tính, nhưng cũng loại bỏ bất kỳ thông tin nào về việc phân nhánh đã xảy ra ban đầu. Nó cũng sẽ khiến lịch sử của nhánh hiện tại được viết lại, tạo lại tất cả các cam kết không có trong nhánh đích (trong trường hợp của bạn là điều khiển từ xa). Vì các cam kết được tạo lại là các cam kết khác nhau , điều này có thể gây ra nhiều nhầm lẫn khi phát triển cùng với những người khác, đặc biệt là khi mọi người đã kiểm tra các phần của các cam kết đó trước khi chúng được viết lại (ví dụ với các nhánh tính năng). Vì vậy, theo nguyên tắc chung, bạn không bao giờ nên viết lại bất kỳ cam kết nào đã được đẩy.

Các cam kết bạn thấy ở đó để kết hợp hai (hoặc nhiều) nhánh. Hoàn toàn ổn nếu có một cam kết mà không làm gì khác sau đó hợp nhất nhiều nhánh. Trên thực tế, nó làm cho nó rất rõ ràng khi bạn có một cam kết hợp nhất kết hợp các nhánh khi xem lịch sử. So với việc phục hồi, hợp nhất cũng cho phép bạn xem hiệu quả lịch sử ban đầu khi nó được phát triển, bao gồm cả các nhánh thực tế đã cùng tồn tại.

Vì vậy, câu chuyện ngắn: Vâng, có các cam kết hợp nhất là hoàn toàn tốt và bạn không nên lo lắng về chúng.


2
Câu trả lời rất hay. Bản thân tôi đã thử kiểu rebase vì nó được khuyến nghị trong một số nguyên tắc đóng góp cho các dự án nguồn mở và nó đã gây ra cho tôi sự cố. Một thành viên mới trong đội cũng vậy. Tôi nghĩ rằng tùy chọn rebase không dành cho các nhóm làm việc cùng nhau cả ngày, nhưng đúng cho các dự án có những người đóng góp chính và những người đóng góp khác chỉ gửi bản vá. Những người đó sẽ ổn khi tìm nạp repo chính và khôi phục các thay đổi của chúng ngay trước khi chúng đưa ra một yêu cầu kéo.
Meligy

2
@sTodorov Nếu không có thay đổi mới, thì phần tìm nạp của phần kéo sẽ không làm gì cả, nhưng quá trình hợp nhất vẫn đang được thực hiện. Vì vậy, nếu chi nhánh địa phương hiện tại của bạn không được cập nhật, nó sẽ hợp nhất các thay đổi mới vào chi nhánh của bạn. Và nếu nó không thể thực hiện hợp nhất tua đi nhanh (nếu bạn có các cam kết phân kỳ), thì nó sẽ tạo một cam kết hợp nhất.
poke

28
Câu trả lời này làm cho nó có vẻ như sử dụng rebase như OP đã mô tả là nguy hiểm, nhưng không phải vậy. Viết lại ở bước 3 không viết lại toàn bộ lịch sử. Chỉ các cam kết cục bộ chưa được đẩy mới được viết lại bằng cách được áp dụng lại trên đầu HEAD mới (cam kết mới nhất được đẩy đến nhánh đó). Điều này ngăn chặn các cam kết hợp nhất không liên quan và không có tác dụng phụ nào khác.
bob esponja

1
@bobesponja Tất cả các cam kết không nằm trên nhánh điều khiển từ xa được kéo đều được viết lại. Điều này có thể bao gồm các cam kết đã xuất bản từ các nhánh khác, ví dụ với các nhánh tính năng, mà những người khác có thể đã truy cập trước đó. Như vậy, vâng, việc khôi phục mà không suy nghĩ về những gì bạn rebase là một phần nguy hiểm.
poke

1
@bobesponja Có, nếu bạn đang xuất bản nhánh tính năng của mình sớm (vì những người khác làm việc trên nó hoặc đơn giản là sao lưu), thì bạn không nên căn cứ lại nó vì những người khác có thể đã tìm nạp nó. Sau đó, phục hồi — như bạn tự nói — đi ngược lại với các nguyên tắc phục hồi mà tôi đã nêu trong câu trả lời của mình. Tuy nhiên, nếu bạn không công bố cam kết của mình, thì việc giảm giá cũng không sao nếu bạn muốn và đừng bận tâm đến lịch sử tuyến tính. Nhưng nó phụ thuộc vào cách bạn làm việc, vì vậy câu trả lời chung là tránh nó, trừ khi nó thực sự an toàn. Btw. Tôi đã sửa lại câu trả lời của mình, vì vậy nếu vấn đề được giải quyết, tôi sẽ đánh giá cao nếu bạn xóa phiếu phản đối của mình.
poke

48

Câu trả lời này đã được sửa đổi, vì sự hiểu biết, sơ đồ và kết luận của tôi không chính xác.


git pullgây ra các cam kết hợp nhất vì git đang hợp nhất. Điều này có thể được thay đổi bằng cách đặt các nhánh của bạn sử dụng rebase thay vì hợp nhất. Sử dụng rebase thay vì hợp nhất trên một pull cung cấp lịch sử tuyến tính hơn cho kho lưu trữ được chia sẻ. Mặt khác, cam kết hợp nhất thể hiện nỗ lực phát triển song song của chi nhánh.

Ví dụ, hai người đang làm việc trên cùng một chi nhánh. Chi nhánh bắt đầu là:

...->C1

Người đầu tiên hoàn thành công việc của họ và đẩy đến nhánh:

...->C1->C2

Người thứ hai hoàn thành công việc của họ và muốn thúc đẩy, nhưng không thể vì họ cần cập nhật. Kho lưu trữ cục bộ cho người thứ hai trông giống như sau:

...->C1->C3

Nếu kéo được đặt để hợp nhất, kho lưu trữ ngôi thứ hai sẽ trông như thế nào.

...->C1->C3->M1
      \      /
       ->C2->

Trong đó M1 là một cam kết hợp nhất. Lịch sử nhánh mới này sẽ được đẩy lên repo. Nếu thay vào đó, pull được đặt để rebase repo cục bộ sẽ trông giống như sau:

...->C1->C2->C3

Không có cam kết hợp nhất. Lịch sử đã được thực hiện tuyến tính hơn.

Cả hai lựa chọn đều phản ánh lịch sử của chi nhánh. git cho phép bạn chọn lịch sử nào bạn thích.

Thực sự có những nơi mà rebase có thể gây ra sự cố với các nhánh từ xa. Đây không phải là một trong những trường hợp đó. Chúng tôi thích sử dụng rebase vì nó đơn giản hóa lịch sử nhánh vốn đã phức tạp cũng như hiển thị phiên bản lịch sử liên quan đến kho lưu trữ được chia sẻ.

Bạn có thể đặt branch.autosetuprebase = luôn để git tự động thiết lập các nhánh từ xa của bạn dưới dạng rebase thay vì master.

git config --global branch.autosetuprebase always

Cài đặt này khiến git tự động tạo cài đặt cấu hình cho từng nhánh từ xa:

branch.<branchname>.rebase=true

Bạn có thể tự thiết lập điều này cho các chi nhánh từ xa đã được thiết lập.

git config branch.<branchname>.rebase true

Tôi muốn cảm ơn @LaurensHolst đã đặt câu hỏi và theo đuổi những phát biểu trước đây của tôi. Tôi chắc chắn đã tìm hiểu thêm về cách git hoạt động với các cam kết kéo và hợp nhất.

Để biết thêm thông tin về các cam kết hợp nhất, bạn có thể đọc Đóng góp cho một dự án trong ProGit-Book . Phần Nhóm nhỏ Riêng tư hiển thị các cam kết hợp nhất.


7
“Việc sử dụng rebase thay vì hợp nhất trên một pull cung cấp lịch sử chính xác cho kho lưu trữ được chia sẻ. Sử dụng hợp nhất cung cấp một lịch sử sai lệch. ” - Cơ sở lý luận cho tuyên bố khá táo bạo này là gì? Không có cách nào một lịch sử có hợp nhất là 'lịch sử sai lầm'. Nó là một mô tả chính xác về thứ tự mà mọi thứ đã xảy ra. Những gì bạn đang làm bằng cách khôi phục thực sự là thay đổi lịch sử, để tạo ra một phiên bản tuyến tính hơn của nó. Bạn hy sinh độ chính xác cho thẩm mỹ. Có thể điều gì đó bạn thích làm, nhưng không có cách nào trung thực hơn.
Laurens Holst

2
Sử dụng rebase thay vì hợp nhất không hy sinh độ chính xác cho tính thẩm mỹ. Chúng tôi sử dụng --no-ff để hợp nhất, vì vậy tính thẩm mỹ không phải là một yêu cầu. Độ chính xác là một mong muốn. Rebase cung cấp độ chính xác đó.
Cửa thanh toán

2
Làm thế nào là lịch sử phục hồi chính xác hơn? Bạn không làm rõ điều này, và tôi không biết nó sẽ như thế nào.
Laurens Holst

1
Lịch sử phản ánh thời gian mà các cam kết xảy ra trong repo được chia sẻ . Một ngày đầu tiên, repo được chia sẻ đã thấy cam kết C2. Vào ngày thứ 2, repo được chia sẻ thấy cam kết C3. Nếu C3 đến trước C2 thì phản xạ của thời gian sẽ không đúng. C3 không đến trước C2. Tất cả những gì rebase làm là tổ chức lại các cam kết trên kho lưu trữ cục bộ để phản ánh đúng lịch sử được hiển thị bởi kho lưu trữ được chia sẻ.
Cửa thanh toán

6
Câu hỏi của bạn khiến tôi phải xem lại hiểu biết của mình về cam kết hợp nhất. Sơ đồ của tôi không chính xác. Tôi đang sửa đổi cuộc thảo luận. Kết luận của tôi cũng không chính xác. Lịch sử cho rebase và merge đều đúng như nhau. Bạn có thể đưa ra lựa chọn của riêng bạn.
Bill Door

11

Bạn có thể làm:

git pull --rebase

Tuy nhiên, điều này sẽ luôn đặt các thay đổi của bạn lên hàng đầu của các cộng tác viên. Nhưng bạn sẽ không nhận được bất kỳ thông báo hợp nhất gây ô nhiễm nào.


9

Thực sự có một câu trả lời đơn giản hơn nhiều cho điều này. Chỉ cần nhà phát triển B kéo TRƯỚC KHI thực hiện cam kết của mình. Điều này sẽ ngăn các cam kết hợp nhất đó, vì chúng được gây ra bởi lịch sử bạn đã tạo trên repo cục bộ của mình từ cam kết cục bộ của bạn đang cố gắng hợp nhất với lịch sử của các cam kết trên repo từ xa. Nếu bạn nhận được thông báo cho biết điều gì đó dọc theo dòng 'thay đổi sẽ bị ghi đè' khi thực hiện kéo, điều đó có nghĩa là cả hai bạn đã chạm vào cùng một tệp, vì vậy hãy làm như sau:

git stash
git pull
git stash pop

thì bạn có thể giải quyết mọi xung đột hợp nhất nếu có.


Khó chịu và lo lắng nhất chính xác là các xung đột hợp nhất. Tôi thà tránh nó
Green

1
@Green Nếu bạn lo lắng về xung đột hợp nhất thì ngay cả git pull cũng không khác gì.
Zoso

Ngoại trừ một lần khi bạn quên stashtrước bạn pull. Ugh git đòi hỏi tôi phải luôn đứng đầu trò chơi của mình.
linuxNoob

Cần git pull --rebasetích hợp các thay đổi từ xa trước các thay đổi cục bộ, bất kể.
vonbrand

7

Thực hiện kéo git sẽ chèn các thông báo "Hợp nhất chi nhánh", đó chỉ là những gì nó làm. Bằng cách kéo git, bạn đã hợp nhất nhánh từ xa vào nhánh cục bộ của mình.

Khi bạn thực hiện kéo git và có xung đột, nhật ký git sẽ hiển thị các bản cập nhật cho các tệp bị xung đột do người dùng đã giải quyết xung đột. Tôi cho rằng điều này là do người khắc phục xung đột cam kết lại tệp.

Theo như tôi biết đó chỉ là cách git hoạt động, và không có cách nào xung quanh nó.

Rebasing sẽ xóa lịch sử git, vì vậy bạn sẽ không thể biết thời điểm hợp nhất xảy ra.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.