git rebase <base> берёт коммиты текущей ветки от общего предка с
<base> до HEAD и пересаживает их поверх <base>. У каждого
коммита меняется родитель → меняется SHA.
Базовый сценарий
до: main: A → B → E
\
feat: C → D
git switch feat
git rebase main
после: main: A → B → E
\
feat: C' → D' ← новые SHA
Логика правок в C' и D' та же, что в C и D. Но это другие объекты - для Git «новые коммиты».
Когда применять
- Перед PR, чтобы feature-ветка стояла на свежем main, без merge-коммитов.
- Очистить локальную историю через
rebase -i(см. interactive-rebase). - Переместить ветку на другой базис через
--onto.
Главное правило
Не rebase'ить публичные ветки. Если ветку кто-то склонил, у него останутся старые SHA. После твоего force-push возникает каша. Безопасно - только на личной feature до или сразу после ревью.
Конфликты
Если на каком-то коммите возникает конфликт - Git останавливается, показывает файлы с маркерами, ждёт разрешения:
# править файлы, убирать маркеры
git add <files>
git rebase --continue # продолжить со следующего коммита
# или отказаться от текущего коммита:
git rebase --skip
# или отменить всё:
git rebase --abort
Конфликт может возникать на каждом коммите подряд (если каждый трогает спорные строки). После rebase каждый промежуточный результат уже разрешён.
После rebase
Если ветка была запушена - обычный push откажет, потому что SHA поменялись:
git push --force-with-lease
--force-with-lease проверяет, что remote не успел уйти вперёд
(никто другой не запушил). Лучше всегда --force-with-lease, не
--force.
Подводные камни
- Rebase копирует автор-даты коммитов как есть, но обновляет
committer-даты. После rebase
git log --pretty=fullerможет показывать «странные» расхождения. - На большом количестве коммитов rebase может занять заметное время и потребовать разрешения конфликтов на каждом шаге.
git pull --rebaseобъединяет fetch с rebase'ом твоих локальных коммитов поверх удалённых. Удобно как дефолт:git config --global pull.rebase true.