Прим. перев.: эта статья была написана автором Git-клиента Tower, Tobias Gnther, и опубликована в блоге GitLab. В ней просто и наглядно рассказывается об основных возможностях интерактивного rebase'а, что может стать отличным введением для тех, кто только начинает им пользоваться.
Interactive rebase один из самых универсальных инструментов Git'а. В этой статье мы поговорим о том, как с его помощью корректировать сообщения при коммитах, исправлять ошибки, и о многом другом.
Интерактивный rebase иногда называют швейцарским армейским ножом Gitа, поскольку он объединяет в себе так много различных инструментов для совершенно разных сценариев применения. При этом главным вариантом использования, без сомнения, является очистка локальной истории коммитов.
Обратите внимание на слово локальной: rebase следует использовать только для очистки локальной истории коммитов (например, перед включением одной из ваших локальных feature-веток в общую ветку команды). И наоборот, этот мощный инструмент НЕ следует использовать для исправления коммитов в ветке, которая уже загружена и открыта для совместной работы в удаленном репозитории. Интерактивный rebase инструмент для переписывания истории Git, и его не следует использовать для редактирования коммитов, которые уже открыты для других.
Теперь, когда все необходимые предупреждения сделаны, давайте перейдем к практике.
Примечание: для визуализации сценариев и последовательностей операций для некоторых скриншотов я использовал GUI к Git под названием Tower.
Редактирование сообщения в старом коммите
Иногда вы замечаете опечатку в старом коммите или вспоминаете, что забыли упомянуть нечто важное в его описании. Если бы речь шла о самом последнем коммите, можно было бы воспользоваться опцией --amend команды git commit. Но в случае более старых коммитов придется воспользоваться интерактивным rebaseом.
Вот пример описания к коммиту, которое мы будем исправлять:
Плохое сообщение к коммиту, которое мы будем исправлятьПервый шаг при использовании interactive rebase определить, какой частью истории мы обираемся манипулировать. В примере выше для того, чтобы изменить плохое коммит-сообщение, мы должны начать с его родительского коммита.
Начинаем с родительского коммитаТеперь нужно скормить хэш родительского коммита команде:
$ git rebase -i 0023cddd
Откроется окно редактора со списком коммитов для изменения. Не удивляйтесь тому, что они приведены в обратном порядке: в рамках интерактивного rebaseа Git будет повторно применять прошлые коммиты один за другим. Другими словами, с точки зрения Git коммиты выстроены в правильном порядке.
Окно редактора со списком выбранных коммитовОбратите внимание: в этом окне менять ничего не нужно!
Или, говоря о нашем конкретном примере: не пытайтесь здесь поменять
сообщение к коммиту ca9aacb
! В этом окне вы только
помечаете нужный коммит ключевым словом для соответствующего
действия. Поскольку нужно перефразировать описание коммита, мы
вводим reword
. Сохраните изменения и закройте окно
редактора. После этого появится новое окно, содержащее сообщение к
старому коммиту. Теперь можно вносить изменения:
Сохраните изменения и закройте окно. Поздравляю сессия интерактивного rebaseа завершена, сообщение к коммиту успешно отредактировано!
Объединение нескольких коммитов в один
Rebase также можно использовать для объединения нескольких старых коммитов в один. При этом, конечно, актуальным остается золотое правило систем управления версиями: в большинстве случаев лучше создавать множество мелких коммитов, нежели несколько крупных. Однако, как и во всем остальном, мы можем внезапно обнаружить, что несколько перестарались со следованием этому правилу, и решить, что было бы хорошо объединить несколько старых коммитов в один.
Давайте предположим, что нужно объединить следующие выбранные коммиты в один:
Объединяем несколько коммитов в одинКак и в первом случае, процесс начинается с запуска сессии интерактивного rebaseа на коммите-предшественнике тех, что мы хотим изменить.
$ git rebase -i 2b504bee
Снова откроется окно редактора с историей коммитов, которые мы хотим объединить:
Помечаем нужные строки кодовым словом squashДействию, которое мы собираемся произвести над коммитами,
соответствует кодовое слово squash
. В данном случае
следует помнить лишь об одной тонкости: строка, помеченная
squash, будет объединена со строкой, которая находится выше
нее. Именно поэтому на скриншоте выше я пометил словом
squash
строку 2 (она будет объединена со строкой
1).
Сохраните изменения и закройте окно редактора. Как и в первом случае, появится новое окно с просьбой ввести сообщение для нового, объединенного коммита:
Вводим сообщение для нового коммитаСохраните сообщение и закройте окно. Будет создан новый коммит, содержащий изменения обоих старых коммитов. Вуаля!
Исправление ошибок
Interactive rebase отлично подходит для исправления ошибок в предыдущих коммитах. При этом не имеет значения, какая именно это ошибка: забыли ли вы внести определенное изменение, должны ли были удалить файл, или просто опечатались
Обычное решение в подобной ситуации просто сделать новый коммит, исправляющий ошибку. Но с другой стороны, это внесет дополнительную путаницу в историю: сначала у нас оригинальный коммит, затем мы добавили еще один, исправляющий ошибки в общем, не слишком чистый рабочий подход. Очень скоро в истории коммитов станет нелегко разобраться, поскольку она будет забита всеми этими исправлениями/заплатками.
Именно для таких случаев и предназначен fixup
. Этот
инструмент берет коммит с быстрым исправлением, применяет его
изменения к оригинальном коммиту (тем самым исправляя его), и
удаляет корректирующий коммит.
После этого все будет выглядеть так, словно с оригинальным коммитом не было никаких проблем! Итак, давайте изучим весь процесс на практическом примере.
Прежде всего необходимо исправить проблему: добавить новый файл, внести изменения в существующий, удалить устаревшие файлы. Другими словами, внести изменения, исправляющие ошибку.
Следующий шаг сделать коммит этих изменений в репозиторий, но с
небольшой добавкой: делать коммит надо с флагом
--fixup
, попутно указав хэш плохого коммита:
$ git add corrections.txt
$ git commit --fixup 2b504bee
Если теперь посмотреть на историю, вы увидите, что был создан ничем не примечательный коммит (разве вы ожидали чего-то иного?). Но при более внимательном взгляде становятся заметны некоторые особенности: к новому коммиту были автоматически добавлены пометка fixup! и описание старого плохого коммита:
Оригинальный коммит и корректирующий коммит (fixup)Теперь пора запускать interactive rebase. Опять же, в качестве отправной точки выбираем коммит, предшествующий плохому:
$ git rebase -i 0023cddd autosquash
А вторым ингредиентом нашего секретного соуса выступает флаг
--autosquash
. Он позволяет не вносить
дополнительных правок в открывшемся окне редактора.
Внимательно посмотрите на скриншот:
Git автоматически сделал две вещи:
-
Он пометил новый коммит как
fixup
. -
И переупорядочил строки так, чтобы fixup-коммит оказался непосредственно под плохим коммитом. Дело в том, что
fixup
работает в точности какsquash
: он объединяет выделенный коммит с коммитом выше.
Таким образом, нам ничего делать не надо. Сохраните изменения и закройте окно редактора.
Давайте еще раз взглянем на историю коммитов:
Счастливый финал!Мало того, что оригинальный коммит теперь содержит правки из вспомогательного, но и некрасивый вспомогательный коммит (с исправлениями) исчез из истории. Все красиво, словно никогда и не было никаких проблем!
Откройте для себя возможности interactive rebase
Существует множество различных вариантов использования интерактивного rebaseа: большинство из них связаны с исправлением ошибок. Подробнее узнать о других способах можно в бесплатном (англоязычном) курсе First Aid Kit for Git коллекции коротких видео (по 2-3 минуты на эпизод).
Примечание оригинального редактора: забавно, но мне пришлось воспользоваться interactive rebase при редактировании этой статьи! Один из коммитов включал изображение, размер которого превышал 1 Мб (что противоречит правилам сайта GitLab). Пришлось вернуться к этому коммиту и включить в него изображение подходящего размера. Спасибо за урок, Вселенная! ?
P.S. от переводчика
Читайте также в нашем блоге: