Git merge vs rebase: что выбрать для своей команды
И merge, и rebase объединяют изменения веток, но по-разному обращаются с историей: merge сохраняет её как было и добавляет коммит слияния, rebase переписывает ваши коммиты поверх новой базы и делает историю линейной. Практичное правило: rebase — для наведения порядка в своих неопубликованных коммитах, merge — для вливания готовой работы в общие ветки.
Коротко: в чём разница между merge и rebase
Спор «merge vs rebase» на самом деле не о том, какая команда лучше, а о том, какую историю проекта вы хотите видеть. Обе команды приводят к одному и тому же коду в итоге. Различается путь, который будет записан в журнале.
- Merge соединяет две линии истории и честно фиксирует, что работа шла параллельно. Существующие коммиты не меняются, добавляется merge-коммит с двумя родителями.
- Rebase переписывает ваши коммиты так, будто вы начали работу от самой свежей версии базовой ветки. История выглядит прямой линией, но коммиты получают новые хеши.
Механику каждой команды — флаги, разрешение конфликтов, интерактивный режим — мы разобрали в отдельных статьях. Здесь — только выбор стратегии.
Как выглядит история в обоих случаях
Исходная ситуация: вы ответвили feature, сделали два коммита, а в main за это время появились два чужих.
D---E feature
/
A---B---C---F---G main
После git merge feature в main:
D---E
/ \
A---B---C---F---G---M main
После git rebase main в feature и перемотки main:
A---B---C---F---G---D'---E' main
В первом случае в истории видно, что фича делалась параллельно и когда её влили. Во втором история читается как последовательный рассказ, но факта параллельной работы больше нет: D' и E' — новые коммиты, созданные в момент rebase.
Таблица сравнения merge vs rebase
| Критерий | git merge | git rebase |
|---|---|---|
| Существующие коммиты | Не меняются | Пересоздаются с новыми хешами |
| Форма истории | Ветвящийся граф с merge-коммитами | Линейная |
| Безопасность для общих веток | Безопасен всегда | Опасен для опубликованных коммитов |
| Нужен ли force push | Нет | Да, если ветка уже на сервере |
| Конфликты | Решаются один раз, в merge-коммите | Могут появляться на каждом переносимом коммите |
| Контекст «откуда пришло» | Сохраняется | Теряется |
| Откат целой фичи | Один git revert -m 1 merge-коммита | Revert каждого коммита или диапазона |
Удобство git bisect и git log | Сложнее читать граф | Проще: одна прямая линия |
| Порог входа | Низкий | Выше, нужно понимать переписывание истории |
| Чистка коммитов (squash, reword) | Только --squash целиком | Гибко через rebase -i |
Плюсы и минусы merge
Плюсы
- Не разрушает историю. Всё, что было опубликовано, остаётся на месте. Коллегам не придётся ничего чинить после вашего действия.
- Конфликт решается один раз. Даже если в ветке 30 коммитов, вы разбираетесь с итоговым состоянием, а не с каждым шагом.
- Сохраняется контекст. По merge-коммиту видно, какая задача когда попала в основную ветку, и её легко откатить целиком.
Минусы
- Шумный граф. При частых синхронизациях история обрастает коммитами «Merge branch 'main' into feature», и в
git log --graphначинается «железнодорожная схема». - Черновые коммиты попадают в историю. «wip», «fix», «ещё фикс» навсегда остаются в основной ветке, если их не сжать.
Плюсы и минусы rebase
Плюсы
- Линейная, читаемая история. Каждый коммит стоит на своём месте,
git logчитается как список изменений, аgit bisectпроще находит коммит с ошибкой. - Возможность привести коммиты в порядок перед ревью: склеить, переименовать, переставить.
- Нет технических merge-коммитов от синхронизации с основной веткой.
Минусы
- Переписывает историю. Rebase уже опубликованной общей ветки ломает работу всем, кто её скачал.
- Требует force push для веток на сервере, а значит дисциплины и
--force-with-lease. - Повторные конфликты. Один и тот же участок может конфликтовать на нескольких коммитах подряд.
- Промежуточные коммиты могут не собираться. После переноса на новую базу старые коммиты никто не тестировал в новом окружении.
Когда что использовать
Rebase — для своей локальной работы
Вы обновляете свою ветку перед отправкой — git pull --rebase. Готовите ветку к ревью и хотите убрать черновые коммиты — git rebase -i. Ваша личная feature-ветка отстала от main — git rebase origin/main. Во всех этих случаях переписываются коммиты, которые видели только вы (или которые принадлежат только вам).
Merge — для общих веток и интеграции
Готовая фича вливается в main или develop. Над веткой работают несколько человек. Нужно влить релизную ветку обратно в основную. Здесь важно не трогать уже опубликованные коммиты, и merge — единственный корректный вариант.
Простое правило выбора
Если коммиты есть только у вас — можно rebase. Если их уже кто-то скачал — только merge.
Типовые схемы для команды
| Схема | Как работает | Кому подходит |
|---|---|---|
| Только merge | Фичи вливаются через --no-ff, синхронизация — git merge main | Новичкам в Git, командам, где важна полная хронология и простые откаты |
| Rebase + merge | Ветку перед вливанием перебазируют на main, затем merge-коммит с --no-ff | Командам, которым нужны и чистая история, и границы фич |
| Rebase + fast-forward | Ветку перебазируют и вливают перемоткой, история полностью линейна | Небольшим командам с аккуратными атомарными коммитами |
| Squash merge | Каждый pull request превращается в один коммит в main | Командам с короткими задачами, где детальная история ветки не нужна |
На GitHub и GitLab эти схемы соответствуют кнопкам «Create a merge commit», «Rebase and merge» и «Squash and merge». Лучше разрешить в настройках репозитория только выбранный вариант, чтобы история не превращалась в смесь стилей.
Настройки, которые закрепляют выбор
# pull без лишних merge-коммитов
git config --global pull.rebase true
# или: pull только перемоткой, иначе ошибка — решаете сами
git config --global pull.ff only
# всегда merge-коммит при слиянии фич
git config --global merge.ff false
Будьте внимательны с merge.ff false: он действует и на git pull в режиме merge. Многие предпочитают не ставить его глобально, а писать --no-ff явно при вливании фичи.
Пример: одна задача в двух стилях
Разберём живой сценарий. Вы три дня делаете фичу оплаты в ветке feature/payment, за это время в main влили пять чужих задач. Перед pull request нужно подтянуть свежий main и проверить, что всё работает.
Вариант с merge
git switch feature/payment
git fetch origin
git merge origin/main
# решили конфликты один раз, git add, git commit
git push
Ветка получила коммит «Merge remote-tracking branch 'origin/main' into feature/payment». Push проходит обычный, коллеги, которые смотрели ветку, ничего не замечают. Если синхронизироваться так каждый день, в ветке будет три таких коммита, и в main они попадут вместе с фичей, если вливать без squash.
Вариант с rebase
git switch feature/payment
git fetch origin
git rebase origin/main
# конфликты решаются по ходу, на каждом коммите: git add, git rebase --continue
git rebase -i origin/main # по желанию: склеить черновые коммиты
git push --force-with-lease
Ветка выглядит так, будто начата сегодня утром от свежего main, лишних коммитов нет, ревьюеру проще читать изменения по шагам. Цена — принудительный push и необходимость следить, чтобы никто больше не коммитил в эту ветку.
Что в итоге увидит main
При вливании через pull request разница сохранится. Merge-стиль даст граф с ответвлениями и синхронизирующими слияниями, rebase-стиль — аккуратную цепочку коммитов фичи, которую можно влить перемоткой или одним merge-коммитом. Код при этом будет одинаковым, если конфликты разрешены одинаково.
Как принять решение в команде
Ответьте на несколько вопросов. Работают ли в одной ветке несколько человек? Если да, rebase для этой ветки исключён. Насколько опытна команда в Git? Если новички частые, начните с merge и squash merge — они прощают ошибки. Нужна ли полная хронология для аудита и разбора инцидентов? Тогда merge-коммиты фич полезны. Важен ли читаемый git log и частый git bisect? Тогда в пользу rebase и линейной истории.
Запишите решение в CONTRIBUTING или README репозитория и включите защиту основной ветки: запрет force push, обязательный pull request, разрешённый способ слияния. Правило, которое закреплено настройками, работает надёжнее любой устной договорённости.
Частые заблуждения
- «Rebase — это продвинуто, merge — для новичков». Нет: это разные инструменты под разные задачи. Опытные разработчики используют оба.
- «Merge-коммиты — мусор». Синхронизирующие — да, часто шум. Но merge-коммит вливания фичи — полезная точка: по ней видно и откатывается целая задача.
- «После rebase меньше конфликтов». Конфликты те же, ведь меняются те же строки. Rebase лишь раскладывает их по коммитам, а merge решает разом.
- «Rebase теряет коммиты». Старые коммиты остаются в
git reflogи восстанавливаются, пока их не удалит сборка мусора.
Итог
Выбор merge vs rebase — это выбор между честной хронологией и читаемой линейной историей. Merge безопасен и сохраняет контекст, rebase даёт чистоту, но переписывает коммиты. Самая надёжная практика — сочетать: перебазировать и чистить свою ветку, пока она ваша, и вливать готовую работу в общие ветки через merge или squash по правилам команды. Главное — договориться о схеме и закрепить её в настройках репозитория. Обе стратегии и работа с ветками в команде разобраны на практике в курсе по Git.
Частые вопросы
Что использовать для git pull — merge или rebase?
Для обновления локальной ветки удобнее rebase: ваши неопубликованные коммиты встанут поверх свежих удалённых без лишнего merge-коммита. Включается через git config --global pull.rebase true.
Можно ли смешивать merge и rebase в одном проекте?
Да, и это самый распространённый подход: rebase для своих веток, merge для общих. Важно лишь, чтобы правило было известно всей команде.
Что выбрать новичку?
Начните с merge: он не переписывает историю и сложнее что-то сломать. Rebase осваивайте на своих локальных ветках, когда разберётесь с reflog и force push.
Влияет ли выбор стратегии на код в итоге?
Нет, при одинаковом разрешении конфликтов итоговое состояние файлов совпадает. Различается только записанная история коммитов.
Чем squash merge отличается от rebase?
Squash merge сжимает всю ветку в один новый коммит в основной ветке, а rebase переносит каждый коммит по отдельности. Оба дают линейную историю, но squash теряет детализацию по шагам.
Эта тема подробно разобрана с практикой в курсе «Git с нуля до уверенного разработчика».
- Git-процессы в реальных командах: какой workflow выбрать
- Продвинутые инструменты: rebase, stash, cherry-pick и теги