Сравнение

Git merge vs rebase: что выбрать для своей команды

Обновлено: · Skillaria.Top

И 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 mergegit 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 с нуля до уверенного разработчика».

Перейти к курсу