Повторяющиеся слияния в GIT. Как рассчитать разницу? - PullRequest
2 голосов
/ 06 февраля 2020

Я пытался понять, как работает слияние GIT. Я знаю, что есть несколько типов слияния: рекурсивный, осьминог и т. Д. c. Я понял, что разрешение / рекурсив используется чаще всего. И это рекурсивное слияние полезно только тогда, когда есть несколько общих предков / баз.

Однако я не смог найти, какой алгоритм используется (или как должен рассчитываться предок) с повторяющимися слияниями с мастером из ветка.

Простой пример. Давайте создадим пустой проект с 1 файлом «A»:

A

Затем создадим еще один файл «B» и передадим мастеру

A
B

Затем я создам ветку из самая первая версия , в которой был только 1 файл "A" и создан другой файл "C". Таким образом, моя ветвь выглядит следующим образом:

A
C

Затем я решаю объединить изменения моей ветви с основной и получаю:

A
B
C

Затем я решаю go вернуться к своей ветке и продолжу мою работу оттуда. Я создаю другой файл "D"

A
C
D

Теперь я хочу объединить свои изменения из ответвления обратно в ствол. Как рассчитывается предок?

Визуальный пример: Repetitive merge

Если я возьму предка "A C", то должно произойти "B" также является новым дополнением, поскольку его не существовало в двух версиях: ветвь и предок.

Если я возьму предка "AB C", то следует сказать, что "B" удалено, поскольку B существовало в двух Версии: мастер и предок.

Обе эти опции выглядят неверно. Я попытался выяснить это с помощью "Plasti c SCM", который имеет функцию объяснения слияния. Как видно, предок / база используется как версия «A C», однако он все равно правильно рассчитал, сколько файлов было добавлено (только 1, а не 2).

enter image description here

1 Ответ

2 голосов
/ 07 февраля 2020

Чтобы суммировать комментарии и ответить на вопрос как задано ...

Поиск базы слияния

  1. Git вычисляет базу слияния пары коммитов с использованием алгоритма для поиска самого низкого общего предка направленного ациклиуса c График. Точный алгоритм нигде не описан и может измениться, пока новый дает правильные результаты. См. Также Алгоритм поиска наименьшего общего предка в направленном графике ацикли c?

    Может быть несколько LCA. В этом случае стратегия слияния -s resolve выбирает одну из них. Вы не можете контролировать, какой из них он выбирает. Стратегия слияния -s recursive запускает на них git merge по два, как если бы это было следующим:

    commits=$(git merge-base --all $left $right)
    if len($commits) > 1
        a=$commits[0]
        for i in range(1, len(commits))
            b=$commits[i]
            a=$(git-merge-recursively-inner $a $b)
        rof
        commits=($a)
    fi
    

    (в псевдокоде). Обратите внимание, что внутреннее рекурсивное слияние само может найти более одной базы слияния; если это так, он использует этот алгоритм для их объединения.

    Окончательный результат - один коммит, $commits[0]. Это база слияния.

  2. В любом случае, теперь, когда у нас есть единая фиксация базы слияния - из алгоритма нахождения LCA, который нашел только один LCA, или с помощью рекурсивного слияния слиянием множественные базы слияния, которые возникли из алгоритма LCA-поиска, или путем разрешения слияния, просто выбрав один коммит из списка - мы можем посмотреть, как git merge-(recursive|resolve) на самом деле объединяет файлы. Он должен выполнить две внутренние операции git diff, каждая с включенным детектором переименования.

Различия и идентификация файла / обнаружение переименования

A разница файла Движок сравнивает два файла. Мы помещаем один файл слева и другой файл справа. Там, где два файла совпадают, diff ничего не говорит. Там, где два файла различаются, механизм различий - в зависимости от того, насколько он хорош - предлагает набор изменений, которые мы можем применить, чтобы содержимое левой стороны соответствовало содержимому правого файла.

Для сравнения пара совершает , Git ставит один слева и один справа. Затем он должен выполнить сопряжение файлов в этих двух коммитах. Git может сделать это с включенным детектором переименования, или нет.

Изображение довольно четкое, когда детектор переименования отсутствует. Файлы слева и справа являются «одним и тем же файлом», если и только если они имеют одинаковое имя . Добавление детектора переименования идентифицирует (помечает как «одинаковые») некоторые файлы с левой и правой сторон от diff, даже если names изменились.

Git Существующий детектор переименования претерпевает некоторые изменения, чтобы сделать его лучше. Точных подробностей здесь не требуется: все, что нам нужно знать, - это то, что некоторые файлы будут переименованы, так что это «один и тот же» файл, даже если они имеют разные имена. Другие файлы автоматически становятся «одним и тем же» файлом, потому что они имеют одинаковые имена.

Для каждого парного файла механизм различий производит набор изменений, которые превращают файл левой части в правую часть. файл. Детектор переименования производит операции переименования, которые должны быть выполнены первыми. Файлы, которые new справа, называются добавлено , а файлы, которые существовали при фиксации слева, но не существуют при фиксации справа, удаляются.

Следовательно, diff-of-pair-of-commits приводит к:

  • файлам для переименования (от старого имени к новому имени)
  • файлов для добавления
  • файлы для удаления

плюс некоторые наборы изменений для файлов, которые существуют в обеих фиксациях, при необходимости.

Объединение с учетом базы объединения

При единой фиксации базы слияния как разрешение, так и рекурсивный процесс выполняются одинаково:

  • Различает базу слияния с HEAD с включенным обнаружением переименования. Это наши изменения.
  • Отличить базу слияния от других коммитов с включенным обнаружением переименования. Это их изменения.
  • Объединение изменений.

«Объединение» требует учета как изменений высокого уровня, таких как переименование, добавление и удаление, так и изменений низкого уровня в одном файле. Файл, к которому будут применены объединенные изменения, является файлом из базы слияния . Это гарантирует, что результат работает во всех случаях.

Например, предположим, что мы переименовали файл, а они изменили файл, который мы переименовали. Объединенные изменения говорят, что, по сути, в конце, переименуйте файл base.ext в head.ext; тем временем измените строку 17 из base.ext. Таким образом, мы изменим строку 17 и переименуем файл, захватывая оба действия.

Операции высокого уровня могут конфликтовать! Например, если мы переименуем файл, а они его удалят, это конфликт высокого уровня. Если и мы, и они переименовывают файл, это конфликт, если мы оба не выбрали одно и то же окончательное имя. Если и мы, и они удаляют файл, это хорошо сочетается с очевидным результатом.

Низкоуровневые изменения также могут конфликтовать. Конфликт возникает, если мы и они оба изменяем одни и те же строки по-разному, или если наши изменения и их изменения «соприкасаются» с обеих сторон. Например, если мы заменим строки 9 и 10 (удалите 2 строки после строки 8 и вставим 2 строки после строки 8), а они заменят строки 11 и 12, наши изменения упираются. Из общей осторожности называет это конфликтом.

Конечно, если мы и они сделаем одинаковые изменения в одинаковых исходных строках, это не конфликт , Git просто берет одну копию этих изменений.

Расширенная опция -Xours или -Xtheirs разрешает конфликты низкого уровня, выбирая одну сторону (нашу или свою), игнорируя другую. Это работает только для конфликтов низкого уровня. Логично, что это может относиться и к конфликтам высокого уровня, но это не так.

Объединив все наши и их изменения, Git применит объединенные изменения к снимку находится в коммите слияния . Полученные файлы могут быть зафиксированы автоматически, если нет конфликтов. Это действие по умолчанию для этих слияний; используйте --no-commit для подавления этой фиксации по умолчанию.

Когда рекурсивная слияние использует внутреннее слияние для создания базовой фиксации слияния, она принудительно фиксирует результат , даже если существуют конфликты слияния . Вы не сможете увидеть, что он сделал с этими конфликтами, за исключением того, что появляется в базе слияний, когда ваше (внешнее) слияние также имеет конфликт. (В этом случае копия файла базы слияния доступна в слоте индекса 1. Кроме того, если вы установите merge.conflictStyle на diff3, каждая копия рабочего дерева конфликтующего файла будет отображать текст из базы слияния , в комплекте с маркерами конфликта.)

...