Когда бы вы использовали разные стратегии git merge?

На странице руководства git-merge есть несколько стратегий слияния, которые вы можете использовать.

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

  • рекурсивный - это может разрешить только две головы с использованием алгоритма 3-way merge. Если для трехстороннего слияния можно использовать несколько общих предков, оно создает объединенное дерево общих предков и использует его в качестве ссылочного дерева для трехстороннего слияния. Сообщается, что это приводит к меньшему количеству конфликтов слияния, не вызывая слияний в результате тестов, выполненных на реальных коммитах слияния, взятых из истории разработки ядра Linux 2.6. Кроме того, это может обнаруживать и обрабатывать слияния, связанные с переименованием. Это стратегия слияния по умолчанию при извлечении или слиянии одной ветви.

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

  • ours - Это разрешает любое количество головок, но результатом слияния всегда является текущая головка. Он предназначен для замены старой истории развития боковых веток.

  • поддерево - это измененная рекурсивная стратегия. При объединении деревьев A и B, если B соответствует поддереву A, B сначала корректируется, чтобы соответствовать древовидной структуре A, вместо того, чтобы читать деревья на том же уровне. Эта корректировка также выполняется для общего дерева предков.

Когда я должен указать что-то отличное от значения по умолчанию? Для каких сценариев лучше всего подходит каждый?

3 ответа

Решение

Я не знаком с решимостью, но я использовал другие:

рекурсивный

Рекурсивный является значением по умолчанию для слияний без ускоренной пересылки. Мы все знакомы с этим.

Осьминог

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

Ветвь осьминога объединяет несколько голов в один коммит, если он может делать это чисто.

Для иллюстрации представьте, что у вас есть проект с мастером, а затем три ветви для объединения (назовите их a, b и c).

Серия рекурсивных слияний будет выглядеть следующим образом (обратите внимание, что первое слияние было ускоренным, так как я не вызывал рекурсию):

серия рекурсивных слияний

Тем не менее, одиночное слияние осьминога будет выглядеть так:

commit ae632e99ba0ccd0e9e06d09e8647659220d043b9
Merge: f51262e... c9ce629... aa0f25d...

слияние осьминога

наш

Наша == Я хочу добавить другую голову, но выбросить все изменения, которые вносит эта голова.

Это сохраняет историю ветви без каких-либо эффектов ветви.

(Читайте: это даже не рассматривало изменения между этими ветвями. Ветви просто слиты, и ничего не делается с файлами. Если вы хотите объединить в другую ветку, и каждый раз возникает вопрос "наша версия файла или их версия "вы можете использовать git merge -X ours)

Subtree

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

На самом деле единственными двумя стратегиями, которые вы хотели бы выбрать, являются наши, если вы хотите отказаться от изменений, внесенных ветвью, но сохранить ветку в истории и поддерево, если вы объединяете независимый проект в подкаталог суперпроекта (например, "git-gui" in "). Git 'хранилище).

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

"Решить" против "Рекурсивной" стратегии слияния

Рекурсивная является текущей стратегией с двумя головками по умолчанию, но после некоторого поиска я наконец нашел некоторую информацию о стратегии слияния "решить".

Взято из книги О'Рейли Контроль версий с помощью Git ( Amazon) (перефразировано):

Первоначально, "разрешение" было стратегией по умолчанию для слияний Git.

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

Я успешно объединил деревья, используя "resolv", который не удался с рекурсивной стратегией по умолчанию. Я получал fatal: git write-tree failed to write a tree ошибки, и благодаря этому сообщению в блоге ( зеркало) я попытался "-с разрешить", который работал. Я все еще не совсем уверен, почему... но я думаю, что это произошло потому, что у меня были дублирующие изменения в обоих деревьях, и я решил, что "пропустил" их правильно.

В Git 2.30 (первый квартал 2021 г.) будет новая стратегия слияния: ORT ("якобы рекурсивный близнец").

git merge -s ort

См. Фиксацию 14c4586 (2 ноября 2020 г.), фиксацию fe1a21d (29 октября 2020 г.) и фиксацию 47b1e89, фиксацию 17e5574 (27 октября 2020 г.) Элайджа Ньюрен (newren).
(Слияние Junio ​​C Hamano - gitster- в коммите a1f9595, 18 ноя 2020)

merge-ort: barebones API новой стратегии слияния с пустой реализацией

Подписано: Элайджа Ньюрен

Это начало новой стратегии слияния.

Хотя есть некоторые различия API, а реализация имеет некоторые отличия в поведении, по сути, это подразумевается как возможная замена для merge-recursive.c.

Тем не менее, он строится так, чтобы существовать бок о бок с рекурсивным слиянием, так что у нас есть достаточно времени, чтобы узнать, как эти различия проявляются в реальном мире, в то время как люди все еще могут вернуться к рекурсивному слиянию.
(Кроме того, я намерен избегать изменения рекурсивного слияния во время этого процесса, чтобы он оставался стабильным.)

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

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

И:

См. Commit 848a856, commit fd15863, commit 23bef2e, commit c8c35f6, commit c12d1f2, commit 727c75b, commit 489c85f, commit ef52778, commit f06481f (26 Oct 2020) Элайджа Ньюрен (newren).
(Слияние Junio ​​C Hamano - gitster- в коммите 66c62ea, 18 ноя 2020)

t6423, t6436: обратите внимание на улучшенную обработку орт с грязными файлами

Подписано: Элайджа Ньюрен

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

Бэкэнд "ort", напротив, выполняет полное слияние в памяти и обновляет только индекс и рабочую копию на этапе постобработки.
Если на пути есть грязные файлы, он может просто прервать слияние.

t6423: ожидайте улучшенных меток маркеров конфликтов в бэкэнде ort

Подписано: Элайджа Ньюрен

Маркеры конфликтов содержат дополнительную аннотацию в виде REF-OR-COMMIT:FILENAME, чтобы помочь различить, откуда поступает контент, с :FILENAME кусок будет опущен, если он одинаков для обеих сторон истории (таким образом, только переименования с конфликтами содержимого несут эту часть аннотации).

Однако были случаи, когда :FILENAME аннотация была случайно отключена из-за формата слияния-рекурсии для каждого-кода-пути-требуется-копия-всего-особого-кода-кода.

t6404, t6423: ожидайте улучшенной обработки переименования / удаления в бэкэнде ort

Подписано: Элайджа Ньюрен

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

Такой подход упрощает пользователю доступ к различным версиям и разрешение конфликта (нет необходимости вручную git rm'(человек) старая версия, а также'git add'(мужчина) новый).

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


С Git 2.30 (первый квартал 2021 г.), подготовка к новой стратегии слияния.

См. Commit 848a856, commit fd15863, commit 23bef2e, commit c8c35f6, commit c12d1f2, commit 727c75b, commit 489c85f, commit ef52778, commit f06481f (26 Oct 2020) Элайджа Ньюрен (newren).
(Слияние Junio ​​C Hamano - gitster- в коммите 66c62ea, 18 ноя 2020)

merge tests: ожидайте улучшения обработки конфликтов каталогов / файлов в ort

Подписано: Элайджа Ньюрен

merge-recursive.c построен на идее бега unpack_trees()а затем "мелкие поправки" для получения результата.
К сожалению, unpack_trees() был запущен в режиме обновления по мере продвижения, merge-recursive.c чтобы последовать их примеру и в итоге получить немедленную оценку и готовый дизайн.

Некоторые вещи, такие как конфликты каталогов / файлов, плохо представлены в структуре данных индекса, и для их обработки требуется специальный дополнительный код.
Но затем, когда было обнаружено, что конфликты переименования / удаления также могут быть вовлечены в конфликты каталогов / файлов, специальный код обработки конфликтов каталогов / файлов пришлось скопировать в путь кода переименования / удаления.
... а затем его нужно было скопировать для изменения / удаления и для конфликтов переименования / переименования (1to2)... и все же он все еще пропустил некоторые.
Кроме того, когда было обнаружено, что также существуют конфликты файлов / подмодулей и конфликты подмодулей / каталогов, нам нужно было скопировать специальный код обработки подмодулей во все особые случаи всей кодовой базы.

А затем было обнаружено, что наша обработка конфликтов каталогов / файлов была неоптимальной, потому что она создавала неотслеживаемые файлы для хранения содержимого конфликтующего файла, которое не было бы очищено, если бы кто-то запустил 'git merge --abort'(мужчина) или'git rebase --abort'(мужчина).

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

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

Поскольку ответы выше не показывают всех деталей стратегии. Например, в каком-то ответе отсутствуют сведения об импортеresolve вариант и recursive который имеет много дополнительных опций, таких как ours, theirs, patience, renormalize, так далее.

Поэтому рекомендую посетить официальный git документация, которая объясняет все возможные функции:

https://git-scm.com/docs/merge-strategies

Другие вопросы по тегам