Команда git rebase
имеет репутацию магического фокуса, от которого новичкам следует держаться подальше, но на самом деле она может значительно облегчить жизнь команде разработчиков, если использовать ее с осторожностью. В этой статье мы сравним git rebase
с родственной командой git merge
и определим все потенциальные возможности для включения git rebase
в типичный рабочий процесс с Git.
Друзья, поддержите нас вступлением в наш телеграм канал QaRocks. Там много туториалов, задач по автоматизации и книг по QA.
Обзор концепции
Первое, что нужно понять о команде git rebase
это то, что она решает ту же проблему, что и git merge
. Обе эти команды предназначены для интеграции изменений из одной ветки в другую – просто они делают это совершенно разными способами.
Подумайте, что происходит, когда вы начинаете работать над новой функцией в отдельной ветке, а другой участник команды добавляет новые коммиты в главную ветку main
. В результате возникает история форков (forks), которая должна быть знакома всем, кто использовал Git в качестве инструмента для совместной работы.

Теперь предположим, что новые коммиты в ветке main
имеют отношение к функции, над которой вы работаете. Чтобы включить новые коммиты в свою функциональную ветку feature
, у вас есть два варианта: слияние (merge
) или ребазинг (rebase
).
Опция слияния (merge)
Самый простой вариант – объединить ветку main
с веткой feature
с помощью следующей команды:
git checkout feature git merge main
При желании этот код можно записать в одну строку:
git merge feature main
Это создаст в ветке feature
новый “коммит слияния”, который свяжет воедино истории обеих веток, что даст вам новую структуру ветки, которая выглядит следующим образом:

Слияние удобно тем, что при нем существующие ветки никак не изменяются. Это позволяет избежать всех потенциальных проблем, связанных с ребазингом (о них речь пойдет ниже).
С другой стороны, это означает, что каждый раз, когда вам будет необходимо включить вышестоящие изменения, в функциональную ветку feature
будет попадать внешний коммит слияния. Если работа в главной ветке main
ведется активно, история вашей функциональной ветки быстро засорится. Хотя эту проблему можно устранить, используя продвинутые варианты команды git log
, другим разработчикам будет тяжело разобраться в истории проекта.
Опция ребазинга (rebase)
В качестве альтернативы слиянию вы можете “перебазировать” ветку feature
на ветку main
, используя следующие команды:
git checkout feature git rebase main
Это перемещает всю ветку feature
поверх ветки main
, включая в себя все новые коммиты в main
. Однако, в отличие от команды слияния, ребазинг переписывает историю проекта, создавая совершенно новые коммиты для каждого коммита в исходной ветке.

Основное преимущество ребазинга заключается в том, что вы получаете гораздо более чистую историю проекта. Во-первых, это устраняет ненужные коммиты слияния, получаемые при выполнении git merge
. Во-вторых, как видно на диаграмме выше, ребазинг также приводит к идеально линейной истории проекта – вы сможете отследить функционал до самого начала проекта без каких-либо форков. Это облегчает навигацию по проекту с помощью таких команд, как git log
, git bisect
и gitk
.
Но у такого способа есть два недостатка: безопасность и отслеживаемость. Если вы не следуете золотому правилу ребазинга, то переписывание истории проекта может стать катастрофой для вашего рабочего процесса. Кроме того, при ребазинге теряется контекст, обеспечиваемый коммитом слияния – вы не сможете увидеть, когда изменения, внесенные выше, были включены в функциональность.
Интерактивный ребазинг
Интерактивный ребазинг дает вам возможность изменять коммиты по мере их перемещения в новую ветку. Этот вариант предоставляет еще больше возможностей, чем автоматическое выполнение rebase, поскольку дает полный контроль над историей коммитов ветки. Обычно он используется для очистки истории перед слиянием функциональной ветки с главной веткой main
.
Чтобы начать интерактивную сессию ребазинга, передайте параметр i
в команду git rebase
:
git checkout feature git rebase -i main
Это откроет текстовый редактор со списком всех коммитов, которые будут перемещены:
pick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
Этот список определяет, как именно будет выглядеть ветка после выполнения ребазинга. Изменив команду pick
и/или порядок коммитов, вы можете сделать историю ветки такой, какой захотите. Например, если второй коммит исправляет небольшую проблему в первом коммите, их можно объединить в один коммит с помощью команды fixup
:
pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
Когда вы сохраните и закроете файл, Git выполнит rebase
в соответствии с вашими инструкциями, в результате чего история проекта будет выглядеть следующим образом:

Удаление таких незначительных коммитов помогает быстрее разобраться в истории функциональной ветки. Это то, что git merge
просто не может сделать.
Золотое правило ребазинга
Как только вы поймете, что такое ребазинг, самое важное, что нужно усвоить, – это когда не стоит его делать. Золотое правило команды git rebase
заключается в том, чтобы никогда не использовать её в публичных ветках.
Например, подумайте, что произойдет, если выполнить ребазинг главной ветки main
на вашу функциональную ветку feature
:

Ребазинг перемещает все коммиты из ветки main
в конец ветки feature
. Проблема в том, что это происходит только в вашем репозитории. Все остальные разработчики по-прежнему работают с исходной веткой main
. Поскольку ребазинг приводит к появлению совершенно новых коммитов, Git будет считать, что история вашей ветки main
разошлась со всеми остальными.
Единственный способ синхронизировать две ветки main
– это слить их обратно вместе, в результате чего появится дополнительный коммит слияния и два набора коммитов, которые содержат одни и те же изменения (исходные изменения и изменения из вашей ветки после выполнения команды rebase
). Нет нужды говорить, что это очень запутанная ситуация.
Поэтому, прежде чем выполнить git rebase
, всегда спрашивайте себя: “А кто-нибудь еще просматривает эту ветку?”. Если ответ положительный, уберите руки с клавиатуры и подумайте об ином способе внесения изменений (например, о команде git revert
).
Принудительный пуш (Force-pushing)
Если вы попытаетесь поместить переделанную ветку main
обратно в удаленный репозиторий, Git не даст вам этого сделать, поскольку она конфликтует с удаленной веткой main
. Но вы можете принудительно выполнить команду push
, передав параметр --force
следующим образом:
# Be very careful with this command! git push --force
Это действие перезапишет удалённую ветку main
, чтобы она соответствовала переделанной ветке из вашего репозитория, что, в свою очередь, может внести путаницу в работу остальных членов вашей команды. Поэтому будьте очень осторожны и используйте эту команду только тогда, когда вы точно знаете, что делаете.
Одна из немногих ситуаций, требующих выполнения принудительного пуша, — это локальная очистка после помещения частной функциональной ветки в удаленный репозиторий (например, для создания резервной копии). Это все равно что сказать: “Упс, я не очень-то хотел отправлять исходную версию этой функциональной ветки. Возьмите вместо нее текущую”. Опять же, важно, чтобы никто не работал с коммитами из исходной версии функциональной ветки.
Описание рабочего процесса
В этом разделе мы рассмотрим преимущества, которые может дать ребазинг на различных этапах разработки функций продукта.
Первым шагом в любом рабочем процессе, использующем git rebase
является создание отдельной ветки для каждой функции. Это даст вам необходимую структуру веток для безопасного использования команды rebase
:

Локальная очистка
Один из лучших способов включить ребазинг в рабочий процесс – это очистка локальных функциональных веток, в которых еще ведется работа. Периодически выполняя интерактивный ребазинг, вы можете убедиться, что ни один коммит в вашей ветке не потеряет смысла. Вы сможете быть уверены, что ваш код не распадется на изолированные коммиты. Если это случится, ситуацию всегда можно будет исправить.
Когда вы вызываете git rebase
, у вас есть два варианта для нового положения ветки: родительская ветка для функциональной ветки (например, main
) или более ранний коммит в функциональной ветке. Пример первого варианта мы рассмотрели в разделе Интерактивный ребазинг. Последний вариант удобен, когда вам нужно исправить только несколько последних коммитов. Например, описанная ниже команда запускает интерактивный ребазинг только последних 3 коммитов:
git checkout feature git rebase -i HEAD~3
Указав HEAD~3
в качестве нового положения, вы фактически не перемещаете ветку, а лишь интерактивно переписываете 3 коммита, которые следуют за ней. Обратите внимание, что это не включит вышестоящие изменения в ветку feature
.

Если вы хотите переписать всю функциональную ветку этим методом, команда git merge-base
поможет вам найти начальное положение этой ветки. Следующая команда возвращает возвращает ID коммита начального положения, который затем можно передать в команду git rebase
:
git merge-base feature main
Такое использование интерактивного ребазинга – отличный способ внедрить git rebase
в ваш рабочий процесс, поскольку он затрагивает только локальные ветки. Единственное, что увидят другие разработчики, – это ваш конечный продукт, который должен представлять собой чистую, легко читаемую историю ветки feature
.
Но, опять же, это работает только для частных функциональных веток. Если вы сотрудничаете с другими разработчиками через одну и ту же ветку, эта ветка является публичной, и вам не разрешается переписывать ее историю.
Варианта очистки локальных коммитов с интерактивным использованием rebase с помощью команды git merge
не существует.
Внесение изменений из репозитория другого разработчика
В разделе Обзор концепции мы рассмотрели, как включить вышестоящие изменения из ветки main
в функциональную ветку с помощью команд git merge
или git rebase
. Слияние – это безопасный вариант, который сохраняет всю историю вашего репозитория, в то время как rebase
создает линейную историю, перемещая вашу функциональную ветку в конец ветки main
.
Это использование git rebase
похоже на локальную очистку, но при ее выполнении включаются вышестоящие изменения из главной ветки main
.
Помните, что вы можете делать ребазинг в удаленную ветку, отличную от ветки main
. Это может произойти, когда вы работаете над одной и той же функцией с другим разработчиком и вам нужно включить его изменения в свой репозиторий.
Например, если вы и другой разработчик по имени Джон добавили коммиты в ветку feature
, то после получения удаленной ветки feature
из репозитория Джона ваш репозиторий может выглядеть следующим образом:

Вы можете решить эту проблему с форком точно так же, как при интеграции вышестоящих изменений из ветки main
: либо выполнить слияние вашей локальной ветки feature
с john/feature
, либо сделать ребазинг вашей локальной ветки feature
в конец ветки john/feature
.

Обратите внимание, что этот ребазинг не нарушает Золотое правило ребазинга, поскольку перемещаются только коммиты вашей ветки feature
, тогда как предшествующие элементы остаются нетронутыми. Это все равно что сказать: “Добавьте мои изменения к тому, что уже сделал Джон”. В большинстве случаев это более интуитивно понятно, чем синхронизация с удаленной веткой с помощью коммита слияния.
По умолчанию команда git pull
выполняет слияние. Однако если передать ей параметр --rebase
, будет выполнен ребазинг удаленной ветки.
Пул-реквест (pull request)
Если вы используете пул-реквесты как часть процесса проверки кода, вам следует избегать использования git rebase
после создания пул-реквеста. Как только вы создадите pull request
, другие разработчики смогут видеть ваши коммиты, то есть ваша ветка станет публичной. В случае перезаписи ее истории Git и ваши коллеги не смогут отслеживать последующие коммиты в функциональную ветку.
Любые изменения от других разработчиков должны быть включены с помощью команды git merge
, а не git rebase
.
По этой причине, как правило, рекомендуется очистить код с помощью интерактивного ребазинга до подачи запроса на pull request
.
Интеграция утвержденной функции
После того как функция была одобрена вашей командой, у вас есть возможность перенести её на конец ветки main
, прежде чем использовать git merge
для интеграции функции в основную кодовую базу.
Эта ситуация похожа на включение вышестоящих изменений в функциональную ветку, но поскольку вам не разрешено переписывать коммиты в ветке main
, вам придется в конечном итоге использовать git merge
для интеграции фич. Однако, выполнив rebase
перед слиянием, вы обеспечите ускоренное слияние и идеальную линейную историю проекта. Это также даст вам возможность совместить любые последующие коммиты, добавленные до закрытия запроса pull.

Если вам не совсем удобно работать с git rebase
, вы всегда можете выполнить rebase
во временную ветку. Таким образом, если вы случайно испортите историю своей функциональной ветки, вы всегда сможете переключиться в исходную ветку и попробовать снова. Например:
git checkout feature git checkout -b temporary-branch git rebase -i main # [Clean up the history] git checkout main git merge temporary-branch
Резюме
И это всё, что вам нужно знать, чтобы приступить к ребазингу ваших веток. Если вы предпочитаете чистую, линейную историю, свободную от ненужных коммитов слияния, вам следует использовать git rebase
вместо git merge
при интеграции изменений из другой ветки.
С другой стороны, если вы хотите сохранить полную историю своего проекта и избежать перезаписи публичных коммитов, воспользуйтесь командой git merge
. Любой из этих вариантов вполне приемлем, но теперь у вас, по крайней мере, есть возможность использовать преимущества git rebase
.
Перевод статьи «Merging vs. rebasing».