← Git: основы
12 мин · средне · Урок 4 из 6

Merge vs rebase

Что делает merge, что делает rebase, когда выбирать что и почему не ребейзим публичное.

Постановка

У тебя ветка feature от main. Пока ты в ней работал, в main прилетели чужие коммиты. Теперь твоя ветка отстала:

main:     A --- B --- C --- D
                \
feature:         E --- F

Нужно объединить твои E, F с актуальной main. Есть два способа: merge и rebase. Они дают одинаковый итоговый код, но разную историю.

Merge

git switch main
git merge feature

Git создаёт новый merge-commit M, у которого два родителя: последний коммит main и последний коммит feature.

main:     A --- B --- C --- D --- M
                \              /
feature:         E --- F ------

Что важно:

Fast-forward merge

Частный случай: main не двигалась, пока ты работал. Тогда merge просто передвигает указатель main вперёд на конец feature - никакого M не создаётся:

до main feature A B C D после A B C D main · feature

История остаётся линейной. Иногда команды требуют отключать FF (git merge --no-ff), чтобы merge-commit всё равно остался - по нему легко найти границы фичи в логе.

Rebase

git switch feature
git rebase main

Git берёт твои коммиты E, F, отрезает их от старого основания и прикладывает заново поверх свежего main:

было:
main:     A --- B --- C --- D
                \
feature:         E --- F

стало:
main:     A --- B --- C --- D
                            \
feature:                     E' --- F'

E' и F' — новые коммиты с новыми SHA. Содержимое то же самое, но это другие объекты в git.

После такого feature можно влить FF-merge'ем в main — получится линейная история без merge-коммитов.

Когда что выбирать

Два правила, которыми пользуются большинство команд:

Подтягивать main в свою длинную ветку можно и rebase, и merge. Trade-off: rebase оставляет линию чистой, но переписывает SHA твоих коммитов; merge ничего не переписывает, зато добавляет merge-коммиты в историю ветки.

Многие команды настраивают MR-стратегию централизованно: GitLab/GitHub позволяют выбрать "squash and merge", "rebase and merge", "merge commit". Тогда тебе не нужно думать — правило одно для всех.

Золотое правило: не ребейзим публичное

Если коммиты уже запушены и у кого-то локально могут лежать — не ребейзь их.

Почему: rebase создаёт новые SHA. У тебя локально старые E, F исчезли, появились E', F'. У коллеги в его клоне старые E, F остались. При следующем git pull git увидит две разные истории и впадёт в конфликт. Force-push в общую ветку — расстрел проекта.

Безопасные кейсы для rebase:

Опасные:

Как запушить ветку после rebase

Второй пункт в «безопасных» случаях — свою запушенную feature-ветку ребейзить можно. Но обычный git push после этого откажется работать:

! [rejected]  feature -> feature (non-fast-forward)

Логика Git понятна: на сервере лежат E, F, у тебя теперь E', F', и одно не является продолжением другого. Обновить ветку можно только перезаписью.

Первое, что советует интернет, — git push --force. Работает, но бьёт вслепую: команда затирает удалённую ветку тем, что у тебя локально, не глядя на то, что там сейчас. Если коллега за это время успел дописать коммит в ту же ветку, его работа исчезнет без единого вопроса.

Правильный вариант:

git push --force-with-lease

Флаг сначала сверяется: удалённая ветка должна указывать ровно на тот коммит, который ты видел при последнем fetch. Совпало — Git перезаписывает. Не совпало (кто-то запушил после тебя) — push отклоняется, и ты сначала разбираешься, что там появилось.

Правило простое: --force-with-lease вместо --force всегда, а сам force — только в своей ветке. В main не форсят даже с проверкой, поэтому её обычно защищают настройками репозитория, чтобы force-push туда был технически невозможен.

Что важно запомнить

Merge сохраняет историю + добавляет merge-commit. Rebase переписывает коммиты твоей ветки поверх свежей базы.

Fast-forward merge возможен, когда базовая ветка не двигалась — тогда указатель просто прыгает вперёд.

Rebase для своей feature, merge для интеграции чужой работы.

Не ребейзим то, что уже у других. SHA меняются, история расходится.

После rebase запушенной ветки обычный push отклоняется. Обновляют её через git push --force-with-lease: он откажется затирать чужие коммиты, появившиеся после твоего последнего fetch. Голый --force такой проверки не делает.

В следующем уроке — что делать, когда merge или rebase споткнулся о конфликт.

Дрилл · вопросы как на собесе

Проверь себя без подсказок

Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.

01

Что создаёт git merge и что такое fast-forward?

GIT-111
02

Почему нельзя ребейзить публичную (запушенную) ветку?

GIT-112
03

В чём разница между git merge и git rebase? Когда что выбирать?

GIT-002

Прошли материал до конца? Отметьте урок пройденным.