Почему git не объединяет соседние строки без конфликтов?


25

Недавно я узнал, что при объединении двух веток в git, если есть изменения в двух соседних строках, git объявляет это конфликтом. Например, если файл test.txtимеет это содержимое:

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

и в ветке masterмы меняем это на

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

в то время как в филиале testingмы меняем это на

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

и затем попытка слиться testingс mastergit объявляет конфликт слияния. Мое наивное ожидание состояло в том, что слияние произойдет без конфликта и приведет к следующему:

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

Я уверен, что есть веская причина, почему git не сливается таким образом. Может кто-нибудь объяснить эту причину?


Эй, я тоже заметил это на прошлой неделе. Может быть, мы делали то же учебник.
детально

5
Способности Git к слиянию на самом деле довольно плохие, IMO
James

@ Джеймс, ты пробовал использовать алгоритм терпения? Я нахожу, что получаю лучшие результаты с этим, особенно когда имеешь дело с тем, где разделены фрагменты (например, захват одного функционального тела вместо двух). Если вам не нравятся git's, вы также можете использовать свои собственные (например, см. Blog.wuwon.id.au/2010/09/… ).
Определить

1
Основная причина в том, что git пытается выполнить слияние, а не выделять его специализированному инструменту. Совсем не философия Unix. Для исходных файлов вы можете использовать грамматику языка для надежного определения различий.
MSalters

Единственный общий контекст - это A и D, так почему же A / C1 / B1 / D не является правильным слиянием?
Изката

Ответы:


13

Предполагая, что этот фрагмент кода

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

был изменен в одном филиале в этот

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

и в другой ветке в эту

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

тогда я не хотел бы, чтобы мерзавец слился с этим

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

не тревожа меня.

Чтобы избежать возникновения таких проблем, git обычно отказывается автоматически объединять изменения, затрагивая соседние строки. Это дает вам возможность проверить, будет ли логика программы нарушена или нет.

Этот пример тривиален, но при объединении огромных ветвей риск подобных «логических» конфликтов намного выше. Иногда я бы даже хотел, чтобы контекст был еще больше, чем сейчас.


5
Это не имеет ничего общего с этим, просто добавьте неизменную строку между этими двумя и внезапно git объединяет их без проблем.
Darkhogg

Да, я не получаю этот ответ. Вы можете использовать ту же логику, чтобы избежать всех автоматических слияний.
Мердад

Должна быть проведена граница между безопасностью и полезностью. Автоматические слияния несут риск повреждения потока программы - как я пытался показать в ответе - но если git просто откажется что-либо слить, это станет бесполезным. Создатели git только что определились с некоторым контекстом, который поймает большинство «опасных» случаев. Добавление какой-то неизменной строки (как выяснил Даркхогг) дуракам кажется, что слияние может быть безопасным для выполнения.
Арсен7

11

Это поведение только для мерзавцев?

После обсуждения с коллегой я только что попробовал, и SVN справляется с этим без проблем: вы модифицируете 2 строки.

Возможности слияния нескольких VCS тестируются здесь на базаре, darcs, git и mercurial : https://github.com/mndrix/merge-this

Кажется, только дарки успешно объединяют дело "соседних линий".

Применение смежных изменений к файлам не является сложной проблемой. Я действительно думаю, что это поведение было выбрано специально.

Почему кто-то решил, что изменение соседних строк приводит к конфликту?

Я думаю, что это заставит вас взглянуть на это .

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

Модификация № 1, о мастере:

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

Модификация № 2, объединенная из ветки:

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

После слияния вы не хотите этого:

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);

Видя это поведение как особенность

Вы можете превратить поведение git merging в преимущество. Когда вам нужно сохранить согласованность двух строк, но вы не можете обнаружить их (во время компиляции, в начале ваших тестов или еще), вы можете попробовать присоединиться к ним.

Перепишите это ...:

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

...к этому:

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

Итак, когда вы объединяете 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

... с 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 создаст конфликт, и вы заставите его взглянуть на него.


2
Я просто собираюсь пойти дальше и сказать, что я думаю, что ваш ответ вполне разумен, но в зависимости от запутанного взаимодействия, определяемого реализацией, между вашим кодом и вашим контролем исходного кода для проверки работоспособности - это быстрый путь к thedailywtf.com. В любом случае, слепое слияние кода без синтаксического анализатора языка всегда является наилучшим усилием, и у меня было несколько случаев, когда git старательно автоматизировал то, чего не должно быть, и создавал код, который даже не компилируется.
Wug

5

Я в основном догадываюсь, но я думаю, что это связано со строкой 2, используемой в качестве контекста для изменения строки 3.

Git не может просто сказать, что «строка с C стала строкой с C1», потому что может быть другая строка с «C», поэтому он говорит: «Строка с C, это сразу после начала файла, строка с A, и линия с B, теперь C1 "

Если «строки с буквой B» больше нет, то часть контекста теряется, и git может только приблизительно сказать, куда должна идти новая строка.


5
также очень вероятно, что C зависит от B, поэтому наивное слияние может быть проблематичным, даже если git «знает», как это сделать
Lucina

Поверьте мне, Git только «думает, что знает». Git - кладбище неправильных понятий, пытающихся быть выпрямленными!
user3833732

2

Все остальные ответы здесь являются точными, но для меня это всегда казалось ненужным ограничением.

Как уже говорили другие, в этих случаях вы определенно не захотите, чтобы Git объединял строки без предупреждения.

Но я все еще хотел вариант сделать это автоматически, после предупреждения. Поэтому я написал собственный драйвер git merge, который может интерактивно объединять конфликты в соседних (или отдельных) строках:

введите описание изображения здесь

Это экономит мне огромное количество времени, так как я управляю проектом, в котором люди часто работают над одними и теми же файлами и проводят рефакторинг большого количества кода.

Скрипт доступен на GitHub под лицензией GPLv3 +. Может быть, вы найдете это полезным:

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


4
Не возражает ли кто-нибудь объяснить, почему за это проголосовали? Я здесь новичок, поэтому, если я сделал что-то не так, я хотел бы знать, что это было, чтобы я мог избежать этого в будущем. Я понимаю, что мой пост не совсем отвечает на заданный вопрос, но он по-прежнему актуален, и я думаю, что большинство людей, которые приходят сюда, хотели бы знать не только, почему Git делает это, но и что они могут с этим поделать (как я сделал, когда я впервые дошел до этого вопроса из поиска гугл).
deltacrux

Я еще не пробовал, но я искал способ автоматизировать это и был рад найти его здесь. Спасибо :) ты классный.
Мердад

1
Нет проблем! Я надеюсь, что вы найдете это полезным, и, пожалуйста, не стесняйтесь оставлять отзывы на Github, если у вас возникнут проблемы или у вас есть предложения по улучшению. Благодарность!
deltacrux
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.