git rebase: что это и когда использовать вместо merge
git rebase — один из самых мощных и при этом наиболее неправильно понимаемых инструментов Git. Он позволяет переписывать историю коммитов, делать её линейной и чистой. Но за эту чистоту приходится платить осторожностью: неправильное использование rebase может испортить историю репозитория для всей команды.
Что такое rebase и как он работает #
Rebase буквально означает «сменить базу». Когда вы делаете git rebase master, Git берёт все коммиты из вашей текущей ветки и «пересаживает» их на вершину другой ветки.
Представьте ситуацию: вы создали ветку feature от коммита C2 в master. Пока вы работали, в master появились коммиты C3 и C4.
До rebase история выглядит так:
master: C1 - C2 - C3 - C4
\
feature: F1 - F2
После git rebase master из ветки feature:
master: C1 - C2 - C3 - C4
\
feature: F1' - F2'
Коммиты F1 и F2 пересозданы как F1' и F2' — они содержат те же изменения, но имеют другие SHA-хеши, потому что их «родитель» изменился с C2 на C4.
Это принципиально отличает rebase от merge: merge создаёт новый коммит слияния и сохраняет исходную историю, rebase переписывает историю, делая её линейной.
Rebase vs Merge: различия #
# Merge: создаёт коммит слияния, история нелинейная
git checkout feature
git merge master
# Результат: F1 - F2 - M (merge commit)
# /
# C1-C2-C3-C4
# Rebase: переписывает коммиты, история линейная
git checkout feature
git rebase master
# Результат: C1-C2-C3-C4-F1'-F2'
Когда использовать merge:
- Публичные ветки, которые видят другие разработчики
- Нужно сохранить точную историю слияния
- Хотите зафиксировать момент, когда ветки объединились
Когда использовать rebase:
- Локальные ветки до их публикации
- Хотите «подтянуть» вашу ветку к актуальному состоянию main
- Готовите чистую историю перед pull request
- Хотите squash нескольких коммитов в один
Базовый rebase #
# Переключиться на feature ветку
git checkout feature
# или
git switch feature
# Выполнить rebase на master
git rebase master
# Аналогично, но переключение не нужно
git rebase master feature
# Посмотреть результат
git log --oneline
После успешного rebase, чтобы слить feature в master без лишнего merge-коммита:
git checkout master
git merge feature
# Это будет fast-forward merge — просто перемотка HEAD
Interactive rebase: полный контроль над историей #
Интерактивный rebase — самый мощный инструмент для управления историей коммитов. Он позволяет изменить порядок, объединить, разделить или удалить коммиты.
# Интерактивный rebase последних 3 коммитов
git rebase -i HEAD~3
# Интерактивный rebase всей ветки на master
git rebase -i master
# Интерактивный rebase начиная с конкретного коммита
git rebase -i a1b2c3d
Git откроет редактор с таким содержимым:
pick a1b2c3d Добавить авторизацию
pick b2c3d4e Исправить ошибку в авторизации
pick c3d4e5f Добавить тесты
# Rebase d5e6f7g..c3d4e5f onto d5e6f7g (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
Команды interactive rebase:
pick— использовать коммит как естьreword— использовать коммит, но изменить сообщениеsquash— объединить с предыдущим коммитом, объединить сообщенияfixup— объединить с предыдущим коммитом, отбросить сообщениеdrop— удалить коммит из историиedit— остановиться для изменения коммита
Squash коммитов в один #
Частый сценарий: вы сделали несколько коммитов в процессе работы («WIP», «fix typo», «ещё одно исправление»), и хотите объединить их в один перед merge.
# Запустить интерактивный rebase для последних 3 коммитов
git rebase -i HEAD~3
В редакторе измените:
pick a1b2c3d Добавить авторизацию
squash b2c3d4e Исправить ошибку в авторизации
squash c3d4e5f Добавить тесты
Git объединит все три в один коммит и предложит отредактировать итоговое сообщение.
Если хотите просто объединить без редактирования сообщений — используйте fixup вместо squash:
pick a1b2c3d Добавить авторизацию
fixup b2c3d4e WIP
fixup c3d4e5f fix
Разрешение конфликтов при rebase #
В отличие от merge, при rebase конфликты могут возникать несколько раз — для каждого переносимого коммита. Это объясняется тем, что каждый коммит применяется последовательно.
# При конфликте Git останавливается и сообщает:
# CONFLICT (content): Merge conflict in src/auth.js
# error: could not apply a1b2c3d... Добавить авторизацию
# Resolve all conflicts manually...
# Проверить статус
git status
# Разрешить конфликты в файлах (открыть и отредактировать)
# Убрать маркеры <<<<<<, =======, >>>>>>>
# Добавить исправленные файлы
git add src/auth.js
# Продолжить rebase
git rebase --continue
# Если конфликтов несколько — Git остановится снова для следующего коммита
# Повторить процесс
# Если хотите отменить rebase и вернуться к исходному состоянию
git rebase --abort
Rebase с опцией –onto #
--onto позволяет «пересадить» ветку на другую базу, минуя промежуточные ветки:
# Синтаксис: git rebase --onto <newbase> <upstream> <branch>
# Перенести ветку feature на main, минуя develop
git rebase --onto main develop feature
# Пример: была ветка server, от неё client
# Хотим перенести client прямо на master
git rebase --onto master server client
Практические примеры #
# Обновить feature ветку до актуального состояния main
git fetch origin
git rebase origin/main
# Squash последних 5 коммитов
git rebase -i HEAD~5
# Исправить сообщение коммита (не последнего)
git rebase -i HEAD~3
# Заменить 'pick' на 'reword' для нужного коммита
# Удалить коммит из истории
git rebase -i HEAD~5
# Заменить 'pick' на 'drop' для нужного коммита
# Отменить неудачный rebase через reflog
git reflog
git reset --hard ORIG_HEAD
# Автоматически применить нашу версию при конфликтах (осторожно!)
git rebase -X ours master
# Применить их версию при конфликтах
git rebase -X theirs master
# Показать историю после rebase
git log --oneline --graph
Когда использовать rebase #
Rebase отлично подходит для следующих ситуаций:
Локальные ветки до публикации. Пока ваша ветка только у вас — свободно используйте rebase для чистоты истории.
Подтягивание ветки к актуальному состоянию. Вместо git pull (который делает merge) можно настроить git pull --rebase или использовать git fetch && git rebase origin/main.
Очистка истории перед pull request. Объедините «WIP» коммиты в осмысленные, исправьте опечатки в сообщениях.
Поддержание линейной истории в проекте. Некоторые команды требуют линейной истории как часть политики.
Когда избегать rebase #
Главное правило: никогда не делайте rebase опубликованных (публичных) веток.
Если ваши коммиты уже в удалённом репозитории, и другие разработчики их видели или работают на их основе — rebase создаст проблемы. После rebase SHA-хеши коммитов изменятся. Для других разработчиков это выглядит как полностью разная история, и при следующем pull/merge возникнут конфликты и дублированные коммиты.
# Опасно: rebase общей ветки
git checkout develop
git rebase main # НЕ делайте этого, если develop — общая ветка
# Безопасно: rebase своей feature ветки
git checkout feature/my-task
git rebase main # Нормально, если только вы работаете в feature/my-task
Настройка pull с rebase #
Вместо стандартного git pull (который делает merge) можно использовать rebase:
# Pull с rebase для текущего раза
git pull --rebase
# Настроить rebase по умолчанию для pull
git config --global pull.rebase true
# Или для конкретного репозитория
git config pull.rebase true
Часто задаваемые вопросы #
Какая разница между merge и rebase? Merge создаёт новый коммит слияния и сохраняет историю обеих веток. Rebase переносит коммиты на новую базу, создавая линейную историю. Merge безопаснее для публичных веток, rebase даёт чистую историю для локальной работы.
Когда я должен использовать rebase вместо merge? Используйте rebase для локальных веток перед публикацией, для «подтягивания» вашей ветки к актуальному состоянию main, для объединения мелких коммитов. Избегайте rebase на опубликованных ветках.
Что произойдёт, если я сделаю rebase опубликованной ветки? Git переписывает SHA-хеши коммитов. Другие разработчики, у которых есть старые версии этих коммитов, получат конфликты при следующем pull. История раздвоится, коммиты продублируются.
Как отменить неудачный rebase? Если rebase ещё не завершён — git rebase --abort. Если уже завершён — git reflog для поиска исходного коммита, затем git reset --hard ORIG_HEAD или git reset --hard <hash>.
Что такое interactive rebase и зачем он нужен? git rebase -i открывает редактор с возможностью изменить историю: объединить коммиты (squash), удалить (drop), переупорядочить, изменить сообщения (reword). Незаменимо для подготовки чистой истории перед pull request.
Можно ли использовать rebase вместо git pull? Да, git pull --rebase вместо обычного pull избежит лишних merge-коммитов при обновлении локальной ветки. Можно настроить по умолчанию через git config --global pull.rebase true.
Заключение #
git rebase — инструмент для тех, кто хочет поддерживать чистую и читаемую историю коммитов. Ключевое правило: rebase локальных неопубликованных веток — хорошо; rebase общих публичных веток — опасно. Interactive rebase (git rebase -i) особенно полезен для подготовки pull request: можно объединить черновые коммиты в один осмысленный.
Подробнее о слиянии веток — полное руководство по git merge. Для понимания истории изменений — git log и git reflog.