git rebase -i (интерактивный)
Научитесь использовать git rebase -i для объединения, переименования, изменения порядка и удаления коммитов перед публикацией.
Что такое интерактивный rebase
Интерактивный rebase, запускаемый командой git rebase -i, позволяет переписать серию коммитов до того, как вы ими поделитесь. Он открывает редактируемый «список задач» коммитов, где вы построчно решаете, что произойдёт с каждым из них — оставить как есть, изменить сообщение, объединить с другим, разбить, изменить порядок или удалить. Это расширение обычного git rebase, дающее вам контроль над каждым шагом.
На этой странице рассматривается, как запустить интерактивный rebase, все команды, доступные в списке задач, самые распространённые сценарии (объединение, переименование, изменение порядка, удаление и разбиение коммитов), как восстановиться при ошибке, а также единственное правило безопасного использования.
Коммит — это сохранённый снимок вашей работы; HEAD — указатель на текущий коммит. «Переписывание истории» означает создание новых коммитов взамен существующих — Git никогда не изменяет коммит на месте, поэтому каждый переписанный коммит получает совершенно новый хеш.
Запуск интерактивного rebase
Укажите команде коммит перед первым, который вы хотите изменить — rebase воспроизведёт всё, что идёт после него. Чтобы пересмотреть последние три коммита:
git rebase -i HEAD~3HEAD~3 означает «три коммита назад от HEAD». Можно также указать конкретный коммит (git rebase -i a1b2c3d) или, находясь в ветке, выполнить rebase всего, что добавлено после её отделения от другой ветки (git rebase -i main).
Git откроет настроенный редактор с одной строкой на каждый коммит, старые сверху (в отличие от git log):
pick a1b2c3d add login form
pick b2c3d4e fix typo
pick c3d4e5f more css tweaksНиже списка Git добавляет закомментированную шпаргалку по всем командам. Вы меняете слово в начале каждой строки, выбирая действие, при необходимости переставляете строки, затем сохраняете и закрываете файл. Git воспроизводит коммиты сверху вниз в соответствии с вашими инструкциями. Если сохранить файл без изменений (или удалить все строки), rebase ничего не сделает и остановится.
Команды списка задач
Каждая строка начинается с команды. Вот наиболее часто используемые:
| Команда | Сокращение | Действие |
|---|---|---|
pick | p | Использовать коммит как есть. |
reword | r | Использовать коммит, но изменить его сообщение. |
edit | e | Приостановить выполнение на коммите, чтобы изменить его содержимое (amend, разбить или добавить файлы). |
squash | s | Объединить с предыдущим коммитом, сливая оба сообщения. |
fixup | f | Как squash, но отбросить сообщение этого коммита. |
drop | d | Полностью удалить коммит (удаление строки даёт тот же результат). |
exec | x | Выполнить команду оболочки (например, тесты) в этой точке rebase. |
Команды reorder не существует — изменение порядка коммитов выполняется простым перемещением строк вверх или вниз в списке задач. Строки применяются сверху вниз, поэтому оставленный порядок — это порядок, в котором Git создаёт коммиты.
Объединение коммитов
Распространённый вариант очистки истории — сворачивание нескольких мелких коммитов в один. Пометьте первый как pick, а остальные как squash (или fixup, чтобы отбросить их сообщения):
pick a1b2c3d add login form
squash b2c3d4e fix typo
fixup c3d4e5f more css tweakssquash/fixup всегда объединяют с вышестоящей строкой. При сохранении Git объединяет три коммита в один. Так как b2c3d4e был объединён через squash, Git откроет второй редактор с обоими сообщениями коммитов, чтобы вы могли написать одно чистое сообщение; fixup для c3d4e5f добавляет его изменения, но без сообщения.
Если вам нужно только объединить небольшие «случайные» коммиты с более ранним, обратите внимание на git commit --fixup (см. git commit --amend и git rebase -i --autosquash), который автоматически расставляет метки за вас.
Изменение сообщения коммита
Чтобы исправить опечатку или уточнить сообщение, не трогая код, пометьте строку как reword:
pick a1b2c3d add login form
reword b2c3d4e fix tpyo
pick c3d4e5f more css tweaksGit воспроизводит a1b2c3d как есть, затем приостанавливается и открывает редактор со старым сообщением b2c3d4e, чтобы вы могли его переписать, после чего продолжает. Изменения коммита остаются нетронутыми — меняются только сообщение (и его хеш).
Изменение порядка и удаление коммитов
Чтобы изменить порядок, переместите строки. Чтобы удалить коммит, пометьте его drop или просто удалите строку. Этот список задач переставляет правку CSS перед исправлением опечатки и полностью удаляет коммит с опечаткой:
pick a1b2c3d add login form
pick c3d4e5f more css tweaks
drop b2c3d4e fix typoУдаление или изменение порядка может вызвать конфликты, если более поздний коммит зависел от удалённого или перемещённого — Git приостановится и позволит вам их разрешить.
Разбиение коммита
Чтобы разбить один большой коммит на несколько, пометьте его edit. Git приостановится на этом коммите, уже применив его; затем вы откатываете коммит, сохраняя изменения, и фиксируете их частями:
# rebase pauses on the commit marked 'edit'
git reset HEAD~ # move the commit's changes back to the working tree
git add login.js
git commit -m "add login form markup"
git add styles.css
git commit -m "style the login form"
git rebase --continue # resume the rest of the rebasegit reset HEAD~ отменяет последний коммит, оставляя его изменения в файлах (см. git reset), так что вы можете зафиксировать их отдельными, более мелкими коммитами.
Завершение или отмена
Если на каком-то шаге возникает конфликт, Git приостанавливается, чтобы вы могли его разрешить. Отредактируйте конфликтующие файлы, добавьте исправления через git add, затем продолжите:
git rebase --continueЕсли вы решите, что определённый коммит не нужно применять во время паузы на конфликте, используйте git rebase --skip. Чтобы полностью отменить rebase и вернуться к исходному состоянию:
git rebase --abortДаже после завершения rebase оригинальные коммиты не теряются немедленно — они остаются доступными через git reflog некоторое время, что является страховочной сетью на случай, если вы сделали rebase и пожалели об этом.
Предупреждение
Интерактивный rebase переписывает историю — итоговые коммиты получают новые хеши. Это отлично подходит для приведения в порядок вашей собственной ветки перед созданием pull request, но никогда не делайте rebase коммитов, которые уже получили другие. Переписывание общей истории заставляет всех остальных согласовывать расходящиеся копии, а для публикации результата требуется принудительный push (git push --force-with-lease). Золотое правило: rebase локально, merge публично.
Если вам нужно изменить только самый последний коммит, обычно не требуется полный rebase — git commit --amend проще. Чтобы скопировать один коммит из другого места в вашу ветку, см. git cherry-pick.
Полный пример с объединением коммитов
Ниже приведена полная последовательность команд, которую можно запустить в пустой папке, чтобы увидеть объединение в действии. Создаются четыре коммита, затем последние три сворачиваются в один:
git init -b main demo && cd demo
git config user.email [email protected]
git config user.name You
echo "init" > base && git add base && git commit -m "initial commit"
echo "a" > f && git add f && git commit -m "add login form"
echo -e "a\nb" > f && git add f && git commit -m "fix typo"
echo -e "a\nb\nc" > f && git add f && git commit -m "more css tweaks"
git log --oneline # four commits
git rebase -i HEAD~3 # mark line 2 'squash', line 3 'fixup', save
git log --oneline # now: "initial commit" + one combined commitПосле rebase команда git log --oneline показывает всего два коммита — начальный коммит и один объединённый — а файл f по-прежнему содержит все три строки (a, b, c). Работа идентична; история коммитов стала просто аккуратнее.