git rebase
Перенос или переупорядочивание коммитов на новую базу
Описание
git rebase перемещает или «переигрывает» серию коммитов поверх другой базы. В отличие от git merge, который создаёт новый merge-коммит с двумя родителями, rebase переписывает историю, создавая новые коммиты с теми же изменениями, но с новыми хешами и родителями. Результат — чистая линейная история без «замёрзших» веток.
Интерактивный rebase (git rebase -i) — мощный инструмент редактирования истории. Он позволяет: объединять несколько коммитов в один (squash/fixup), переформулировать сообщения коммитов (reword), переставлять коммиты местами, удалять ненужные коммиты (drop), разбивать один коммит на несколько (edit).
Важнейшее правило rebase: никогда не перебазируйте коммиты, которые уже опубликованы в общем репозитории. Rebase переписывает хеши, и другие разработчики, которые работали на основе этих коммитов, столкнутся с серьёзными проблемами при синхронизации.
Синтаксис
Флаги и опции
| Флаг | Описание |
|---|---|
-i, --interactive |
Интерактивный режим: открывает редактор со списком коммитов для редактирования (pick/reword/squash/fixup/drop/edit) |
--onto <ветка> |
Указать новую базу для перебазирования (пересадить коммиты на произвольную точку) |
--continue |
Продолжить rebase после разрешения конфликта и выполнения git add |
--abort |
Отменить rebase и вернуть ветку в исходное состояние |
--skip |
Пропустить текущий проблемный коммит и перейти к следующему |
--quit |
Прервать rebase, оставив рабочий каталог как есть |
-r, --rebase-merges |
Сохранить merge-коммиты при перебазировании (иначе они удаляются) |
--autosquash |
Автоматически переупорядочить коммиты с fixup! и squash! в префиксе сообщения |
--autostash |
Автоматически создать stash перед rebase и применить после |
--no-ff |
Создавать новые коммиты даже если можно применить fast-forward |
-x <команда> |
Выполнять shell-команду после каждого переигранного коммита (напр. для тестов) |
--stat |
Показать диффстат изменений с момента последнего rebase |
Паттерны использования
Обновление feature-ветки от main
# Находясь на feature-ветке git switch feature/payments # Получить изменения main git fetch origin # Перебазировать поверх свежего main git rebase origin/main # Если всё OK — обновить remote (force-push!) git push --force-with-lease origin feature/payments
Интерактивное редактирование коммитов
# Редактировать последние 4 коммита git rebase -i HEAD~4 # В редакторе: # pick a1b2c3 feat: add login form # squash d4e5f6 fix: typo in login # reword g7h8i9 feat: add password reset # drop j0k1l2 WIP: debug output
Автоматическое squash через fixup!
# Создать fixup-коммит для более раннего git commit --fixup=HEAD~2 # При rebase --autosquash fixup! автоматически # переместится и сольётся с нужным коммитом git rebase -i --autosquash main
Перенос коммитов (--onto)
# Ветка server создана от client, # но нужно пересадить её на main git rebase --onto main client server # Удалить коммиты с неправильной базой # (убрать 3 коммита из середины истории) git rebase --onto HEAD~5 HEAD~3
Rebase с автоматическим stash
# Есть незафиксированные изменения, # но нужно перебазироваться git rebase --autostash origin/main # Git сам выполнит: # 1. git stash # 2. rebase # 3. git stash pop
Запуск тестов при каждом коммите
# Проверить, что каждый коммит собирается git rebase -i --exec "npm test" main # При ошибке rebase остановится. # Исправить, затем: git rebase --continue
Советы и подводные камни
--force-with-lease вместо --force: он проверяет, что remote не изменился с момента вашего последнего fetch.
rebase даёт чистую линейную историю — хорошо для feature-веток перед merge в main. merge --no-ff сохраняет точную историю развития — хорошо для основных веток (main, develop). Многие команды используют оба: rebase для обновления feature-ветки, merge для принятия в main.
git push этих коммитов, потребуется force-push. Никогда не делайте force-push в ветки main/master или другие общие ветки — это перепишет историю для всей команды.