Как Git обрабатывает код слияния, который был перемещен в другой файл?
Скажем, у меня есть функция X в файле A, и я хотел переместить эту функцию в файл B. Тем временем кто-то еще внес изменения в функцию X в файле A.
Git делает что-то особенное при объединении этих двух изменений? Будет ли он распознавать ту же функцию, которая была перемещена в файл B, и применять изменения там? Или мы потеряли бы изменения или получили бы две копии одной и той же функции в файлах A и B?
Большинство статей, которые я нашел о перемещении кода в git, касаются в основном переименования целых файлов, а не блоков кода внутри файлов. Самым близким, что я нашел, является реклама Линуса в ядре:
А при использовании git все "держать движение кода отдельно от изменений" имеет еще более фундаментальную причину: git может отслеживать перемещение кода (опять же, перемещая весь файл или просто функцию между файлами) и выполняя "обвинение git -C'фактически будет следовать за движением кода между файлами. Это делается с помощью анализа сходства, но это означает, что если вы оба переместите код и измените его одновременно, git не сможет увидеть, что "о, эта функция изначально пришла из этого другого файла", и теперь вы получаете худшие аннотации о где код на самом деле возник.
Таким образом, кажется, что git распознает, что код был перемещен куда-то еще, но на самом деле не говорит, что происходит во время слияния.
4 ответа
Нет, мерзавец не будет делать этот уровень изменений. Он заметит, когда вы переместите весь файл; так, например, если вы переместили файл, удалили все остальное в нем, тогда у него может быть шанс получить изменения. Но он не вносит изменений в каждый подфайл или какой-либо рефакторинг.
Я не думаю, что предыдущий ответ является правильным в общем случае, потому что git на самом деле не заботится о файлах - имя файла используется в качестве основы для некоторой эвристики, но способ, которым git думает о контенте, не полностью сосредоточен вокруг идея файла. В отличие от других VCS, git отслеживает контент, и в этом случае контент перемещается, но это тот же контент.
В результате, git должен иметь возможность обрабатывать слияния между ветвями, даже если файлы были переименованы, или код перемещается между файлами, поэтому, в зависимости от того, что именно вы сделали, он, вероятно, будет нормально обрабатывать слияние.
Пока изменения в X не вызывают конфликт слияния (что может произойти, если вы изменили как оригинальную, так и переименованную версию), тот факт, что X был перемещен в B, не должен иметь значения, и слияние результат должен содержать результат обоих изменений. Если есть проблема, это указывает на то, что git неправильно отслеживает движение кода.
На практике предыдущий ответ, вероятно, основан на личном опыте, когда механизм обнаружения неисправен, и в этом случае может помочь https://git.wiki.kernel.org/index.php/GitFaq#How_to_manually_resolve_conflicts_when_Git_failed_to_detect_rename.3F.
Я думаю, что AlBlue правильный, а не Най.
В моем простом тесте git 1.7.0.5 не смог автоматически объединить две ветви, где одна ветвь вставила строку в функцию, а другая переместила эту функцию в новое место в том же файле. Похоже, git не отслеживает порции контента, меньшего, чем файл.
Чтобы разрешить конфликт слияния, лицо, выполняющее слияние, должно знать, как сработали два изменения и как их следует объединить.
Смотрите также git merge: применить изменения к коду, который перемещен в другой файл
Я думаю, что реальная ситуация намного сложнее, но из того, что я испытал, git может хорошо справиться с этим трехсторонним слиянием.
Например:
Одна функция файла в ветви A была перемещена в другое место в том же файле, и в то же время создайте ветвь B для ее хранения. Разница между ветвью B и ветвью A заключается только в изменении местоположения кода в одном и том же файле.
Ветвь С изменила эту функцию.
Итак, если вы делаете:
$ git rebase --onto B A C
Он автоматически объединит изменение из ветви C с новым местоположением этой функции в ветви B.