Когда вы запускаете git merge <thing>
, Git должен найти три коммитов.
Одним из трех коммитов является ваш текущий коммит.(Ваш индекс и рабочее дерево должны совпадать, и команда front end git merge
обычно применяет это. Обычно неблагоразумно запускать git merge
в «грязной» ситуации, хотя git stash apply
делает это все время. Я рекомендуюизбегая git stash
, частично из-за этого.)
Один из трех коммитов, конечно, коммит, который вы называете с помощью аргумента <thing>
:
git merge theirbranch
выбираеткоммит в конце их ветви как второй из двух коммитов для слияния.
третий коммит - это то место, где происходит большая часть магии.Git находит этот третий коммит автоматически.Git называет это базисом слияния , и он получен из графа коммитов.В некоторых случаях легко увидеть, откуда это взялось.
Предположим, что ваша ветвь и их ветвь имеют очень простую дивергентную структуру:
o--...--o <-- yourbranch (HEAD)
/
...--o--*
\
o--...--o <-- theirbranch
Базой слияния этой структуры является просто commit*
.
В сложных установках Git по-прежнему находит базу слияния самостоятельно: база слияния является лучшим общим предком двух коммитов с ветвлением-кончиком, которые должны быть объединены.Однако перед слиянием вы можете провести расследование:
git merge-base --all theirbranch
сообщит вам, какие коммиты являются базой (ами) слияния.В идеале есть только один;когда их два или более, у Git есть проблема, которую Git решит, но (а) это довольно редко и (б) он немного мешает остальной части этого описания, так что давайте просто пока проигнорируем проблему.: -)
Найдя только одну базу слияния, Git теперь делает это:
git diff --find-renames <em>base</em> <em>your-commit</em>
, чтобы увидеть, что вы изменили; git diff --find-renames <em>base</em> <em>their-commit</em>
, чтобы увидетьчто они изменили.
То есть Git запускает git diff
дважды , используя то же самое (общий, общий) базовый коммит.
Учитывая ситуацию, которую вы описали выше, либо вы переименовали некоторые файлы, либо они переименовали некоторые файлы - либо, возможно, вы оба переименовали некоторые файлы.Опция --find-renames
указывает git diff
на обнаружение этих переименований.(В данном случае это вы переименовывали файлы.)
Поиск переименования не идеален, но в большинстве случаев он делает именно то, что нужно.Git обнаруживает, кто из вас переименовал какие файлы.Это позволяет Git идентифицировать базовый файл слияния (который должен быть folder1/src/Class1.java
) с переименованным файлом folder2/src/Class1.java
.Git помнит, что переименование также произошло.
В diff для их изменений Git идентифицирует исходный файл folder1/src/Class1.java
с неотименованным folder1/src/Class1.java
в их последнем коммите.
Поскольку эти изменения взяты из идентичных исходных файлов, Git теперь объединяет ваши изменения с их изменениями.Он пытается применить те же самые изменения - включая переименование - к базовому файлу.Таким образом, Git получает версию базового коммита folder1/src/Class1.java
, объединяет ваши изменения и их изменения - которые могут или не могут конфликтовать - и помещает результат в ваш индекс и рабочее дерево как folder2/src/Class1.java
, принимая (одиночное) переименование.
Все это объединение завершается неудачей, если Git не может идентифицировать исходные файлы base-commit с переименованными файлами.Таким образом, вы можете запустить тот же git diff --find-renames
, возможно, с --name-status
, чтобы пропустить просмотр реальных различий и просто посмотреть, что совпадает.Если правильные вещи совпадают, git merge
сделает правильные вещи.
Если буровая установкаht вещи не совпадают, вы можете попробовать настроить «порог переименования» Git, указанный как число --find-renames=<em>num</em>
.Число является пределом для индекса сходства Git .Когда Git сравнивает два коммита, такие как база слияния и ваш текущий коммит, если у базы слияния есть файл d1/d2/file.ext
, а у вашего коммита нет, а у вашего коммита d3/d4/other.ext
, которого нет у базы, Git сравниваетсодержимое двух файлов.Затем он вычисляет индекс сходства.Это, примерно, количество файла , которое было перенесено без изменений; максимально равно 100%, и, если ни одна из частей файла не совпадает, становится равным 0%.По умолчанию выполняется сопряжение файлов, которые по крайней мере на 50% похожи.Если один источник соответствует пяти адресатам, Git примет соединение с наивысшим индексом сходства, если оно соответствует пороговому значению.
Команда git merge
принимает тот же параметр, но записывает его -X find-renames=<em>number</em>
.
Если существует несколько баз слияний
Если git merge-base --all
дает вам более одной базы слияний, вот как Git решает эту проблему:
-s recursive
(по умолчанию): Git сначала объединяет базы слияний, выполняя на них git merge
, более или менее.Результирующий коммит становится базой слияния. -s resolve
: Git выбирает одну из баз слияния случайно (очевидно) и использует ее.
Все остальное - этото же самое, но учтите, что если рекурсивное слияние встречается с конфликтом слияния, Git просто фиксирует сам конфликт слияния.Этот конфликтующий результат слияния становится входной базой, что обычно вызывает странные конфликты.