Tại sao git không hợp nhất các đường liền kề mà không có xung đột?


25

Gần đây tôi đã biết rằng khi hợp nhất hai nhánh trong git, nếu có thay đổi trên hai dòng liền kề thì git tuyên bố đây là một xung đột. Ví dụ: nếu tệp test.txtcó nội dung này:

Line 1: A
Line 2: B
Line 3: C
Line 4: D

và trong chi nhánh, masterchúng tôi thay đổi điều này thành

Line 1: A
Line 2: B1
Line 3: C
Line 4: D

trong khi ở chi nhánh, testingchúng tôi thay đổi điều này thành

Line 1: A
Line 2: B
Line 3: C1
Line 4: D

và sau đó cố gắng hợp nhất testingvào master, git tuyên bố một cuộc xung đột hợp nhất. Kỳ vọng ngây thơ của tôi là sự hợp nhất sẽ xảy ra mà không có xung đột và mang lại điều này:

Line 1: A
Line 2: B1
Line 3: C1
Line 4: D

Tôi chắc chắn có một lý do chính đáng tại sao git không hợp nhất theo cách này. Ai đó có thể giải thích lý do này?


Này, tôi chỉ nhận thấy tuần này quá. Có lẽ chúng tôi đã làm cùng một hướng dẫn.
gièm pha

5
Khả năng hợp nhất của git thực sự khá kém, IMO
James

@James bạn đã thử sử dụng thuật toán kiên nhẫn chưa? Tôi thấy tôi nhận được kết quả tốt hơn với nó, đặc biệt là khi xử lý nơi các khối được chia (ví dụ: lấy một cơ thể chức năng thay vì hai). Nếu bạn không thích git's, bạn cũng có thể sử dụng của riêng bạn (xem blog.wuwon.id.au/2010/09/iêu để làm ví dụ).
răn đe

1
Nguyên nhân sâu xa là git cố gắng tự hợp nhất, thay vì bao thanh toán nó thành một công cụ chuyên dụng. Hoàn toàn không phải là triết lý Unix. Đối với các tệp nguồn, bạn thực sự có thể sử dụng ngữ pháp ngôn ngữ để xác định độ lệch một cách đáng tin cậy.
MSalters

Bối cảnh phổ biến duy nhất là A và D, vậy tại sao A / C1 / B1 / D không hợp nhất?
Izkata

Câu trả lời:


13

Giả sử rằng đoạn mã này

x=0
x+=1 if foo
x+=1 if bar
return x

đã được thay đổi trong một chi nhánh thành này

x=0
x+=1 if foo && xyzzy
x+=1 if bar
return x

và trong một chi nhánh khác

x=0
x+=1 if foo
x+=1 if bar && xyzzy
return x

sau đó tôi sẽ không muốn git hợp nhất nó vào đây

x=0
x+=1 if foo && xyzzy
x+=1 if bar && xyzzy
return x

mà không báo động cho tôi.

Để tránh gây ra sự cố như vậy, git thường từ chối tự động hợp nhất các thay đổi chạm vào các đường gần đó. Nó cho bạn cơ hội xác minh xem logic chương trình có bị hỏng hay không.

Ví dụ này là tầm thường, nhưng khi sáp nhập các nhánh lớn, nguy cơ xảy ra xung đột "logic" tương tự sẽ lớn hơn nhiều. Đôi khi tôi thậm chí sẽ thích bối cảnh thậm chí còn lớn hơn hiện tại.


5
Điều đó không có gì để làm với nó, mặc dù, chỉ cần thêm một đường không thay đổi giữa hai người đó và đột nhiên git hợp nhất chúng mà không có vấn đề gì.
Darkhogg

Vâng, tôi không nhận được câu trả lời này. Bạn có thể sử dụng cùng một logic để tránh tất cả các kết hợp tự động.
Mehrdad

Phải có một ranh giới giữa an ninh và hữu ích. Hợp nhất tự động chịu rủi ro làm hỏng luồng chương trình - như tôi đã cố gắng thể hiện trong câu trả lời - nhưng nếu git chỉ từ chối hợp nhất bất cứ điều gì, nó sẽ trở nên vô dụng. Những người tạo ra git vừa quyết định một số bối cảnh, trong đó sẽ nắm bắt hầu hết các trường hợp "nguy hiểm". Thêm một số dòng không thay đổi (như Darkhogg đã phát hiện ra) những kẻ ngu ngốc tin rằng việc hợp nhất có thể an toàn để thực hiện.
Arsen7

11

Đây có phải là hành vi chỉ git?

Sau khi thảo luận với một đồng nghiệp, tôi mới thử và SVN xử lý nó mà không gặp vấn đề gì: bạn đã sửa đổi 2 dòng.

Các khả năng hợp nhất của một số VCS được thử nghiệm ở đây cho chợ, darcs, git và mercurial : https://github.com/mndrix/merge-this

Dường như chỉ có darcs hợp nhất thành công trường hợp "đường liền kề".

Áp dụng các thay đổi liền kề cho các tập tin không phải là một vấn đề khó khăn. Tôi thực sự nghĩ rằng hành vi này đã được chọn trên mục đích.

Tại sao một người nào đó quyết định rằng sửa đổi các đường liền kề sẽ tạo ra một cuộc xung đột?

Tôi sẽ nghĩ rằng đây là để buộc bạn nhìn vào nó .

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif số 1, trên master:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif số 2, được sáp nhập từ một chi nhánh:

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

Sau khi hợp nhất, bạn không muốn điều đó:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

Xem hành vi này là một tính năng

Bạn có thể biến hành vi hợp nhất git thành một lợi thế. Khi bạn cần giữ 2 dòng nhất quán nhưng bạn không thể phát hiện ra nó (tại thời điểm biên dịch, sớm trong các thử nghiệm của bạn hoặc cách khác), bạn có thể thử tham gia chúng.

Viết lại ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    // Need to do something else
    do_something_else(r);

... đến đây:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    do_something_else(r); // Need to do something else

Vì vậy, khi bạn hợp nhất Modif 1 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i)/2; // we need only the half
    do_something_else(r); // Need to do something else

... với Modif 2 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    if(r < 0) // do_stuff can return an error
        handle_error(r);
    do_something_else(r/2); // Need to do something else

..., git sẽ tạo ra một cuộc xung đột và bạn sẽ buộc bạn phải nhìn vào nó.


2
Tôi sẽ tiếp tục và nói rằng tôi nghĩ câu trả lời của bạn là hoàn toàn hợp lý, nhưng tùy thuộc vào sự phức tạp, việc triển khai được xác định tương tác giữa mã của bạn và kiểm soát nguồn của bạn để kiểm tra độ tỉnh táo là một cách nhanh chóng để thed Dailywtf.com. Mã hợp nhất một cách mù quáng mà không có trình phân tích cú pháp ngôn ngữ LUÔN LUÔN là nỗ lực tốt nhất và tôi đã có một vài trường hợp trong đó git tự động hóa một cái gì đó không nên có và tạo ra mã thậm chí không biên dịch được.
Wug

5

Tôi chủ yếu đoán, nhưng tôi nghĩ nó phải làm với dòng 2 được sử dụng làm bối cảnh cho thay đổi dòng 3.

Git không thể chỉ nói rằng "Dòng có C trở thành một dòng với C1" bởi vì có thể có một dòng khác với "C", do đó, nó nói "Dòng với C, ngay sau khi bắt đầu tệp, dòng với A và đường thẳng với B, giờ là C1 "

Nếu "dòng với B" không còn ở đó nữa, thì một số bối cảnh bị mất và git chỉ có thể nói đại khái nơi dòng mới phải đi.


5
Cũng rất có khả năng C phụ thuộc vào B, do đó, một sự hợp nhất ngây thơ có thể gây rắc rối ngay cả khi git "biết cách" làm điều đó
Lucina

Hãy tin tôi Git chỉ "nghĩ rằng nó biết". Git là một nghĩa địa của các khái niệm sai, cố gắng để được thẳng!
dùng3833732

2

Các câu trả lời khác ở đây đều có điểm, nhưng với tôi điều này luôn có vẻ như là một giới hạn không cần thiết.

Như những người khác đã nói, trong những trường hợp này, bạn chắc chắn sẽ không muốn Git hợp nhất các dòng mà không có cảnh báo.

Nhưng tôi vẫn muốn tùy chọn tự động làm điều đó, sau khi được cảnh báo. Vì vậy, tôi đã viết một trình điều khiển hợp nhất git tùy chỉnh có thể hợp nhất các xung đột trên các dòng liền kề (hoặc riêng lẻ) một cách tương tác:

nhập mô tả hình ảnh ở đây

Nó giúp tôi tiết kiệm rất nhiều thời gian, vì tôi quản lý một dự án nơi mọi người thường làm việc trên cùng các tệp và tái cấu trúc nhiều mã.

Kịch bản có sẵn trên GitHub theo giấy phép GPLv3 +. Có lẽ bạn sẽ thấy nó hữu ích:

https://github.com/paulaltin/git-subline-merge


4
Ai đó có thể giải thích lý do tại sao điều này đã bị hạ cấp? Tôi khá mới ở đây, vì vậy nếu tôi làm sai điều gì đó tôi muốn biết nó là gì để tôi có thể tránh nó trong tương lai. Tôi nhận ra bài đăng của mình không trả lời chính xác câu hỏi khi được hỏi, nhưng nó vẫn có liên quan và tôi nghĩ hầu hết những người đến đây sẽ muốn biết không chỉ tại sao git làm điều này mà còn những gì họ có thể làm về nó (như tôi đã làm khi tôi làm lần đầu tiên đạt được câu hỏi này từ một tìm kiếm Google).
deltacrux

Tôi chưa thử nhưng tôi đã tìm cách để tự động hóa việc này và rất vui khi tìm thấy nó ở đây. Cảm ơn :) bạn thật tuyệt vời.
Mehrdad

1
Không vấn đề gì! Tôi hy vọng bạn thấy nó hữu ích và vui lòng để lại phản hồi trên Github nếu bạn gặp vấn đề hoặc có bất kỳ đề xuất cải tiến nào. Cảm ơn!
deltacrux
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.