Глава 3.2 книги Git указывает:
…Occasionally, this process doesn’t go smoothly. If you changed the same part of the same file differently in the two branches you’re merging, Git won’t be able to merge them cleanly.…
Как следует интерпретировать неоднозначное выражение «изменить одну и ту же часть одного и того же файла по-разному»? Выполняет ли Git внутреннее сравнение строк за строкой в конфликтующем файле между ветвями?
Пример:
Предположим, в ветке л я добавляю дополнительную новую строку в начале README.md. Означает ли это, что изменение любой строки, отличной от первой, в README.md в другой ветке р вызовет конфликт при слиянии?
Вы ожидаете, что наивное построчное сравнение не удастся, поскольку все строки были сдвинуты на позицию в л, а некоторая часть строк в р осталась неизменной.
Добавлен пример для дальнейшего объяснения.
«…вызовет конфликт при слиянии?» Нет, потому что это будет другая часть того же файла. В книге сказано «та же часть того же файла».
«Вы ожидаете, что наивное построчное сравнение потерпит неудачу…» Сравнение Git не так уж и наивно.
@Code-Apprentice Именно поэтому я задаю вопрос. Чем отличается Git от наивности?
Помните, что Git делает не одну, а два диффов:
База слияния Б одинакова в командах обеgit diff
:
git diff --find-renames <hash-of-B> <hash-of-L> # what we changed
git diff --find-renames <hash-of-B> <hash-of-R> # what they changed
Итак, предположим, что мы и они оба модифицировали файл F.ext
. Из первый diff видно, какие строки F.ext
мы изменились: те, которые перечислены как удаленные в фрагменте diff относительно B, плюс еще одна «промежуточная» линия на краю для безопасности — край идет вперед, поэтому, если мы заменили исходную строку 3 мы «тронули» строки 3 и «3 с половиной». Если бы мы не удалили ни одной строки — если бы мы вставили строку после строки 3 перед строкой 4 — тогда мы коснулись строки «3 с половиной».
Между тем, из разницы второй также видно, какие строки F.ext
Oни изменились. Применяются те же правила: если они заменили исходную строку 3 новой строкой 3, то они коснулись строки 3 (включая строку «3.5»). Итак, если мы коснулись строки 3 или строки 3.5, возник конфликт. В противном случае конфликта нет: Git может просто принять изменение от той стороны, которая внесла изменение в рассматриваемую строку (строки).
(Обратите внимание, что git diff
представляет разницу как разницу контекста единый. В коде слияния используется необработанная, неунифицированная разница. Поэтому в нем есть список, который гласит: «в строке Икс оригинала удалите строки Д и вставьте новые строки замены я», где не более одного из Д или я может быть нулем. Коснувшийся отрезок - это линия Икс через линию Х + Д + 0,5, более или менее. Со своим странным "плюсом на половину" я действительно просто энергично машу руками, чтобы прикрыть пустой пролет проблема, которая является является проблемой. Вам придется поэкспериментировать с Git, чтобы увидеть, что именно он делает в каждом случае.)
То, что сказал Торек, правильно, но я просто хочу немного больше подчеркнуть основы, так как, похоже, это то, с чем вы боретесь.
Когда вы объединяете две ветви, каждая ветвь представляет одну историю усилий или изменений. Например, представьте, что вы и ваш коллега Боб одновременно работаете над одним и тем же проектом с разных компьютеров.
Когда вы начинали, у вас была точно такая же версия, что и у Боба. Но поскольку вы работаете независимо, легко увидеть, что вы и Боб можете изменять файл ПРОЧТИ МЕНЯ одновременно. Возможно, вы заметили, что в первом предложении есть слово с ошибкой, и исправили его. Тем временем Боб решает, что введение сбивает с толку, поэтому переписывает первые два абзаца. В версии Боба опечатка осталась, но теперь она находится в другой строке.
Итак, между вашей версией и версией файла README Боба есть конфликт. Когда вы с Бобом решите объединить свои изменения вместе, git merge
обнаружит этот конфликт и отправит вам жалобу. Вы не хотите, чтобы слияние отбросило ваши изменения, так как Боб не заметил орфографическую ошибку. Но вы также не хотите выбрасывать работу Боба. Следовательно, решение об объединении изменений из вашего README и README Боба должно приниматься человеком.
В данном случае вы с Бобом изменили одну и ту же строку README. Но что, если бы вы коснулись разных линий? В качестве меры предосторожности git merge
по-прежнему будет жаловаться, если ваши измененные строки достаточно близки к измененным строкам Боба, потому что все еще может возникнуть конфликт.
Но git merge
не обнаруживает (и не может) все возможные конфликты. Иногда результат слияния не будет иметь смысла, хотя git merge
не жаловался. Например, если одновременно с исправлением орфографической ошибки вы также добавите предложение в конце README, которое ссылается на что-то, упомянутое в первом абзаце, ваши изменения могут не иметь смысла вместе с изменениями Боба. Однако git не может сказать, что последняя строка и первый абзац связаны, потому что он не может угадать значение или семантика файла, который он объединяет. То же самое относится и к файлам кода.
Как это двусмысленно? И да, довольно много.