Варианты слияния веток в Git и их отличия
В git есть несколько вариантов слияния веток, какие? Чем отличаются.
Базовые понятия: что значит «влить ветку»
Под «слиянием веток» обычно понимается интеграция изменений из одной линии разработки в другую так, чтобы целевая ветка получила нужные правки, а история коммитов осталась в понятной форме.
Для различий между способами слияния важно разделять два вопроса:
- «Какие изменения попадут в целевую ветку?» (итоговое содержимое файлов после операции).
- «Как будет выглядеть история коммитов?» (граф коммитов, наличие merge-коммита, линейность, переписывание хэшей).
Merge: обычный merge и fast-forward
git merge интегрирует изменения из указанной ветки (или коммита) в текущую ветку.
Если ветки разошлись, создаётся новый merge-коммит с двумя родителями, который явно фиксирует факт объединения историй.
Схема: merge-коммит (true merge)
Ниже показан типичный случай, когда ветки разошлись и нужен merge-коммит.
A---B---C feature
\
D---E---F main
# На ветке main выполняется:
git checkout main
git merge feature
# Результат (появляется merge-коммит M):
A---B---C feature
\ \
D---E---F---M main
Fast-forward как режим merge
Если текущая ветка не имеет собственных новых коммитов после точки ветвления и целевая история является предком вливаемой, merge может выполниться как fast-forward: указатель ветки просто сдвигается вперёд, а новый merge-коммит не создаётся.
# Пример fast-forward:
# main: A---B
# feature: A---B---C---D (main является предком feature)
git checkout main
git merge feature
# main станет указывать на D, нового merge-коммита не будет
# Полезные режимы:
git merge --ff-only feature # разрешать только fast-forward, иначе отказ
git merge --no-ff feature # создавать merge-коммит даже если возможен fast-forward
Rebase: перенос коммитов поверх новой базы
git rebase переносит последовательность коммитов текущей ветки на другую базу: создаётся список коммитов, затем они «проигрываются» по одному поверх выбранного коммита-основания.
Простая модель для начинающего: rebase делает историю «ровнее», но платой за это становится переписывание истории (появляются новые коммиты с новыми хэшами).
Схема: rebase для линейной истории
Смысл rebase: сохранить изменения из feature, но сделать историю линейной, без merge-коммита.
# Было:
A---B---C feature
\
D---E---F---G main
# На ветке feature:
git checkout feature
git rebase main
# Стало (коммиты feature становятся "новыми" A',B',C' поверх main):
D---E---F---G---A'---B'---C' feature
Типичные команды:
# Перенос текущей ветки поверх main:
git rebase main
# Интерактивный rebase для упорядочивания/объединения коммитов:
git rebase -i <after-this-commit>
Squash merge: «слить всё одним коммитом»
Squash merge означает «взять итог всех изменений ветки и оформить одним коммитом в целевой ветке», скрыв промежуточные шаги разработки.
В командной строке это часто делается как подготовка изменений без создания merge-коммита, а затем вручную создаётся один обычный коммит.
Мини-сценарий squash merge
# На целевой ветке (например, main):
git checkout main
# "Склеить" изменения ветки feature в индекс/рабочее дерево без merge-коммита:
git merge --squash feature
# Затем создать один коммит:
git commit -m "Добавлена функциональность X (squash)"
Таблица различий: merge vs rebase vs squash merge
| Способ | Как меняется история и коммиты | Когда обычно подходит |
|---|---|---|
| Merge | Добавляется merge-коммит (если не fast-forward), сохраняется ветвление; все коммиты из feature сохраняются как отдельные. | Когда важно сохранить контекст ветки и факт слияния, удобно для командной разработки и просмотра истории. |
| Rebase | Коммиты feature «перепроигрываются» поверх новой базы и пересоздаются, поэтому меняются хэши; история становится линейной. | Когда нужна линейная история и аккуратная последовательность коммитов перед интеграцией (с учётом риска переписывания истории). |
| Squash merge | Итог всех изменений feature попадает в целевую ветку одним новым коммитом; отдельные коммиты feature в целевой истории не сохраняются. | Когда нужен один «логический» коммит без промежуточных шагов разработки и не важна детализация коммитов feature. |
Итого: корректный набор «способов интеграции ветки» — merge, rebase и squash merge; merge может быть fast-forward или с merge-коммитом, rebase пересоздаёт коммиты поверх новой базы (переписывает историю), а squash merge превращает серию коммитов в один итоговый коммит в целевой ветке.