Git merge vs rebase: отличия, примеры и когда что использовать
Выбор между Git merge и rebase определяет, как выглядит история проекта. Сравните оба варианта на одном примере с реальным выводом log, узнайте правило, которое предотвращает беды, и скопируйте безопасный рабочий процесс для команды.
- Merge соединяет две ветки новым коммитом слияния и сохраняет историю такой, какой она была.
- Rebase воспроизводит ваши коммиты поверх другой ветки, давая прямую линию, но новые хеши.
- Никогда не делайте rebase коммитов, которые другие уже забрали.
- Делайте rebase своей ветки функциональности перед pull request, а при переносе в основную ветку используйте merge.
Ваша ветка функциональности отстаёт от main, а новая работа вам нужна. Git merge vs rebase — это выбор между двумя способами её получить, и оба верны. Они различаются тем, как выглядит история после.
Мы создали крошечный репозиторий, выполнили обе команды и вставили реальный вывод ниже, чтобы вы сравнивали результат, а не воображали его. Только знакомитесь с Git? Начните с нашего учебника по Git для начинающих (на английском).
Что делают git merge и git rebase
git merge берёт вершины двух веток и объединяет их. Если в обеих есть новые коммиты, Git создаёт коммит слияния с двумя родителями. Ничто из существующего не меняется.
git rebase берёт коммиты, которые есть только в вашей ветке, откладывает их, переносит вашу ветку на вершину другой и затем воспроизводит ваши коммиты один за другим. Воспроизведённые коммиты — это новые коммиты с новыми хешами, хотя изменения в них те же.
Git merge на практике
Наша отправная точка: коммит A в main, два коммита (F1, F2) в feature и ещё один коммит (M1), добавленный затем в main. Теперь сливаем main в feature.
git switch feature
git merge main
git log --oneline --graph
* f507aab Merge main into feature
|\
| * 8842fb5 M1
* | 082cc6a F2
* | 5a71e3c F1
|/
* 8766865 A
Граф показывает настоящую историю: две линии работы, которые соединили. Исходные коммиты F1 и F2 сохраняют свои хеши. Цена — лишний коммит слияния, а в оживлённом проекте граф превращается в паутину.
Git rebase на практике
Та же отправная точка, но теперь мы делаем rebase feature на main.
git switch feature
git rebase main
git log --oneline --graph
* 1ff474b F2
* bf9149e F1
* 8842fb5 M1
* 8766865 A
История — одна прямая линия, будто вы начали работу после M1. Посмотрите на хеши: F1 был 5a71e3c, а стал bf9149e; F2 изменился с 082cc6a на 1ff474b. Git переписал их, поэтому rebase требует осторожности.
Merge и rebase рядом
| Аспект | Merge | Rebase |
|---|---|---|
| Форма истории | Ветвящийся граф с коммитами слияния | Одна прямая линия |
| Существующие коммиты | Не меняются | Переписываются с новыми хешами |
| Конфликты | Разрешаются один раз, в коммите слияния | Могут понадобиться для каждого воспроизводимого коммита |
| Безопасно в общих ветках | Да | Нет |
| Лучше всего для | Переноса готовой работы в main | Обновления своей ветки перед ревью |
Совет: если не можете решить, спросите, у кого ещё есть ваша ветка. Если ни у кого — rebase безопасен. Если коллега её забрал — используйте merge.
Золотое правило rebase
Не делайте rebase коммитов, которые существуют за пределами вашего репозитория. После rebase ваша ветка больше не совпадает с копией на сервере, поэтому для отправки нужен принудительный push. Если коллега построил работу на старых коммитах, его история теперь расходится с вашей, и разбираться с этим мучительно.
Делать rebase своей приватной ветки функциональности нормально. Делать его для main или любой ветки, которой пользуются другие, — нет.
Безопасный рабочий процесс для команды
Эта процедура сохраняет историю чистой и никого не удивляет. Так же поступает большинство команд перед CI-конвейером, например таким, как в нашем руководстве по GitHub Actions CI/CD (на английском).
git switch feature
git fetch origin
git rebase origin/main # or: git pull --rebase
# fix conflicts, then: git add <file> && git rebase --continue
git push --force-with-lease # only on your own branch
--force-with-lease отказывается перезаписывать ветку на сервере, если с вашего последнего fetch туда кто-то отправил изменения, — обычный --force молча уничтожил бы их. Когда pull request одобрен, слейте его в main.
Как отменить неудачный rebase
Rebase выглядит разрушительным, но старые коммиты некоторое время остаются в reflog Git, так что можно вернуться.
git reflog # find the commit before the rebase
git reset --hard HEAD@{5} # example position, check reflog first
Создайте резервную ветку командой git branch backup перед любым рискованным rebase, и восстановление сведётся к одной команде. Запускаете базу данных или инструменты в контейнерах? Загляните в наш учебник по Docker для начинающих (на английском).
Итоги
Merge хранит правду, rebase держит её в порядке. Используйте rebase, чтобы обновить свою ветку, merge — чтобы доставить её, и никогда не переписывайте историю, от которой зависят другие.
Часто задаваемые вопросы
В чём разница между git merge и git rebase?
Merge объединяет две ветки коммитом слияния и сохраняет все существующие коммиты. Rebase переносит ваши коммиты поверх другой ветки и переписывает их, давая линейную историю.
Что использовать: merge или rebase?
Делайте rebase своей ветки функциональности, чтобы быть в курсе изменений, и merge — при её доставке в общую ветку вроде main. Используйте merge всегда, когда ветку делят с другими.
Опасен ли git rebase?
В приватных ветках он безопасен, в общих рискован, потому что переписывает хеши коммитов. Используйте резервную ветку и reflog, чтобы восстановиться при проблемах.
Что делает force-with-lease?
Он выполняет принудительный push, только если удалённая ветка всё ещё там, где вы видели её в последний раз, поэтому вы не перезапишете случайно новые коммиты коллеги.
Удаляет ли rebase мои коммиты?
Нет. Он создаёт новые коммиты с теми же изменениями, а старые остаются в reflog, пока Git их не очистит.
Продолжайте изучать (статьи на английском)
Нужна помощь с тестированием, WordPress или SEO?
Мы помогаем командам выпускать более качественное ПО и развивать сайты. Расскажите, над чем вы работаете, и мы предложим понятный следующий шаг.
Больше руководств по тестирование, искусственный интеллект и современные разработки
Понравилась эта статья? Выберите тему и сразу приступайте к нашим новейшим руководствам.
Объяснение галлюцинаций искусственного интеллекта: почему языковые модели все выдумывают и как их уменьшить
Галлюцинации ИИ объяснили: что это такое, почему языковые модели их создают, распространенные типы, как уменьшить их с помощью заземления и подсказо…
Читать статью →DevOpsОбъяснение переменных среды: файлы .env, секреты и правильно выполненная конфигурация
Переменные среды объясняются на примерах Python, которые мы запускали: их чтение и установка, почему каждое значение является строкой, файлы .env, н…
Читать статью →Разработка ПООбъяснение ограничения скорости API: алгоритмы, заголовки и обработка ошибок 429
Ограничение скорости API объясняется с помощью проведенного нами моделирования: сегмент токенов, фиксированное окно, скользящее окно, 429 ответов, з…
Читать статью →Автоматизация тестированияИздевательство в Python: unittest.mock, патч и ошибки, из-за которых тесты лгут
Издевательство на Python с помощью unittest.mock, протестировано: Mock и return_value, Side_effect, исправление использования имени, спецификация и …
Читать статью →Тестирование ПООбъяснение покрытия кода: покрытие линий или ветвей и что на самом деле означает 100%
Покрытие кода объяснено с помощью реального запуска покрытия.py: покрытие строк, ветвей и условий, чтение отчета, почему 100%-ное покрытие все еще м…
Читать статью →Базы данных и SQLDELETE, TRUNCATE и DROP в SQL: различия, откат и когда использовать каждый из них
Объяснение DELETE, TRUNCATE и DROP на примере протестированного SQL: что каждый из них удаляет, откат, триггеры, сброс идентификаторов, скорость и ч…
Читать статью →Базы данных и SQLОбъяснение представлений SQL: создавайте, используйте и избегайте ловушек, с примерами
Представления SQL, объясненные с помощью запросов, которые мы выполняли: создание представления, результаты в реальном времени, скрытие столбцов, пр…
Читать статью →Разработка ПОИнтерфейс TypeScript и тип: реальные различия, протестированные с помощью компилятора
Интерфейс TypeScript против типа, проверенный реальным компилятором: что могут делать оба, что может делать только один, слияние объявлений, расшире…
Читать статью →Разработка ПООбъяснение Python *args и **kwargs: гибкие функции с понятными примерами
Python *args и **kwargs, объясненные с помощью кода, который мы запускали: упаковка и распаковка, порядок параметров, аргументы только по ключевым с…
Читать статью →Разработка ПОЛямбда-функции Python: синтаксис, реальное использование и когда их не следует использовать
Лямбда-функции Python, объясненные с помощью кода, который мы запускали: синтаксис, сортировка с помощью ключа, карты и фильтра, замыкания, ловушка …
Читать статью →Искусственный интеллектЧто такое ИИ-агенты? Как они работают: можно запустить простой цикл агента
Что такое агенты ИИ? Простое руководство по циклу агента, инструментам, памяти и ограничениям, с небольшим циклом Python, который мы запустили, реал…
Читать статью →DevOpsМониторинг и наблюдаемость: реальная разница с журналами, метриками и трассировками
Объяснение мониторинга и наблюдаемости: известные и неизвестные сбои, журналы, метрики и трассировки, пошаговое руководство по инцидентам при оформл…
Читать статью →DevOpsDocker Volumes против Bind Mounts: правильное сохранение данных контейнера
Объяснение томов Docker и монтирования привязки: почему исчезают данные контейнера, именованные тома, монтирование привязки, tmpfs, примеры создания…
Читать статью →Автоматизация тестированияTDD против BDD: ключевые различия, одна функция, созданная для обоих способов, и когда использовать каждый
TDD и BDD объясняются с помощью одной функции скидки, созданной в обоих направлениях: красно-зеленый рефакторинг с кодом, который мы запускали, сцен…
Читать статью →Разработка ПОGit merge vs rebase: отличия, примеры и когда что использовать
Git merge vs rebase с реальным выводом log, сравнительной таблицей, золотым правилом rebase и безопасным рабочим процессом для команды.
Читать статью →Разработка ПОГенераторы списков в Python: синтаксис, примеры и когда их избегать
Генераторы списков в Python: синтаксис, фильтры, if-else, вложенные циклы, измеренное время и случаи, когда обычный цикл for лучше.
Читать статью →Базы данных и SQLSQL JOIN: объяснение с примерами (Inner, Left, Right, Full)
Разберите SQL JOIN шаг за шагом на примерах таблиц и запросов: INNER, LEFT, RIGHT, FULL, CROSS и SELF, а также частые ошибки и советы по производите…
Читать статью →