git rebase и золотое правило истории

Мощный, но острый инструмент: учимся выпрямлять историю rebase и не выстрелить себе в ногу.

git rebase переносит коммиты вашей ветки так, будто вы ответвились от более позднего коммита, делая историю линейной.

Что делает rebase

Представьте: вы ответвили feature от main, поработали, а тем временем в main появились новые коммиты. Есть два пути снова свести их вместе. merge создаст merge-коммит, объединяющий две линии. rebase поступит иначе: он «отцепит» ваши коммиты и заново применит их поверх свежей вершины main, как будто вы начали работу только что.

git switch feature
git rebase main

Результат — прямая, линейная история без merge-коммитов, словно работа шла последовательно.

merge или rebase

mergerebase
Историясохраняется как есть, с ветвлениямипереписывается в прямую линию
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      # отменить весь rebase

force-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 опубликованных коммитов; для своей локальной ветки — можно.
Проверьте себя
1. Что делает git rebase main, находясь на ветке feature?
AСливает feature в main с merge-коммитом
BЗаново применяет коммиты feature поверх вершины main, делая историю линейной
CУдаляет ветку main
DОтправляет feature на сервер
2. Почему нельзя делать rebase уже опубликованных коммитов?
Arebase работает слишком медленно
Brebase меняет хеши коммитов, и история разойдётся у коллег
CGitHub это запрещает технически
DПосле rebase нельзя пушить
3. Для чего удобен git rebase -i (интерактивный rebase)?
AЧтобы отправить коммиты на сервер
BЧтобы объединить, переименовать или переставить последние коммиты перед отправкой
CЧтобы создать новую ветку
DЧтобы разрешить конфликт автоматически
Поддержать проект