Làm thế nào để git hợp nhất sau khi cherry-pick hoạt động?


190

Hãy tưởng tượng rằng chúng ta có một masterchi nhánh.

Sau đó, chúng tôi tạo ra một newbranch

git checkout -b newbranch

và thực hiện hai cam kết mới thành newbranch: commit1commit2

Sau đó, chúng tôi chuyển sang làm chủ và thực hiện cherry-pick

git checkout master
git cherry-pick hash_of_commit1

Nhìn vào gitkchúng ta thấy rằng commit1 và phiên bản anh đào của nó có các giá trị băm khác nhau, vì vậy về mặt kỹ thuật, chúng là hai cam kết khác nhau.

Cuối cùng, chúng tôi hợp nhất newbranchthành master:

git merge newbranch

và thấy rằng hai cam kết với các giá trị băm khác nhau đã được hợp nhất mà không gặp vấn đề gì mặc dù chúng ngụ ý rằng các thay đổi tương tự nên được áp dụng hai lần, do đó một trong số chúng sẽ thất bại.

Có phải git thực sự phân tích thông minh nội dung của cam kết trong khi hợp nhất và quyết định rằng những thay đổi không nên được áp dụng hai lần hoặc những cam kết này được đánh dấu nội bộ như được liên kết với nhau?

Câu trả lời:


131

Câu trả lời ngắn

Đừng lo lắng, Git sẽ xử lý nó.

Câu trả lời dài

Không giống như các ví dụ SVN 1 , Git không lưu trữ các cam kết trong định dạng tam giác, nhưng là ảnh chụp dựa trên 2,3 . Mặc dù SVN sẽ cố gắng áp dụng từng cam kết hợp nhất thành một bản vá (và không thành công, vì lý do chính xác mà bạn mô tả), Git thường có thể xử lý tình huống này.

Khi hợp nhất, Git sẽ cố gắng kết hợp các ảnh chụp nhanh của cả hai cam kết thành một ảnh chụp nhanh mới. Nếu một phần mã hoặc tệp giống hệt nhau trong cả hai ảnh chụp nhanh (nghĩa là vì một cam kết đã được chọn), Git sẽ không chạm vào nó.

Nguồn

1 Skip-Deltas trong Subversion
2 Khái niệm cơ bản về Git
3 Mô hình đối tượng Git


40
Trên thực tế, tôi muốn nói rằng bạn nên lo lắng về việc hợp nhất và "git sẽ xử lý nó" không phải là một quy tắc tốt.
cregox

4
Trong thực tế, việc hợp nhất CÓ THỂ dẫn đến nội dung trùng lặp trong một số trường hợp. Git đôi khi xử lý nó, nhưng đôi khi không.
donquixote

Điều này là sai lầm khủng khiếp. Có khá nhiều lưu trữ các tập tin tập tin trong tất cả các hình thức có thể chấp nhận. Và nếu tôi nhớ chính xác thì SVN đã sử dụng để lưu trữ ảnh chụp nhanh.
he_the_great

2
@he_the_great, không. Định dạng lưu trữ bỏ qua delta của SVN (! = Ảnh chụp nhanh) được ghi lại trong tài liệu hướng dẫn . Và tôi thực sự không có được những gì bạn có ý nghĩa bởi condevable . Tôi không phải là người bản ngữ, nhưng tôi khá chắc chắn đó không phải là một từ thực sự.
helmbert

2
@he_the_great Nhưng ngay cả khi đóng gói bất kỳ hàm băm nào cho một tệp đều dẫn đến tệp đầy đủ. Có, nó nén bằng deltas, nhưng nó không phải là delta cho những thay đổi trong một cam kết, mà thay vào đó là một delta giữa các giá trị băm cho một tệp. Đối với các đối tượng cam kết có liên quan, nó đang tham chiếu một cây đang tham chiếu hàm băm cho một tệp đầy đủ. Rằng dưới mui xe, dữ liệu được nén không ảnh hưởng đến cách hoạt động của git. Git lưu trữ các tệp hoàn chỉnh khi có liên quan đến cam kết, SVN lưu trữ deltas cho các cam kết theo tôi hiểu.
Peter Olson

43

Sau khi hợp nhất như vậy, bạn có thể có các cam kết được chọn trong lịch sử hai lần.

Giải pháp để ngăn chặn điều này tôi trích dẫn từ bài viết khuyến nghị cho các nhánh có cam kết (cherry-Pick) sử dụng rebase trước khi hợp nhất:

git merge sau git cherry-pick: tránh các cam kết trùng lặp

Hãy tưởng tượng chúng ta có nhánh chính và nhánh b:

   o---X   <-- master
    \
     b1---b2---b3---b4   <-- b

Bây giờ chúng tôi rất cần các cam kết b1 và b3 trong master, nhưng không phải các cam kết còn lại trong b. Vì vậy, những gì chúng tôi làm là kiểm tra chi nhánh chính và cherry-pick cam kết b1 và b3:

$ git checkout master
$ git cherry-pick "b1's SHA"
$ git cherry-pick "b3's SHA"

Kết quả sẽ là:

   o---X---b1'---b3'   <-- master
    \
     b1---b2---b3---b4   <-- b

Hãy nói rằng chúng tôi thực hiện một cam kết khác về chủ và chúng tôi nhận được:

   o---X---b1'---b3'---Y   <-- master
    \
     b1---b2---b3---b4   <-- b

Nếu bây giờ chúng ta sẽ hợp nhất nhánh b thành master:

$ git merge b

Chúng tôi sẽ nhận được như sau:

   o---X---b1'---b3'---Y--- M  <-- master
     \                     /
      b1----b2----b3----b4   <-- b

Điều đó có nghĩa là những thay đổi được giới thiệu bởi b1 và b3 sẽ xuất hiện hai lần trong lịch sử. Để tránh điều đó, chúng ta có thể rebase thay vì hợp nhất:

$ git rebase master b

Mà sẽ mang lại:

   o---X---b1'---b3'---Y   <-- master
                        \
                         b2'---b4'   <-- b

Cuối cùng:

$ git checkout master
$ git merge b

cho chúng tôi:

   o---X---b1'---b3'---Y---b2'---b4'   <-- master, b

Chỉnh sửa EDIT được cho là bởi nhận xét của David Lemon


1
gợi ý tuyệt vời về rebase! nó sẽ 'bỏ qua' tất cả các cam kết được chọn tự động.
iTake

2
Thành thật mà nói, nó có vẻ quá tốt để trở thành sự thật, tôi phải nhìn nó bằng mắt. Đồng thời rebase sửa đổi các cam kết, dòng thời gian cuối cùng của bạn sẽ là---Y---b2'---b4'
David Lemon

Hoạt động hoàn hảo. Rất hữu ích nếu bạn không muốn có những lần anh đào được chọn hai lần trong lịch sử.
dùng2350644

1
Không nên lưu ý rằng mặc dù rebase rất đẹp, nhưng điều nguy hiểm khi sử dụng nó là bất kỳ nhánh hoặc nhánh nào được tạo từ b cũ sẽ không đồng bộ và người dùng có thể cần phải sử dụng các công cụ như git reset --hard và git đẩy -f?
JHH

1
@JHH đó là lý do tại sao chúng tôi nổi loạn chi nhánh địa phương ở đây
ephemerr
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.