git merge: как я получил конфликт в файле BASE? - PullRequest
3 голосов
/ 02 мая 2019

У меня в файле BASE файла git merge есть следующее:

<<<<<<<<< Temporary merge branch 1
    - _modifiedTimeWeak = 4.25.2019::11:41:6;
    - _lastID = 3;
    - weakCGTime = 4.25.2019::11:42:20;
    - strongCGTime = 1.2.1990::0:0:0;
=========
    - _modifiedTimeWeak = 5.1.2019::8:52:36;
    - _lastID = 3;
    - weakCGTime = 5.1.2019::8:52:36;
    - strongCGTime = 3.20.2019::17:13:20;
>>>>>>>>> Temporary merge branch 2

Я выполнил необоснованное объединение файла, поэтому нет проблем, но я хотел бы понять,что могло пойти не так.

Я проверил фиксацию BASE, указанную git merge-base, и она не включала конфликты слияния, как было представлено, поэтому это исключено.Это также первый раз, когда с этим сталкиваются, несмотря на то, что до этого произошло много слияний в этом хранилище.

Возможно, стоит отметить, что я использовал git merge merge-tool.

Что может привести к тому, что в файле BASE при выполнении слияния возникают конфликты слияния, и какие шаги можно предпринять, чтобы этого не произошло в будущем?

1 Ответ

4 голосов
/ 02 мая 2019

Жаргон ответ

Это происходит, когда существует несколько баз слияния и слияние баз слияния вызывает конфликт слияния. Кандидаты базы слияния - это LCA коммитов, которые вы выбираете для слияния, в подграфе, вызванном коммитами:

ОПРЕДЕЛЕНИЕ 3.1. Пусть G = (V; E) DAG и пусть x; y V . Пусть G x; y будет подграфом G , индуцированным множеством всех общих предков x и y . Определите SLCA (x; y) как набор узлов (листков) с нулевой степенью выхода в G x; у . Самые низкие общие предки x и y являются элементами SLCA (x; y) .

(см. https://www3.cs.stonybrook.edu/~bender/pub/JALG05-daglca.pdf). Здесь G - это ориентированный ациклический граф, сформированный коммитами в вашем хранилище, а x и y - это два коммита, которые вы выбираете для слияния. Я на самом деле предпочитаю определение 3.2, хотя оно использует posets , и может быть даже более жаргоном: оно кажется более подходящим (и фактически используется для генеалогии, это то, что делает Git).

Рекурсивная стратегия слияния , -s recursive, использует все базы слияния, объединяет каждую базу слияния и фиксирует результат вместе с конфликтами слияния и использует этот временный коммит в качестве новой базы слияния. Так что это ответ на первую часть вопроса («Что может привести к тому, что у базового файла ... возникнут конфликты слияния»).

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

Длинный, но более полезный ответ

Вы упоминаете, что использовали git merge-base. По умолчанию git merge-base выбирает один из кандидатов на фиксацию на основе наилучшего слияния и печатает этот хэш-идентификатор. Если вы запустите git merge-base --all в двух коммитах с ветвями, вы увидите, что есть несколько кандидатов на лучшее слияние с базой.

В типичном простом шаблоне ветвления и слияния мы имеем:

             o--o--o   <-- branch-A
            /
...--o--o--*
            \
             o--o--o   <-- branch-B

Общая база слияния - как указано в 3.1 или 3.2 в цитируемой статье; с 3.2 вы можете просто вернуться к двум подсказкам веток, пока не найдете коммит, который находится в обеих ветвях - это, конечно, коммит *, а Git просто нужно diff * против двух коммитов подсказок ветки-A и ветки -B соответственно.

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

...--o--o--*---o--o--o   <-- branch-C
            \ /
             X
            / \
...--o--o--*---o--o--o   <-- branch-D

Обратите внимание, что обе помеченные коммиты находятся на обеих ветвях , а обе коммиты одинаково близки к двум подсказкам ветвей . Запуск git merge-base --all branch-C branch-D напечатает два идентификатора хеша. Какой коммит должен использовать Git в качестве базы слияния?

Git ответ по умолчанию: Давайте использовать их все! Git будет работать, в действительности:

git merge <hash-of-first-base> <hash-of-second-base>

как рекурсивное (внутреннее) слияние. Это слияние может иметь конфликты!

Если слияние вызывает конфликты, Git не останавливается и получает помощь от вас, пользователя. Это просто передает конфликтующий результат. Это становится входом для внешнего git merge, который вы прямо запрашиваете:

...--o--o--*---o--o--o   <-- branch-C
            \ /
             M   <-- temporarily-committed merge result
            / \
...--o--o--*---o--o--o   <-- branch-D

Временная фиксация на самом деле не в графе, но для целей вашего внешнего слияния она может быть и так.

Как избежать проблемы

Теперь, когда мы видим, как возникает проблема, способы ее устранения понятнее - не совсем ясно , но ясно er , чем они были, по крайней мере:

  • Метод 1: избегать перекрестных слияний.

    Избегая всего, что создает несколько баз слияний, вы никогда не попадете в ситуацию с самого начала. Это перекрестное слияние было создано в какой-то момент наличием branch-C и branch-D в этой ситуации:

           o--o--o   <-- branch-C
          /
    ...--o
          \
           o--o--o   <-- branch-D
    

    Кто-то, скажем, человек С, побежал git checkout branch-C; git merge branch-D, чтобы получить:

           o--o--o---M1   <-- branch-C
          /         /
    ...--o         /
          \       /
           o--o--o   <-- branch-D
    

    Тогда некоторыеодин - вероятно, кто-то другой, у которого не было нового коммита их branch-C; давайте назовем этого человека D - побежал git checkout branch-D; git merge <commit that was the previous tip of branch-C before the new merge was just added>, чтобы получить:

           o--o--o   <-- branch-C
          /       \
    ...--o         \
          \         \
           o--o--o---M2   <-- branch-D
    

    Как только люди C и D поделились своими новыми коммитами, результат был:

           o--o--o---M1   <-- branch-C
          /       \ /
    ...--o         X
          \       / \
           o--o--o---M2   <-- branch-D
    

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

  • Метод 2: если вы делаете такие слияния, будьте осторожны с ними . Редактировать: это на самом деле не поможет: Git все еще используя коммиты, которые приходят до коммитов M1 и M2, и повторно объединяют их. Тем не менее, стоит немного подумать. Я оставлю часть этого текста в.

    Слияния в основном симметричны. Обратите внимание, что когда люди С и D запускали свои две команды git merge, они имели одинаковые коммиты , образующие тот же граф . Они начались с той же базы слияния , с теми же двумя коммитами с ветвлением. Два сравнения, полученные путем сравнения этой базы слияния с каждым кончиком ветви, были одинаковыми.

    Таким образом, лица С и D должны были разрешить какой-то конфликт, тот же, который вы видели в своей собственной базе слияния-слияния. Возможно, они сделали это автоматически: например, человек C мог запустить git merge -X ours, чтобы предпочесть его изменение коммита tip, для найденного вами конфликтного файла в M1, тогда как у человека D может быть также запустить git merge -X ours, чтобы предпочесть ее изменение коммита чаевых в M2.

    Эти асимметричные результаты были безвредны, за исключением самой бомбы замедленного действия и того факта, что вы позже выполнили рекурсивное слияние. Вы видите конфликт. Вам решать это - опять же - но на этот раз не наш простой / их трюк, если это сработало для людей С и D.

  • Метод 3 (простой, но, возможно, неправильный): используйте другую стратегию слияния.

    Рекурсивная стратегия слияния, -s recursive, - это та, которая берет этот граф, находит все кандидатов на базовую фиксацию слияния, а если их несколько, объединяет их и использует результат в качестве ввод для вашего слияния. Существует стратегия под названием resol (-s resolve), которая разделяет почти весь свой код со стратегией recursive . Разница в том, что когда (или после) вычисления всех баз слияний требуется всего лишь одна из нескольких баз.

    В этом случае, с двумя кандидатами на слияние, M1 и M2, -s resolve просто выберет M1 или M2, по-видимому, случайным образом (не на самом деле случайным образом, просто в зависимости от того, какой из них появится первым, но не контролируемым образом: очевидная причина выбора одного против другого). Git будет использовать этот коммит в качестве базы слияния.

    Проблема здесь очевидна: при использовании одного коммита - человека С или человека D - вы игнорируете разрешение конфликта, выбранное другим человеком. Эти два человека оставили вам бомбу замедленного действия, и ваш ответ с -s resolve - позволил бомбе уничтожить один из двух результатов, и я не посмотрел, кто был действительно прав, или стоит ли вместо этого что-то сделать лучше .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...