W3docs

стратегии слияния

Стратегии слияния Git — ort, recursive, resolve, octopus, ours, subtree, fast-forward, squash и явные слияния с примерами.

mergeconflicts

Стратегии слияния Git

Когда работа над веткой завершена и готова к объединению с основной линией разработки, Git должен решить, как совместить две истории. Алгоритм, который он использует для этого, называется стратегией слияния.

Стратегия слияния берёт две (или более) вершины веток и производит единый результат. В большинстве случаев вам никогда не нужно явно указывать стратегию — команда git merge сама выбирает разумное значение по умолчанию в зависимости от того, сколько веток вы объединяете и расходились ли их истории. Когда вам нужен контроль, передайте -s <strategy> (и опционально -X <strategy-option> для тонкой настройки):

git merge -s recursive feature

На этой странице рассматриваются две связанные концепции, которые легко перепутать:

  • Стратегии слияния (-s): алгоритм, вычисляющий объединённое дерево — ort, recursive, resolve, octopus, ours, subtree.
  • Типы слияния: вид результата, который вы получаете — явный коммит слияния, fast-forward или squash.

Вам почти никогда не нужно выбирать стратегию вручную. Значения по умолчанию верны для подавляющего большинства слияний; прибегайте к -s или -X только при возникновении конкретной проблемы, которую они решают.

Алгоритмы стратегий слияния

ort (по умолчанию)

git merge -s ort feature

ort ("Ostensibly Recursive's Twin") является стратегией слияния двух голов по умолчанию начиная с Git 2.34 (2021). Это более быстрая и корректная переработка старой стратегии recursive, дающая тот же результат: трёхстороннее слияние, умеющее работать с переименованиями и рекурсивно объединяющее несколько общих предков в единого виртуального предка.

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

git checkout main
git merge feature

recursive

git merge -s recursive feature

Оригинальная трёхсторонняя стратегия для слияния двух веток, использовавшаяся по умолчанию до Git 2.34. Она умеет обнаруживать и отслеживать переименования, но не может использовать обнаруженные копии файлов. Запрашивать её по имени сейчас практически не нужно — ort заменяет её, — но она по-прежнему доступна для обратной совместимости.

resolve

git merge -s resolve feature

resolve выполняет одиночное трёхстороннее слияние ровно двух голов (текущей ветки и указанной вами). Она не пытается хитро обрабатывать несколько баз слияния, что делает её быстрой и предсказуемой, однако может ошибиться при «перекрёстной» истории, где две ветки ранее уже сливались друг в друга. Используйте её только тогда, когда слияние recursive/ort даёт результат, который вы хотите перепроверить с помощью более простого алгоритма.

octopus

git merge -s octopus topic-a topic-b topic-c

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

ours

git merge -s ours obsolete-branch

Стратегия ours создаёт коммит слияния, который имеет другую ветку в качестве родителя, но оставляет дерево текущей ветки полностью неизменным — все изменения из другой ветки отбрасываются. Типичное использование — отметить ветку как «слитую» в истории (чтобы будущие слияния знали о ней), игнорируя её фактическое содержимое, например при закрытии долгоживущей ветки, изменения которой больше не нужны.

Внимание

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

subtree

git merge -s subtree project-b

subtree — вариант алгоритма recursive/ort для случая, когда одно дерево является подкаталогом («поддеревом») другого. Перед слиянием Git сдвигает пути одного дерева так, чтобы они совпали, затем выполняет обычное слияние. Это механизм, позволяющий включить один проект в подпапку другого. Для повседневной работы с поддеревьями обычно удобнее использовать высокоуровневую команду git subtree.

Типы слияния: как выглядит результат

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

Слияние fast-forward

Когда ветка, в которую вы выполняете слияние, не продвигалась с момента создания другой ветки, объединять нечего — Git просто перемещает указатель ветки вперёд до последнего коммита. Новый коммит не создаётся, и история остаётся строго линейной. Это поведение Git по умолчанию, когда оно возможно:

git checkout main
git merge feature
# Output (when main is an ancestor of feature):
# Updating a1b2c3d..d4e5f6a
# Fast-forward
#  app.js | 3 +++
#  1 file changed, 3 insertions(+)

Чтобы сохранить явную запись о слиянии даже при возможности fast-forward, принудите создание коммита слияния с помощью --no-ff:

git merge --no-ff feature

Явный коммит слияния (трёхстороннее)

Когда в обеих ветках есть новые коммиты — то есть их истории разошлись — Git создаёт совершенно новый коммит слияния с двумя родителями. Он «явный», потому что этот коммит виден в истории и фиксирует, где и когда именно ветки соединились:

git checkout main
git merge feature
# Output (when histories diverged):
# Merge made by the 'ort' strategy.
#  app.js | 5 +++++
#  1 file changed, 5 insertions(+)

Если обе ветки изменили одни и те же строки, слияние остановится с конфликтом, который нужно разрешить вручную — см. конфликты слияния.

Слияние squash

Слияние squash сжимает все коммиты из исходной ветки в один новый коммит на текущей ветке. При этом не создаётся коммит слияния и не записывается исходная ветка как родитель, поэтому отдельные коммиты исходной ветки никогда не появляются в истории целевой ветки:

git checkout main
git merge --squash feature
# Changes are staged but NOT committed yet:
git commit -m "Add feature X"

Это сохраняет чистоту истории основной ветки — один коммит на функцию — ценой потери детального журнала коммитов функциональной ветки. Это популярная политика для pull request'ов. Если вы хотите переписать коммиты внутри ветки, используйте интерактивный rebase.

Опции стратегии (-X)

Стратегии ort/recursive принимают дополнительные опции через флаг -X (обратите внимание на заглавную X, отдельно от -s). Например, для автоматического разрешения конфликтов в пользу вашей стороны:

git merge -X ours feature

Доступные опции:

ОпцияЭффект
oursАвтоматически разрешает конфликтующие блоки в пользу нашей стороны. Неконфликтующие изменения из другого дерева по-прежнему включаются. (В отличие от -s ours, которая полностью отбрасывает другую сторону.)
theirsПротивоположность ours: автоматически разрешает конфликты в пользу другого дерева. Отдельной стратегии theirs нет, только эта опция.
patienceТратит дополнительное время на сопоставление строк, чтобы избежать ошибочных слияний из-за незначимых совпадающих строк.
diff-algorithm=<algo>Указывает слиянию использовать другой алгоритм diff (например, histogram, minimal, patience).
ignore-space-change / ignore-all-spaceИгнорирует различия, состоящие только из пробелов, при обнаружении конфликтов. Изменения пробелов, смешанные с реальными изменениями, не игнорируются.
renormalizeВыполняет виртуальные извлечение и возврат всех стадий файла, полезно при изменении правил окончаний строк или фильтров smudge/clean.
no-renormalizeОтключает опцию renormalize.
no-renamesОтключает обнаружение переименований во время слияния.
find-renames=<n>Включает обнаружение переименований с порогом сходства n% (по умолчанию 50%).
subtree=<path>Аналогично стратегии subtree, но позволяет указать префикс пути, который нужно сдвинуть для выравнивания деревьев.

Как выбрать

В повседневной работе вы вообще не выбираете стратегию — позвольте Git использовать ort, и определяйте только результат, который хотите получить:

  • Хотите простую, линейную историю там, где это возможно? Просто git merge (fast-forward происходит автоматически).
  • Хотите, чтобы каждое слияние фиксировалось в виде коммита? Добавьте --no-ff.
  • Хотите один чистый коммит на функцию в ветке main? Используйте --squash.
  • Объединяете несколько готовых тематических веток сразу? Обычный git merge a b c использует octopus.

Если вы предпочитаете воспроизвести свои коммиты поверх целевой ветки вместо слияния, обратитесь к git rebase. Чтобы перенести один коммит из другой ветки, используйте git cherry-pick.

Практика

Практика
Каковы различные стратегии слияния в Git и их характеристики?
Каковы различные стратегии слияния в Git и их характеристики?
Was this page helpful?