Прим. перев.: эта статья была написана автором Git-клиента Tower, Tobias Günther, и опубликована в блоге 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
строку №2 (она будет объединена со строкой №1).
Сохраните изменения и закройте окно редактора. Как и в первом случае, появится новое окно с просьбой ввести сообщение для нового, объединенного коммита:
Сохраните сообщение и закройте окно. Будет создан новый коммит, содержащий изменения обоих старых коммитов. Вуаля!
Исправление ошибок
Interactive rebase отлично подходит для исправления ошибок в предыдущих коммитах. При этом не имеет значения, какая именно это ошибка: забыли ли вы внести определенное изменение, должны ли были удалить файл, или просто опечатались…
Обычное решение в подобной ситуации — просто сделать новый коммит, исправляющий ошибку. Но с другой стороны, это внесет дополнительную путаницу в историю: сначала у нас оригинальный коммит, затем мы добавили еще один, исправляющий ошибки… в общем, не слишком «чистый» рабочий подход. Очень скоро в истории коммитов станет нелегко разобраться, поскольку она будет забита всеми этими исправлениями/заплатками.
Именно для таких случаев и предназначен fixup
. Этот инструмент берет коммит с быстрым исправлением, применяет его изменения к оригинальном коммиту (тем самым исправляя его), и удаляет корректирующий коммит.
После этого все будет выглядеть так, словно с оригинальным коммитом не было никаких проблем! Итак, давайте изучим весь процесс на практическом примере.
Прежде всего необходимо исправить проблему: добавить новый файл, внести изменения в существующий, удалить устаревшие файлы. Другими словами, внести изменения, исправляющие ошибку.
Следующий шаг — сделать коммит этих изменений в репозиторий, но с небольшой добавкой: делать коммит надо с флагом --fixup
, попутно указав хэш «плохого» коммита:
$ git add corrections.txt
$ git commit --fixup 2b504bee
Если теперь посмотреть на историю, вы увидите, что был создан ничем не примечательный коммит (разве вы ожидали чего-то иного?). Но при более внимательном взгляде становятся заметны некоторые особенности: к новому коммиту были автоматически добавлены пометка «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. от переводчика
Читайте также в нашем блоге:
«Git happens! 6 типичных ошибок Git и как их исправить»;
«Мой любимый Git-коммит»;
«Пожалуйста, перестаньте рекомендовать Git Flow».