Истории успеха наших клиентов — лучшие проекты
Вход/ Регистрация

Git rebase: что делает и преимущества команды

2570
11 минут чтения
Средний рейтинг статьи: 2.6

В системе контроля версий Git есть два способа объединения одной ветки с другой, выраженные в виде разных команд:

  • git merge. Коммиты (изменения) из одной ветки переносятся в другую путем создания коммита слияния.

  • git rebase. Коммиты (изменения) из одной ветки переносятся из одной ветки в другую путем сохранения оригинального порядка изменений.

Говоря проще, при git merge коммиты одной из веток «схлопываются» в один, а при git rebase — остаются нетронутыми. При этом ветки объединяются.

Таким образом команда git rebase позволяет объединить коммиты обеих веток через образование общей истории изменений.

Image1

Разница между git merge и git rebase

В этом руководстве будет рассмотрена команда git rebase, отвечающая за перебазирование коммитов (изменений) из одной ветки в другую. 

Все показанные примеры использовали систему контроля версий Git версии 2.34.1, которая запускалась на облачном сервере Timeweb Cloud под управлением операционной системы Ubuntu 22.04.

В блоге Timeweb Cloud есть отдельные публикации, подробно рассказывающие об установке Git на популярные операционные системы:

Теория: git rebase

Понять устройство ребазирования в Git лучше всего на примере абстрактного репозитория, состоящего из нескольких веток. При этом операцию ребазирования необходимо рассматривать поэтапно.

Создание веток

Предположим мы создали репозиторий с единственной веткой master, в которую сделали только один коммит. Ветка master приобрела следующий вид:

  • master

    • commit_1

После этого на основе master мы создали новую ветку hypothesis, внутри который решили протестировать некоторые фичи. Внутри новой ветки мы сделали несколько коммитов, улучшающих кодовую. Ветка приобрела такой вид:

  • hypothesis

    • commit_4

    • commit_3

    • commit_2

    • commit_1

А уже потом мы добавили еще один коммит в ветку master, исправив некоторую уязвимость в срочном порядке. Таким образом, ветка master стала выглядеть так:

  • master

    • commit_5

    • commit_1

Теперь наш репозиторий имеет структуру из двух веток:

  • master

    • commit_5

    • commit_1

  • hypothesis

    • commit_4

    • commit_3

    • commit_2

    • commit_1

Ветка master является основной, а hypothesis — второстепенной (производной). Поздние коммиты указываются выше ранних, подобно тому, как их выводит команда git log.

VDS и VPS

Гибкие виртуальные серверы с почасовым
биллингом по всему миру: Россия, Азия и Европа.

Объединение веток

Предположим, мы хотим продолжить работу над улучшением фичи, под которую ранее выделили отдельную ветку hypothesis. Однако эта ветка не содержит жизненно важное исправление уязвимости, которое мы сделали в ветке master.

Поэтому нам хотелось бы «синхронизировать» состояние ветки hypothesis с веткой master так, чтобы коммит исправления оказался в ветке с фичей. То есть мы хотим получить примерно такую структуру репозитория:

  • master

    • commit_5

    • commit_1

  • hypothesis

    • commit_4

    • commit_3

    • commit_2

    • commit_5

    • commit_1

Как видно, ветка hypothesis будет точно повторять историю изменений ветки master, несмотря на то, что изначально она была создана до коммита commit_5. Иными словами, ветка hypothesis будет содержать историю обеих веток — и свою, и master.

Чтобы получить такой результат, необходимо выполнить ребазирование с помощью команды git rebase.

Впоследствии изменения, сделанные в hypothesis, можно будет объединить с веткой master с помощью классической команды git merge, создающей коммит слияния.

Тогда структура репозитория станет такой:

  • master

    • commit_merge

    • commit_5

    • commit_1

  • hypothesis

    • commit_4

    • commit_3

    • commit_2

    • commit_5

    • commit_1

Более того, выполнение git merge после команды git rebase может снизить вероятность возникновения конфликтов.

Практика: git rebase

Разобравшись с теоретическим аспектом команды git rebase, можно перейти к ее тестированию в реальном репозитории некого импровизированного проекта. При этом структура репозитория будет повторять показанный ранее теоретический пример.

Создание репозитория

Для начала создадим отдельную директорию, в которой будет размещаться репозиторий:

    

После чего перейдем в нее:

    

Теперь можно инициализировать репозиторий:

    

В консольном терминале должно вывестись стандартное информационное сообщение:

    

А в текущей директории появится скрытый каталог .git, который можно увидеть с помощью соответствующей команды:

    

Флаг -a означает all и позволяет просматривать файловую систему в расширенном режиме. Ее содержимое будет таким:

    

Перед тем, как начать делать коммиты, необходимо указать базовые сведения о пользователе.

Сперва имя:

    

А потом и почту:

    

Наполнение ветки master 

С помощью простых текстовых файлов мы будем имитировать добавление различных функций в проект. Каждая новая функция будет оформляться в отдельный коммит.

Создадим файл импровизированной функции:

    

И наполним его содержимом:

    

Теперь проиндексируем изменения, сделанные в репозитории:

    

На всякий случай можно проверить статус индексации:

    

В консольном терминале должно появится сообщение со списком проиндексированных изменений:

    

Теперь можно выполнить коммит:

    

После этого в консольном терминале выведется сообщение об успешном внесении изменений в ветку master:

    

Наполнение ветки hypothesis 

Теперь необходимо создать новую ветку hypothesis:

    

Флаг -b необходим, чтобы сразу переключиться на созданную ветку.

В консольном терминале появится сообщение об успешном переключении на новую ветку:

    

Теперь необходимо последовательно выполнить три коммита с тремя файлами по аналогии с веткой master:

  • commit_2 с файлом function_2 и содержимым Функция 2

  • commit_3 с файлом function_3 и содержимым Функция 3

  • commit_4 с файлом function_4 и содержимым Функция 4

Если после этого просмотреть список всех коммитов:

    

То в консольном терминале появится такая последовательность:

    

В этой команде флаг --oneline необходим для вывода информации о коммитах в сжатом формате одной строки.

Последнее, что нужно сделать, — добавить еще один коммит в основную ветку master. Переключимся на нее:

    

В консольном терминале должно появится соответствующее сообщение:

    

После этого создадим еще один файл импровизированной функции:

    

Содержимое будет следующим:

    

Теперь можно проиндексировать изменения:

    

И выполнить очередной коммит:

    

Если проверить текущий список коммитов:

    

То в ветке master их будет всего два:

    

Объединение веток с git rebase

Для выполнения перебазирования сперва необходимо перейти в ветку hypothesis:

    

И выполнить ребазирование:

    

После этого в консольном терминале появится сообщение об успешном ребазировании:

    

Теперь можно проверить список коммитов:

    

В консольном терминале появится список, содержащий коммиты обоих веток в оригинальном порядке:

    

Теперь ветка hypothesis содержит общую историю всего репозитория.

Разрешение конфликтов

Как и в случае с git merge, при использовании команды git rebase могут возникать конфликты, требующие ручного разрешения.

Давайте модифицируем наш репозиторий таким образом, чтобы искусственно создать конфликт ребазирования.

Создадим в ветке hypothesis еще один файл:

    

И запишем в него следующий текст:

    

Проиндексируем изменения:

    

И выполним очередной коммит:

    

Теперь переключимся на ветку master:

    

Создадим аналогичный файл:

    

И наполним его следующим содержимым:

    

Аналогично, выполняем индексацию:

    

И делаем коммит:

    

Заново откроем созданный файл:

    

И изменим его содержимое на следующий текст:

    

Опять проиндексируется:

    

И снова сделаем коммит:

    

Теперь можно обратно переключиться на ветку hypothesis:

    

А далее выполнить еще одно ребазирование:

    

В консольном терминале появится сообщение о конфликте:

    

Git предлагает отредактировать файл conflict, проиндексировать изменения с помощью команды git add, после чего продолжить ребазирование, указав флаг --continue.

Именно так мы и поступим:

    

Файл будет содержать две конфликтующие версии файла, обрамленные специальными символами:

    

Наша задача убрать всё лишнее, наполнив файл итоговым вариантом произвольного текста:

    

Теперь индексируем изменения:

    

И продолжаем процесс ребазирования:

    

После этого в консольном терминале откроется текстовый редактор, предлагающий изменение оригинальное название того коммита, в котором возник конфликт:

    

В консольном терминале появится сообщение об успешном завершении процесса ребазирования:

    

Теперь если проверить список коммитов в ветке hypothesis:

    

Можно увидеть оригинальную последовательность всех сделанных изменений:

    

Обратите внимание, что коммиты conflict_2 и conflict_3, сделанные в ветке master, располагаются в истории изменений раньше, чем коммит conflict_1. Впрочем, это касается любых коммитов, сделанных в ветке master.

Ребазирование удаленного репозитория

Помимо работы с локальными ветками, ребазирование можно выполнить в момент подтягивания изменений из удаленного репозитория. Для этого к стандартной команде pull необходимо добавить флаг --rebase:

    

Здесь:

  • remote. Удаленный репозиторий.

  • branch. Удаленная ветка.

По сути, такая конфигурация команды pull является эквивалентом git rebase за исключением того, что применяемые к текущей ветки изменения (коммиты) берутся из удаленного репозитория.

Преимущества git rebase

  • Линейность

Команда git rebase позволяет сформировать достаточно линейную историю целевой ветки, представляющую собой последовательно сделанные коммиты.

Такая последовательность и отсутствие ветвления делает историю проще для восприятия и понимания.

  • Снижение количества конфликтов

Предварительно выполненная команда git rebase может существенно снизить вероятность возникновения конфликтов при объединении веток с помощью git merge.

Конфликты проще разрешать в последовательно идущих коммитах, нежели в коммитах, сливающихся в один единый коммит слияния. Это особенно актуально при отправке веток в удаленные репозитории.

Недостатки git rebase

  • Изменение истории

В отличие от слияния, ребазирование частично переписывает историю целевой ветки. При этом лишние элементы истории удаляются.

  • Вероятность ошибок

Свойство существенно перестраивать историю коммитов может приводить к необратимым ошибкам внутри репозитория. А это значит, что некоторые данные могут быть безвозвратно утеряны.

Надежные VDS для ваших проектов

Cloud MSK 15

477 ₽/мес

Процессор
1 x 3.3 ГГц
Память
1 ГБ
NVMe
15 ГБ
Канал
1 Гбит/с
Публичный IP
Cloud MSK 30

657 ₽/мес

Процессор
1 x 3.3 ГГц
Память
2 ГБ
NVMe
30 ГБ
Канал
1 Гбит/с
Публичный IP

Заключение

Объединение двух веток методом ребазирования, который реализуется командой git rebase, существенно отличается от классического слияния, выполняемого командой git merge.

  • git merge превращает коммиты одной ветки ветки в один коммит другой.

  • git rebase перемещает коммиты из одной ветки в конец другой, сохраняя оригинальный порядок.

При этом аналогичного поведения ребазирования можно добиться во время использования команды git pull, используя дополнительный флаг --rebase.

С одной стороны, команда git rebase позволяет добиться более чистой и наглядной истории коммитов в основной ветке, что увеличивает простоту поддержки репозитория. С другой стороны, команды git rebase снижает уровень детализации изменений внутри ветки, упрощая историю и удаляя некоторые ее записи.

По этой причине ребазирование — функция для более опытных пользователей, которые понимают механизм работы Git.

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

2570
11 минут чтения
Средний рейтинг статьи: 2.6
Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server