git rebase и золотое правило истории
Мощный, но острый инструмент: учимся выпрямлять историю rebase и не выстрелить себе в ногу.
git rebase переносит коммиты вашей ветки так, будто вы ответвились от более позднего коммита, делая историю линейной.
Что делает rebase
Представьте: вы ответвили feature от main, поработали, а тем временем в main появились новые коммиты. Есть два пути снова свести их вместе. merge создаст merge-коммит, объединяющий две линии. rebase поступит иначе: он «отцепит» ваши коммиты и заново применит их поверх свежей вершины main, как будто вы начали работу только что.
git switch feature
git rebase mainРезультат — прямая, линейная история без merge-коммитов, словно работа шла последовательно.
merge или rebase
| merge | rebase | |
| История | сохраняется как есть, с ветвлениями | переписывается в прямую линию |
| Merge-коммит | создаётся | нет |
| Хеши коммитов | не меняются | меняются |
Главное техническое следствие: rebase создаёт новые коммиты с новыми хешами (хоть содержимое и то же). По сути это уже другие коммиты — старые «переписаны».
Золотое правило rebase
Никогда не делайте rebase коммитов, которые уже опубликованы и которыми пользуются другие.
Почему это критично? Rebase меняет хеши. Если коллеги уже основали работу на ваших старых коммитах, а вы их «переписали» и запушили, истории разойдутся, и у всех начнётся хаос с дублями и конфликтами. Поэтому rebase безопасен только для локальной, ещё не запушенной работы.
Чистите свою ветку rebase до открытия PR — это нормально. После того как другие начали с ней работать — только merge.
Интерактивный rebase
Очень полезный режим — rebase -i. Он позволяет переписать набор последних коммитов: объединить (squash), переименовать (reword), переставить или удалить.
git rebase -i HEAD~3Откроется список последних трёх коммитов, где напротив каждого можно указать действие. Так перед отправкой PR из десятка хаотичных коммитов вроде «фикс», «опять фикс», «точно фикс» делают пару чистых осмысленных.
Если rebase пошёл не так
При конфликте rebase останавливается. Разрешите конфликт, добавьте файл и продолжите; либо прервите и вернитесь как было:
git rebase --continue # после разрешения конфликта
git rebase --abort # отменить весь rebaseforce-with-lease — безопасный force
После rebase своей личной ветки её хеши изменились, и обычный push git отклонит. Здесь нужен force-push, но даже его лучше делать в безопасном варианте:
git push --force-with-leaseВ отличие от грубого --force, флаг --force-with-lease сначала проверяет, что на сервере не появилось новых чужих коммитов, и только тогда перезаписывает. Если кто-то успел запушить, push будет отклонён — и вы не затрёте чужую работу. Это разумный компромисс для своих веток под PR.
Так merge или rebase?
Простое практическое правило для новичка: внутри своей ещё не опубликованной ветки можно смело наводить порядок через rebase; для вливания в общие ветки и для всего опубликованного — используйте merge. Тогда вы получаете чистую локальную историю и при этом не ломаете жизнь команде.
Итог
- rebase переносит коммиты поверх другой ветки, делая историю линейной.
- rebase меняет хеши — это новые коммиты, старые «переписаны».
- Золотое правило: не делайте rebase опубликованных коммитов; для своей локальной ветки — можно.