[Перевод] Поддержание аккуратной истории в Git с помощью интерактивного rebase

Прим. перев.: эта статья была написана автором 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. В данном случае следует помнить лишь об одной тонкости: строка, помеченная «squash», будет объединена со строкой, которая находится выше нее. Именно поэтому на скриншоте выше я пометил словом squash строку №2 (она будет объединена со строкой №1).

Сохраните изменения и закройте окно редактора. Как и в первом случае, появится новое окно с просьбой ввести сообщение для нового, объединенного коммита:

Вводим сообщение для нового коммитаВводим сообщение для нового коммита

Сохраните сообщение и закройте окно. Будет создан новый коммит, содержащий изменения обоих старых коммитов. Вуаля!

Исправление ошибок

Interactive rebase отлично подходит для исправления ошибок в предыдущих коммитах. При этом не имеет значения, какая именно это ошибка: забыли ли вы внести определенное изменение, должны ли были удалить файл, или просто опечатались…

Обычное решение в подобной ситуации — просто сделать новый коммит, исправляющий ошибку. Но с другой стороны, это внесет дополнительную путаницу в историю: сначала у нас оригинальный коммит, затем мы добавили еще один, исправляющий ошибки… в общем, не слишком «чистый» рабочий подход. Очень скоро в истории коммитов станет нелегко разобраться, поскольку она будет забита всеми этими исправлениями/заплатками.

Именно для таких случаев и предназначен fixup. Этот инструмент берет коммит с быстрым исправлением, применяет его изменения к оригинальном коммиту (тем самым исправляя его), и удаляет корректирующий коммит.

Как работает fixupКак работает fixup

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

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

Следующий шаг — сделать коммит этих изменений в репозиторий, но с небольшой добавкой: делать коммит надо с флагом --fixup, попутно указав хэш «плохого» коммита:

$ git add corrections.txt

$ git commit --fixup 2b504bee

Если теперь посмотреть на историю, вы увидите, что был создан ничем не примечательный коммит (разве вы ожидали чего-то иного?). Но при более внимательном взгляде становятся заметны некоторые особенности: к новому коммиту были автоматически добавлены пометка «fixup!» и описание старого «плохого» коммита:

Оригинальный коммит и корректирующий коммит (fixup)Оригинальный коммит и корректирующий коммит (fixup)

Теперь пора запускать interactive rebase. Опять же, в качестве отправной точки выбираем коммит, предшествующий «плохому»:

$ git rebase -i 0023cddd –autosquash

А вторым ингредиентом нашего секретного соуса выступает флаг --autosquash. Он позволяет не вносить дополнительных правок в открывшемся окне редактора. Внимательно посмотрите на скриншот:

Корректирующий коммит помечен как «fixup» и размещен в правильном порядкеКорректирующий коммит помечен как «fixup» и размещен в правильном порядке

Git автоматически сделал две вещи:

  1. Он пометил новый коммит как fixup.

  2. И переупорядочил строки так, чтобы fixup-коммит оказался непосредственно под «плохим» коммитом. Дело в том, что fixup работает в точности как squash: он объединяет выделенный коммит с коммитом выше.

Таким образом, нам ничего делать не надо. Сохраните изменения и закройте окно редактора.

Давайте еще раз взглянем на историю коммитов:

Счастливый финал!Счастливый финал!

Мало того, что оригинальный коммит теперь содержит правки из вспомогательного, но и некрасивый вспомогательный коммит (с исправлениями) исчез из истории. Все красиво, словно никогда и не было никаких проблем!

Откройте для себя возможности interactive rebase

Существует множество различных вариантов использования интерактивного rebase«а: большинство из них связаны с исправлением ошибок. Подробнее узнать о других способах можно в бесплатном (англоязычном) курсе «First Aid Kit for Git» — коллекции коротких видео (по 2–3 минуты на эпизод).

Примечание оригинального редактора: забавно, но мне пришлось воспользоваться interactive rebase при редактировании этой статьи! Один из коммитов включал изображение, размер которого превышал 1 Мб (что противоречит правилам сайта GitLab). Пришлось вернуться к этому коммиту и включить в него изображение подходящего размера. Спасибо за урок, Вселенная! ?

P.S. от переводчика

Читайте также в нашем блоге:

© Habrahabr.ru