Пропускать файлы при объединении git веток в обоих направлениях - PullRequest
2 голосов
/ 05 августа 2020

У меня две ветки в репо git. Есть файл конфигурации, который должен быть разным в двух ветках. Я добавил config merge=ours в .gitattributes, чтобы сохранить файл конфигурации при слиянии. Конфигурационный файл не объединяется при объединении из ветки B в ветку A. См. График ниже

*---*---*---*--*(A)(HEAD) //config file in branch A remains
    \         /
     *---*---*(B)

Однако, когда я сливаюсь из ветви A в B, мой файл конфигурации также получает слияние

*---*---*---*--*(A)
    \         /  \
     *---*---*----*(B)(HEAD) //config file in B gets merge with A

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

1 Ответ

4 голосов
/ 05 августа 2020

A драйвер слияния , как определено в файле .gitattributes, используется только тогда, когда все три входных файла различаются .

Это означает, что настройка merge=ours ( и определение драйвера слияния ours) часто терпят неудачу. Я рекомендую не заморачиваться с этим: просто определите свой собственный процесс, с помощью которого вы будете их обрабатывать.

Подробнее

Прежде всего помните, что git merge не всегда выполняет настоящее слияние:

  • -s / --squash предписывает ему вообще не выполнять настоящее слияние, но он все равно будет использовать действие слияния как глагола (и запрещает перемотку вперед) .

  • --no-ff запретит операцию быстрой перемотки вперед без слияния, в этом случае принудительное слияние будет выполнено.

  • --ff-only вызовет сбой команды (и не слияние), если перемотка вперед без слияния невозможна.

Имейте это в виду при просмотре остальной части этого :

  • Здесь мы рассматриваем только стратегии разрешения и рекурсии; octopus merges и -s ours совершенно разные.

  • Один из входных коммитов всегда HEAD. Другой - коммит, который вы называете в командной строке. Команда git merge сама найдет фиксацию базы слияния; это третья фиксация ввода. Если существует более одной фиксации базы слияния, -s resolve выберет одну случайным образом, а -s recursive сначала объединит базы слияния - это внутреннее рекурсивное слияние - а затем сделает новую фиксацию для использования в качестве базы слияния для внешнее слияние.

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

  • Если базовая фиксация слияния - это другая фиксация, слияние не требуется: текущая ветка актуальна, и ничего не происходит.

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

  • сравнивать каждый файл в базе слияния с каждым файлом в HEAD, чтобы увидеть, что мы изменили;
  • сравните каждый файл в базе слияния с каждым файлом в другом коммите, чтобы увидеть, что они изменили; и
  • объединяют эти изменения.

Чтобы объединить эти изменения, Git будет использовать низкоуровневый драйвер слияния . Это тип драйвера, который вы можете определить с помощью настройки merge=<em>name</em> .gitattributes. (Примечание: код рекурсивного / разрешающего слияния будет выполнять собственное высокоуровневое слияние для обработки вновь созданных файлов, удаленных и переименованных файлов, прежде чем перейти к низкоуровневому обработчику слияния. Это высокоуровневое слияние может привести к конфликт, который приведет к остановке слияния, независимо от того, что может делать низкоуровневый драйвер.)

Вот где возникает проблема: высокоуровневый код слияния, который обрабатывает любые высокоуровневые конфликты, вообще не беспокоится о запуске низкоуровневого кода слияния , если он может его пропустить. Этот высокоуровневый код пропускает низкоуровневый код всякий раз, когда необработанные идентификаторы ha sh объединяемых файлов совпадают в нескольких коммитах.

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

Но предположим, что мы не изменили F, а они сделали . Тогда ha sh из F в базе слияния совпадает с ha sh из F в нашем коммите, но не с ha sh в их. Git предполагает, что правильное действие - взять свой файл. Он никогда не запускает драйвер низкого уровня, даже если это драйвер слияния ours.

Если Git имеет параметр .gitattributes, который заставляет его запускать драйвер низкого уровня для каждого из них файлы, которые устранят проблему. Но это не так.

...