Git merge или rebase: различия и когда что использовать
Практика3 минРедакция Innovialabs
Коротко
Merge сохраняет историю как есть и добавляет коммит слияния, rebase переписывает коммиты в прямую линию. Rebase используйте только для своих локальных веток, общие ветки объединяйте через merge.
Короткий ответ
И merge, и rebase решают одну задачу — переносят изменения из одной ветки в другую. Разница в том, как выглядит история после этого:
- Merge сохраняет реальную историю и добавляет отдельный коммит слияния.
- Rebase заново применяет ваши коммиты поверх другой ветки, и история становится прямой линией.
- Squash merge схлопывает все коммиты ветки в один и добавляет его в основную ветку.
Главное правило: не делайте rebase веток, которыми уже пользуются другие.
Как выглядит история
Исходная ситуация: вы создали ветку feature от main, сделали коммиты D и E, а тем временем в main появились коммиты B и C.
D---E feature
/
A---B---C main
После merge
git switch main
git merge feature
D---E
/ \
A---B---C---M main
Появляется коммит слияния M с двумя родителями. Видно, что работа шла параллельно, ни один существующий коммит не изменился.
После squash merge
git switch main
git merge --squash feature
git commit -m "Add feature"
A---B---C---S main
Все изменения ветки собраны в один коммит S. История main чистая, но промежуточные шаги ветки в ней не видны.
После rebase
git switch feature
git rebase main
D'--E' feature
/
A---B---C main
Коммиты D и E пересозданы как D' и E' поверх C. У них новые хеши. Теперь git merge feature в main будет простой перемоткой (fast-forward), и история останется линейной.
Сравнение
| Критерий | Merge | Squash merge | Rebase |
|---|---|---|---|
| Переписывает историю | нет | нет (для main) | да |
| Вид истории | ветвящаяся | линейная | линейная |
| Сохраняет отдельные коммиты ветки | да | нет | да |
| Безопасен для общих веток | да | да | нет |
| Конфликты решаются | один раз | один раз | по каждому коммиту |
Золотое правило rebase
Не делайте rebase коммитов, которые уже отправлены и которыми пользуются другие.
Rebase создаёт новые коммиты вместо старых. Если коллега уже основал свою работу на старых коммитах, после вашего push --force их истории разойдутся: появятся дубликаты, конфликты и путаница.
Безопасно делать rebase:
- своей локальной ветки перед отправкой;
- своей личной ветки, с которой никто больше не работает;
- через
git pull --rebase, чтобы не создавать лишних коммитов слияния при обновлении.
Если всё же пришлось перезаписать отправленную личную ветку, используйте git push --force-with-lease — он откажет, если на сервере появились чужие коммиты.
Когда что использовать
- Merge — для слияния общих долгоживущих веток (
developвmain, релизные ветки) и когда важна точная хронология. - Squash merge — для pull request с множеством промежуточных коммитов вроде «fix typo»: в
mainпопадает одна осмысленная запись. - Rebase — чтобы обновить свою ветку свежим
mainперед pull request и навести порядок в локальных коммитах (git rebase -i).
Политика для команды
Простая и рабочая схема для большинства команд:
- Каждая задача — отдельная короткоживущая ветка от
main. - Перед pull request разработчик обновляет ветку через
git rebase main(ветка его личная, это безопасно). - В
mainизменения попадают через squash merge или обычный merge — выберите один вариант и закрепите в настройках репозитория. - Force push в
mainи другие общие ветки запрещён защитой ветки. - В
.gitconfigразработчиков можно включитьpull.rebase = true, чтобы локальные обновления не плодили коммиты слияния.
Конкретный выбор менее важен, чем единообразие: смешанные подходы делают историю труднее для чтения, чем любой из них по отдельности.