git rebase
В этом документе подробно рассматривается команда git rebase
. Сведения о ней также можно найти на страницах Настройка репозитория и Переписывание истории. На этой странице особое внимание уделяется конфигурации команды git rebase
и ее применению. Кроме того, здесь приведены распространенные примеры использования перебазирования и типичные ошибки.
Перебазирование — это один из двух инструментов Git для внедрения изменений из одной ветки в другую. Такие же возможности предоставляет команда git merge
(слияние). Операция слияния фиксирует изменения, всегда двигаясь вперед по истории проекта, в то время как перебазирование позволяет эффективно ее переписывать. Подробные сведения об операциях слияния и перебазирования см. в руководстве Сравнение слияния и перебазирования. Перебазирование может выполняться в двух режимах: ручном и интерактивном. Эти режимы будут подробно рассмотрены далее.
Что такое git rebase?
Перебазирование — это процесс перемещения последовательности коммитов к новому базовому коммиту или их объединение. Операцию перебазирования удобнее всего применить и отобразить в контексте создания функциональных веток. В общих чертах процесс можно представить следующим образом:
С точки зрения содержимого перебазирование — это замена одного коммита в основании ветки на другой, в результате чего создается впечатление, что ветка получила новое начало. В процессе этой операции Git создает новые коммиты и применяет их к указанному основанию, поэтому важно понимать, что в действительности ветка всегда состоит из совершенно новых коммитов.
Использование
Перебазирование выполняется прежде всего для обеспечения линейной истории проекта. Представим ситуацию: вы работаете над функциональной веткой feature, при этом код в главной ветке main уже изменился с начала вашей работы. Вам нужно отразить последние изменения ветки main в ветке feature, не засоряя при этом историю вашей ветки, чтобы создать впечатление, что ваша работа велась на основе последней версии ветки main. Впоследствии это позволит выполнить беспроблемное слияние ветки feature с веткой main. Почему не следует засорять историю? Ее аккуратность сыграет решающую роль при поиске в Git коммита, в котором появилась проблема. Можно привести более реалистичный пример.
- В ветке main обнаруживается баг, который нарушает работу одной из функций.
- Разработчик изучает историю ветки main с помощью команды
git log
, при этом логичный порядок элементов в истории позволяет ему быстро разобраться в ситуации. - Время появления бага не удается определить с помощью команды
git log
, поэтому разработчик выполняет командуgit bisect
. - История Git имеет логичный порядок, поэтому команда
git bisect
успешно выдает ряд коммитов — их нужно сравнить между собой, чтобы найти проблему. Разработчик быстро находит проблемный коммит и может применить необходимые исправления.
Узнайте больше о командах git log и git bisect на соответствующих страницах.
Внедрить функцию в главную ветку main можно двумя способами: прямым слиянием или перебазированием с последующим слиянием. Первая операция выполняет трехстороннее слияние и создает коммит слияния, а вторая обеспечивает ускоренное слияние и абсолютно линейную историю. На приведенной ниже схеме показано, как перебазирование на ветку main обеспечивает ускоренное слияние.
Перебазирование часто используют для внедрения восходящих изменений в локальный репозиторий. При запросе таких изменений с помощью слияния у вас будет создаваться ненужный коммит слияния всякий раз, когда вы заходите просмотреть изменения в проекте. С другой стороны, перебазирование обеспечит такие условия, когда ваши изменения основываются на результатах работы коллег.
Не выполняйте перебазирование публичной истории
Ранее на странице Переписывание истории мы уже обозначили, что ни при каких обстоятельствах не следует выполнять перебазирование коммитов, отправленных в публичный репозиторий. Команда rebase заменит старые коммиты на новые, и другим разработчикам покажется, будто часть истории проекта просто исчезла.
Сравнение стандартного и интерактивного режимов команды git rebase
Чтобы выполнить перебазирование в интерактивном режиме, к команде git rebase нужно добавить аргумент -i
(от interactive — «интерактивный»). При выполнении команды без аргументов она запустится в стандартном режиме. Для демонстрации обоих режимов представим, что мы создали отдельную функциональную ветку.
# Create a feature branch based off of main
git checkout -b feature_branch main
# Edit files
git commit -a -m "Adds new feature"
В стандартном режиме команда git rebase автоматически берет коммиты из текущей рабочей ветки и применяет их в конце переданной ветки.
git rebase <base>
This automatically rebases the current branch onto <base>
, which can be any kind of commit reference (for example an ID, a branch name, a tag, or a relative reference to HEAD
).
Если запустить команду git rebase
с флагом -i
, перебазирование будет выполняться в интерактивном режиме. Этот режим исключит необходимость перемещения коммитов вслепую на новое основание и позволит изменять отдельные коммиты при выполнении операции. Так вы можете очистить историю путем удаления, разделения и изменения коммитов в существующей последовательности. Получится что-то вроде команды git commit --amend
на стероидах!
git rebase --interactive <base>
This rebases the current branch onto <base>
but uses an interactive rebasing session. This opens an editor where you can enter commands (described below) for each commit to be rebased. These commands determine how individual commits will be transferred to the new base. You can also reorder the commit listing to change the order of the commits themselves. Once you've specified commands for each commit in the rebase, Git will begin playing back commits applying the rebase commands. The rebasing edit commands are as follows:
pick 2231360 some old commit
pick ee2adc2 Adds new feature
# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
Дополнительные команды перебазирования
Как описано на странице Переписывание истории, с помощью перебазирования можно изменять предыдущие коммиты, группы коммитов, зафиксированные с помощью коммитов файлы и группы сообщений. Существуют и более сложные примеры использования, которые требуют добавления к команде git rebase
различных опций.
git rebase -d
— во время операции коммит будет исключен из окончательного блока объединенных коммитов.git rebase -p
— коммит останется в исходном виде. Операция не затронет сообщение и содержимое коммита. При этом сам коммит сохранится в истории веток отдельно.git rebase -x
— для каждого отмеченного коммита будет выполнен скрипт командной строки. Эта опция может быть полезной при тестировании базы кода на отдельных коммитах, поскольку с ее помощью можно выявить ошибки в ходе перебазирования.
Обзор
Интерактивное перебазирование позволяет полностью контролировать состояние истории проекта. Это дает разработчикам большую свободу, поскольку они могут зафиксировать засоренную историю, не отрываясь от написания кода, и очистить ее позже.
Большинство разработчиков используют интерактивное перебазирование, чтобы придать функциональной ветке аккуратность перед слиянием с основной базой кода. Они могут склеить незначительные коммиты, удалить устаревшие элементы и в целом навести порядок в ветке, прежде чем выполнить перенос в «официальную» историю проекта. Со стороны будет казаться, что для разработки функции потребовалось лишь несколько коммитов и тщательное планирование.
Оценить эффективность интерактивного перебазирования можно, взглянув на получившуюся историю ветки main. В глазах окружающих вы будете блестящим разработчиком, который внедрил новую функцию с первого раза и без лишних коммитов. Так интерактивное перебазирование помогает поддерживать порядок в истории проекта, а также сохраняет целесообразность каждого ее элемента.
Варианты конфигурации
С помощью команды git config
можно задать ряд опций перебазирования. Так вы определяете, какие элементы будут выводиться на экран при выполнении команды git rebase
.
rebase.stat
принимает логические значения (по умолчанию false). Эта опция отвечает за наглядное отображение статистики по различиям, с помощью которой можно увидеть изменения с момента последнего перебазирования.
rebase.autoSquash
принимает логические значения и отвечает за поведение опции--autosquash
.
rebase.missingCommitsCheck
принимает несколько значений, которые определяют поведение операции перебазирования относительно отсутствующих коммитов.
warn | Выводит в интерактивном режиме предупреждения о том, что коммиты были удалены. |
| Останавливает перебазирование и выводит предупреждения о том, что коммиты были удалены. |
| Установлено по умолчанию. Игнорирует все предупреждения об отсутствии коммитов. |
rebase.instructionFormat
— форматная строкаgit log
, включающая опции форматирования вывода команды rebase при ее выполнении в интерактивном режиме.
Расширенные возможности перебазирования
Команде git rebase
можно передать аргумент командной строки --onto
. При использовании аргумента --onto
команда git rebase примет следующий вид:
git rebase --onto <newbase> <oldbase>
Опция --onto
расширяет возможности перебазирования: теперь команде rebase можно передавать конкретные ссылки в качестве оснований для перебазирования.
Рассмотрим тестовый репозиторий с такими ветками:
o---o---o---o---o main
\
o---o---o---o---o featureA
\
o---o---o featureB
Ветка featureB основана на featureA. При этом мы понимаем, что ветка featureB не зависит ни от одного изменения в ветке featureA, поэтому она могла бы отходить от главной ветки main.
git rebase --onto main featureA featureB
featureA is the <oldbase>
. main
becomes the <newbase>
and featureB is reference for what HEAD
of the <newbase>
will point to. The results are then:
o---o---o featureB
/
o---o---o---o---o main
\
o---o---o---o---o featureA
Опасности перебазирования
При работе с командой git rebase важно помнить о нескольких трудностях. Одна из них заключается в конфликтах слияния, которые проявляются чаще, если ветка существует достаточно долго и имеет значительные отличия от главной. К тому времени, когда вы захотите перебазировать такую ветку на главную, в последней может возникнуть множество новых коммитов, которые будут конфликтовать с изменениями вашей ветки. Чтобы избежать этого, необходимо регулярно выполнять перебазирование ветки на главную и чаще делать коммиты. Аргументы командной строки --continue
и --abort
определяют поведение git rebase
при возникновении конфликтов и служат соответственно для продолжения и прерывания операции.
Более серьезная проблема перебазирования заключается в том, что при перезаписи истории в интерактивном режиме некоторые коммиты могут быть утрачены. Выполнение перебазирования в интерактивном режиме вместе с такими подкомандами, как squash или drop, приведет к удалению коммитов из локальной истории вашей ветки. Сначала покажется, что коммиты удалены навсегда, но их можно восстановить с помощью команды git reflog
. При этом операция перебазирования будет полностью отменена. Подробные сведения о том, как команда git reflog
позволяет найти утраченные коммиты, см. в документации по команде git reflog.
Сама по себе команда git rebase не сопряжена с серьезной опасностью. Риск возникает, если вы используете интерактивное перебазирование для перезаписи истории и затем принудительно отправляете результаты в удаленную ветку, где работают другие пользователи. Этого делать не стоит, поскольку работа удаленных пользователей может быть перезаписана при осуществлении pull.
Восстановление при перебазировании восходящей ветки
Если другой пользователь выполнил перебазирование и принудительно отправил изменения в ветку, над которой вы работаете, при выполнении команды git pull
отправленные коммиты перезапишут все коммиты, от которых отходила эта ветка. К счастью, команда git reflog
позволяет получить журнал ссылок удаленной ветки. Вы можете найти в нем ссылку, которая предшествовала перебазированию, затем перебазировать свою ветку с помощью этой ссылки и опции --onto
, которая была рассмотрена в разделе «Расширенные возможности перебазирования».
Резюме
В этой статье рассматривалось использование команды git rebase
. Мы обсудили базовые и продвинутые случаи использования, а также изучили более сложные примеры. Ниже перечислены основные моменты:
- Сравнение стандартного и интерактивного режимов команды git rebase.
- Опции конфигурации команды git rebase.
- git rebase --onto
- Потеря коммитов при выполнении команды git rebase.
Мы рассмотрели применение команды git rebase
с другими инструментами, например git reflog
, git fetch
и git push
. Подробнее о них см. на соответствующих страницах.