Конфликты слияния Git во время PR

Я сделал немного необычную настройку, когда сначала были настроены репозитории, вместо заполнения ветки master файлами проекта была создана и заполнена ветка transfer. Затем файлы загружались в development через PR, а затем снова в master через другой PR.

И, в дальнейшем, тематические ветки создаются из development ветки. После этого был создан PR для topic --› development и еще один PR для development --› master. Но я продолжаю получать конфликт слияния для второго PR development --› master, такого как Добавлено в обоих, Редактировано в обоих и т. д. в Azure DevOps.

Прямо сейчас для завершения PR в development и master используется только слияние сквоша.

Я хочу знать, происходят ли те конфликты слияния, которые я вижу, из-за исходной настройки репозитория или слияния сквоша. diff PR для development и master также содержит много изменений, которых я тоже не ожидаю.

Я не могу понять это.


person KZee    schedule 27.11.2020    source источник


Ответы (3)


Конфликты возникают из-за того, что вы используете слияние сквоша и постоянно реинтегрируете одну ветку. Со сквошем вы не выполняете настоящего слияния (другими словами: история не связана). Вместо этого вы создаете один новый коммит в ветке master.

Позже, когда вы попытаетесь объединить development с master, все предыдущие изменения из разработки будут снова объединены (но они уже существуют в master). Вот почему вы видите конфликты.

Я вижу 2 варианта:

  • Не используйте сквош вообще для веток реинтеграции и используйте обычные слияния
  • Воссоздавайте development из master после каждого слияния
person knittl    schedule 27.11.2020
comment
«Со сквошем вы не делаете настоящего слияния». Я бы хотел, чтобы люди просто поняли этот факт. Это ужасное имя. Сквош-слияние — это не слияние! Это ужасная идея, и этот вариант использования — одна из очевидных причин, почему. - person matt; 27.11.2020
comment
Спасибо. Полагаю, под обычным слиянием вы имеете в виду базовое слияние (без быстрой перемотки вперед)? Четыре доступных типа объединения Azure DevOps здесь: prnt.sc/vres0r - person KZee; 27.11.2020
comment
@KZee да, точно, я говорю о непрямом слиянии, которое создаст новый коммит слияния с двумя родителями. Эти коммиты слияния необходимы, если вы планируете объединять ветку несколько раз. - person knittl; 27.11.2020

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

Начнем с того, что создадим файл на master и зафиксируем его:

$ git init
$ echo "a" >> a.txt
$ git add .
$ git commit -m'start'

Теперь мы создаем develop и модифицируем этот файл:

$ git branch develop
$ git checkout develop
$ echo "b" > a.txt
$ git add .
$ git commit -m'changed a to b'

Мы возвращаемся к master и делаем сквош-слияние, и, кажется, все работает нормально:

$ git checkout master
$ git merge --squash develop
$ git commit -m'a squash commit from develop'

Все идет нормально. Теперь мы совершаем ужасную ошибку: мы делаем это снова. Мы переключаемся на develop и еще немного модифицируем этот файл:

$ git checkout develop
$ echo "c" > a.txt
$ git add .
$ git commit -m'changed b to c'

Возвращаемся к master и снова делаем сквош-слияние:

$ git checkout master
$ git merge --squash develop

Аааааааааааааааааааааааа у нас конфликт. Игра закончена.


Что случилось? Ну, проблема в том, что сквош-слияние не слияние. Это своего рода самодельный пластырь. Он создает конфигурацию индекса (промежуточной области), и вы фиксируете ее. В моем постановлении выше мы совершили это как отдельный шаг; с GitHub это сделано для вас. Но дело в том, что этот коммит, хотя и содержит изменения, которые произошли бы в результате реального слияния, не коммит слияния: это просто обычный коммит, как если бы вы сами внесли эти изменения, работая непосредственно над master.

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

  1. Слияние начинается с вычисления общей точки, из которой расходятся сливающиеся ветви: база слияния.

  2. Затем он вычисляет две разницы: от базы слияния до конца первой ветви и от базы слияния до конца второй ветви.

  3. Наконец, он вводит оба различия в базу слияния.

Хорошо, попробуйте сами, используя мысленный эксперимент.

Для первого слияния сквоша в моем вышеприведенном сценарии база слияния — start, где a.txt — это файл a. Так:

  • На master это все еще a, поэтому нет необходимости вводить различия.

  • На develop это b, поэтому разница будет change-a-to-b.

Итак, чтобы сформировать коммит слияния сквоша, мы просто меняем a на b.

Хорошо, давайте приступим ко второму слиянию сквоша в моем сценарии. Вот в чем дело: база слияния все еще запущена, где a.txt все еще a. Так:

  • master разница будет change-a-to-b.

    (Обратите внимание, что master не знает, что это произошло из-за какого-либо слияния; он думает, что это изменение было выполнено независимо кем-то, работавшим непосредственно над master.)

  • develop разница будет change-a-to-c.

Но вы не можете делать и то, и другое; это конфликт!

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

person matt    schedule 30.11.2020
comment
Если мы выполним обычное слияние, то, как я полагаю, началом следующих слияний будет точка фиксации обычного слияния? - person KZee; 30.11.2020
comment
@KZee Совершенно верно! И в этом вся разница. Даже в этом случае повторное слияние одной и той же ветки с одной и той же другой веткой может быть немного сложным, но повторное слияние одной и той же ветки с той же другой веткой почти гарантировано быть хитрым, потому что история ветвей просто продолжает накапливаться. - person matt; 01.12.2020

Существующие ответы отлично подходят для объяснения, почему это произошло. Я хотел бы просто количественно определить простое эмпирическое правило, чтобы определить, какие типы слияния вы можете использовать для каких ветвей. Azure DevOps имеет следующие параметры при заполнении PR:

  1. Слияние (без быстрой перемотки вперед)
  2. Сквош
  3. Перебазировать и перемотать вперед
  4. Полулинейное слияние

Можно использовать любой из этих вариантов при завершении PR с topic на development в зависимости от ваших предпочтений. Но все коммиты в development и master священны и не должны изменяться. Обратите внимание, что варианты 2-4 потенциально перезаписывают коммиты в исходной ветке. По этой причине вам следует:

Используйте первый вариант Объединить только при завершении PR, когда ветка исходный имеет значение development или master (а также release или hotfix в Git Flow). .

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

person TTT    schedule 01.12.2020
comment
Ты прав. Мы можем установить политики ветвей, где мы можем ограничить типы слияния (где ветвь является назначением). Этого, я думаю, достаточно, чтобы предотвратить нежелательные коммиты от меня/людей в ветке master. - person KZee; 02.12.2020