Олег Марков
Перебазирование в Git - команда git rebase от основ до практики
Введение
Перебазирование (rebase) в Git — это способ переписать историю коммитов, перенести их на другую базовую точку и сделать историю проекта более ровной и понятной. Когда вы работаете в команде, часто возникает ситуация, когда ваша ветка отстала от основной. В такие моменты перед вами встает выбор — сделать слияние (merge) или перебазирование (rebase).
С помощью git rebase вы можете:
- выстроить линейную историю без лишних merge-коммитов;
- обновить свою ветку до актуального состояния основной ветки;
- отредактировать, объединить или удалить коммиты;
- сделать историю более «чистой» перед тем как отправить работу в общий репозиторий.
Давайте разбираться, как это работает на практике, когда имеет смысл использовать git rebase, а когда лучше отказаться от него.
Что такое перебазирование в Git
Идея перебазирования простыми словами
Смотрите, я объясню на простой аналогии. Представьте, что у вас есть:
- основная ветка
main, в которой постоянно появляются новые коммиты; - ваша рабочая ветка
feature, которую вы создали отmainнесколько дней назад.
За это время:
- в
mainдобавили новые коммиты A, B, C; - вы в
featureсделали свои коммиты X, Y.
История теперь выглядит так (логически):
main:M1 — M2 — M3 — A — B — Cfeature: ответвилась отM3и имеетX — Y.
Перебазирование берет ваши коммиты X и Y и как бы «переклеивает» их в конец актуальной ветки main — после C. В результате:
- история
mainне меняется; - ветка
featureтеперь выглядит так:A — B — C — X' — Y'(X' и Y' — новые коммиты с тем же содержимым, но с другими идентификаторами).
Важно понимать: git rebase создаёт новые коммиты на новой базе, а не двигает старые. С точки зрения Git это новые объекты, даже если изменения в файлах те же.
Rebase vs merge — в чем разница
Теперь давайте посмотрим на отличие от слияния git merge.
Если вы сделаете:
git checkout feature
git merge main
Git создаст дополнительный merge-коммит, который объединяет историю обеих веток:
X --- Y
/ \
M1 - M2 - M3 - A - B - C --- M
Где M — merge-коммит. История становится ветвистой, но прозрачно показывает, что ветки реально сливались.
Если вы сделаете:
git checkout feature
git rebase main
То история будет линейной:
M1 - M2 - M3 - A - B - C - X' - Y'
Разница в том, что:
- merge сохраняет реальную историю: как развивались ветки, когда они сливались;
- rebase переписывает историю, делая вид, что вы изначально работали поверх свежей версии
main.
Когда использовать rebase, а когда merge
Обратите внимание на практическое правило:
Используйте rebase:
- на своих локальных ветках, до того как вы отправили их в общий репозиторий;
- чтобы обновить рабочую ветку от актуальной
mainбез лишних merge-коммитов; - чтобы «почистить» историю коммитов перед публикацией: объединить мелкие коммиты, поправить сообщения.
Используйте merge:
- для слияния уже опубликованных веток;
- для завершения больших фич в
main/develop, чтобы явно зафиксировать факт слияния; - когда важно сохранить точную, нефальсифицированную историю.
Коротко: рефакторинг истории — rebase, фиксация событий — merge.
Базовый rebase на другую ветку
Обновление фичи от main через rebase
Самый частый сценарий — вы хотите подтянуть изменения из main в свою ветку feature, но без merge-коммита.
Шаги:
# 1. Переключаемся в свою ветку
git checkout feature
# 2. Перебазируемся на актуальную main
git rebase main
Что произойдет:
- Git «откатит» ваши локальные коммиты (временно уберет их с головы ветки).
- Применит все новые коммиты из
mainповерх базы вашей ветки. - Поверх обновленной базы заново применит ваши изменения по одному коммиту.
Если конфликтов нет, ветка feature будет выглядеть так, как будто вы изначально начали работу от последней версии main.
Перебазирование на удаленную ветку
Давайте разберем пример, когда вы работаете с удаленным репозиторием origin:
# Обновляем локальную информацию об удаленных ветках
git fetch origin
# Переключаемся в свою ветку
git checkout feature
# Перебазируемся на актуальную удаленную main
git rebase origin/main
Здесь:
git fetchпросто подтягивает данные, не меняя ваша локальная веткуmain;- вы прямо указываете базу для перебазирования —
origin/main, то есть удаленную версию основной ветки; - после этого ваша локальная
featureбудет идти поверх удаленнойmain.
Интерактивное перебазирование: git rebase -i
Один из самых мощных режимов перебазирования — интерактивный (git rebase -i). Смотрите, я покажу вам, как это работает. Этот режим позволяет:
- изменить порядок коммитов;
- объединить несколько коммитов в один (squash/fixup);
- отредактировать сообщение коммита;
- исправить содержимое коммита;
- удалить коммиты.
Запуск интерактивного rebase
Обычно вы запускаете его так:
# Перебазировать последние 5 коммитов текущей ветки
git rebase -i HEAD~5
Здесь HEAD~5 означает «коммит, который на 5 шагов раньше текущего». То есть вы говорите: «Покажи мне список последних 5 коммитов и дай возможность с ними поработать».
После запуска откроется текстовый редактор (vim, nano или тот, который настроен) со списком коммитов вида:
pick a1b2c3d Первый коммит
pick e4f5g6h Исправление форматирования
pick 1122334 Добавление новой функции
pick 5566778 Фикс опечатки
pick 99aabbc Логирование для отладки
Каждая строка — это коммит и команда, что с ним делать (pick — «взять как есть»).
Основные команды интерактивного rebase
Вот ключевые команды, которые вы можете использовать:
pick— применить коммит как есть.reword— применить изменения, но отредактировать сообщение коммита.edit— остановиться на этом коммите, чтобы изменить его содержимое.squash(илиs) — объединить этот коммит с предыдущим, при этом объединить и сообщения.fixup(илиf) — объединить с предыдущим, но игнорировать его сообщение (оставить только сообщение предыдущего).drop— удалить коммит.
Теперь давайте посмотрим на реальные сценарии.
Объединение нескольких коммитов (squash)
Ситуация: у вас три коммита подряд:
- Реализация фичи
- «Поправил отступы»
- «Еще один мелкий фикс»
И вы хотите, чтобы в истории это был один аккуратный коммит.
Запускаете:
git rebase -i HEAD~3
В открывшемся файле меняете:
pick a1b2c3d Реализация фичи
pick e4f5g6h Поправил отступы
pick 1122334 Еще один мелкий фикс
на:
pick a1b2c3d Реализация фичи
squash e4f5g6h Поправил отступы
squash 1122334 Еще один мелкий фикс
Сохраняете и закрываете редактор. Git:
- применит первый коммит как есть;
- затем попробует «слить» второй и третий в первый;
- покажет вам редактор для объединенного сообщения.
В редакторе вы можете оставить одно сообщение, например:
Реализация фичи X с мелкими правками
В результате в истории останется один коммит вместо трех.
Изменение сообщения коммита (reword)
Если вы хотите просто поправить текст сообщения коммита, делайте так:
git rebase -i HEAD~3
Затем в нужной строке вместо:
pick e4f5g6h Плохое сообщение
пишете:
reword e4f5g6h Плохое сообщение
После сохранения Git дойдет до этого коммита и откроет редактор сообщения. Вы сможете ввести, например:
Исправление валидации формы регистрации
Соответственно, содержимое файлов не изменится, поменяется только текст сообщения.
Изменение содержимого коммита (edit)
Давайте разберемся на примере, когда вы хотите исправить код внутри уже сделанного коммита (не просто добавить новый).
Запускаете:
git rebase -i HEAD~3В списке коммитов меняете строку:
pick 1122334 Добавление новой функциина:
edit 1122334 Добавление новой функцииПосле сохранения Git начнет выполнять rebase и остановится на этом коммите, показав сообщение вроде:
Stopped at 1122334... Добавление новой функции You can amend the commit nowВносите нужные изменения в файлы, например:
# Редактируем файлы как обычно vim service.go # Добавляем измененные файлы в индекс git add service.go # Переписываем текущий коммит git commit --amend # Здесь мы можем изменить и содержимое, и сообщение коммитаКомментарий:
# --amend говорит Git заменить последний коммит новымПродолжаете rebase:
git rebase --continue
Готово. История останется линейной, но нужный коммит будет уже с обновленным содержимым.
Работа с конфликтами при rebase
Перебазирование часто приводит к конфликтам — особенно если и вы, и кто-то в main меняли одни и те же строки файлов. Давайте посмотрим, как это выглядит и что с этим делать.
Как выглядит конфликт при rebase
Допустим, во время git rebase main Git пишет:
Applying: Добавление новой функции
Using index info to reconstruct a base tree...
CONFLICT (content): Merge conflict in service.go
error: Failed to merge in the changes.
Patch failed at 0001 Добавление новой функции
Это значит:
- возник конфликт в файле
service.go; - rebase остановился, ожидая ваших действий.
Разрешение конфликта шаг за шагом
Покажу вам типичный порядок действий:
# 1. Проверяем статус
git status
Git покажет файл в статусе «both modified». Открываем его:
// Пример конфликтного участка в service.go
func Process() {
<<<<<<< HEAD
// Вариант из базы (например, из main)
doOldStuff()
=======
// Вариант из перебазываемого коммита
doNewStuff()
>>>>>>> Добавление новой функции
}
Метки конфликта:
<<<<<<< HEAD— версия из целевой ветки (на которую перебазируемся);=======— разделитель;>>>>>>> ...— версия из текущего коммита, который пытаемся применить.
Дальше:
Редактируете файл, оставляя только нужный код, убирая маркеры:
// Конечный вариант после ручного разрешения конфликта func Process() { // Объединили поведение doOldStuff() doNewStuff() }Добавляете файл в индекс:
git add service.go # Комментарий: так вы говорите Git, что конфликт в этом файле решенПродолжаете rebase:
git rebase --continue
Если на следующем коммите снова будут конфликты — повторяете процесс.
Отмена rebase, если что-то пошло не так
Если в процессе rebase все выглядит слишком сложно или вы поняли, что выбрали неправильную стратегию, можно откатить всё:
git rebase --abort
Git вернет состояние ветки к тому моменту, с которого вы начали перебазирование.
Полезно помнить:
git rebase --abort— отменяет rebase целиком;git rebase --skip— пропустить проблемный коммит (используется редко и осторожно).
Перебазирование и опубликованные ветки
Почему rebase может быть опасен
Главное правило работы с rebase:
Не перебазируйте ветки, которые уже были опубликованы и использовались другими разработчиками.
Причина в том, что rebase создает новые коммиты, а старые «теряются» (хотя и остаются где-то в истории до сборки мусора). Если кто-то уже забрал старую версию ветки, ваша переписанная история и их локальная история будут сильно расходиться.
Вот типичный сценарий проблемы:
- Вы создали ветку
feature, сделали коммиты, запушили вorigin/feature. - Коллега забрал вашу ветку, начал на ней что-то делать.
- Вы решили «почистить» историю и сделали
git rebase -i, а затемgit push --force.
Теперь у коллеги и у вас разные наборы коммитов с одинаковым именем ветки. Это приведет к конфликтам при следующем pull или при попытке пуша с его стороны.
Безопасный вариант: push --force-with-lease
Если вам всё-таки нужно переписать опубликованную ветку (например, это ваша личная ветка, которую никто больше не трогает), используйте более аккуратный вариант пуша:
git push --force-with-lease
Чем он лучше простого --force:
--forceслепо перезаписывает состояние ветки в удаленном репозитории;--force-with-leaseсначала проверит, что в удаленном репозитории ветка не изменилась по сравнению с тем, что у вас локально.
То есть если кто-то успел допушить в эту ветку новые коммиты, --force-with-lease откажется перезаписывать историю, защищая вас от затирания чужой работы.
Рекомендации по использованию rebase в команде
Чтобы не создавать лишних проблем:
- Перебазируйте только свои личные ветки, пока их никто больше не использует.
- Договаривайтесь в команде о правилах:
- Можно ли делать rebase ветки после открытия Pull Request.
- Можно ли применять
push --forceк общим веткам.
- Для веток
main,master,develop,releaseпочти всегда лучше дискутировать: rebase противоречит идее «официальной истории» продукта.
Типичные сценарии использования rebase
Теперь давайте посмотрим на несколько жизненных сценариев, где rebase особенно полезен.
Сценарий 1. Обновление фичи от main перед Pull Request
- Вы создали ветку
feature/search, поработали, сделали 5–10 коммитов. - За это время в
mainпоявилось несколько других задач. - Перед тем как открывать Pull Request, вы хотите:
- подтянуть свежую
main; - сделать свою историю ровной и понятной ревьюерам.
- подтянуть свежую
Шаги:
# Обновляем локальный main
git checkout main
git pull origin main
# Возвращаемся в свою ветку
git checkout feature/search
# Перебазируемся на актуальную main
git rebase main
Если в процессе были конфликты — решаете их, выполняете git rebase --continue.
После этого ветка выглядит так, как будто вы начали работу от последнего состояния main. Pull Request будет содержать только ваши изменения поверх свежей базы.
Сценарий 2. Чистка истории перед публикацией
Допустим, у вас локально такое:
* 99aabbc Debug print
* 5566778 Еще один фикс
* 1122334 Исправил баг
* e4f5g6h WIP
* a1b2c3d Начальная реализация
Вы не хотите публиковать это в таком виде. Нужен, например, один аккуратный коммит «Реализация X».
Действия:
git rebase -i HEAD~5
Меняете список так:
pick a1b2c3d Начальная реализация
squash e4f5g6h WIP
squash 1122334 Исправил баг
squash 5566778 Еще один фикс
squash 99aabbc Debug print
После сохранения и выбора нормального сообщения вы получите один аккуратный коммит. Теперь уже можно спокойно делать:
git push origin feature/your-feature
Сценарий 3. Перенос работы на другую базу (смена ветки-родителя)
Представьте, вы начали фичу от ветки develop, а потом решили, что ее нужно делать от release/1.2:
# Вы сейчас в feature
git checkout feature
# Переносим все коммиты ветки feature на ветку release/1.2
git rebase release/1.2
Git возьмет ваши коммиты и попытается применить их так, словно вы изначально начали их писать от release/1.2.
Технические детали работы rebase
Что делает rebase под капотом
Давайте кратко заглянем внутрь:
- Git определяет общего предка ветки, с которой вы перебазируетесь (например,
main), и вашей текущей ветки. - Строит список коммитов, которые находятся только в вашей ветке после этого предка.
- Для каждого такого коммита:
- создает патч (diff);
- применяет этот патч к новой базе (например, к последнему коммиту
main); - создает новый коммит с новым идентификатором.
Поэтому после rebase:
- старые коммиты технически больше не используются;
- появляются новые коммиты, отличающиеся только идентификатором и, возможно, временем.
Важность понимания идентификаторов коммитов
Поскольку rebase создает новые коммиты, их идентификаторы (хэши) меняются. Это важно:
- если вы сверяете историю по хэшам (например, вам прислали «посмотри коммит
a1b2c3d»); - если вы уже пушили ветку и потом сделали rebase — хэши в удаленной и локальной ветке не совпадут, несмотря на то, что код может быть тем же.
Команда git log после rebase показывает новую историю. Старые коммиты по-прежнему какое-то время хранятся и могут быть доступны через reflog.
Восстановление после неудачного rebase через reflog
Если вы сделали rebase, а потом поняли, что всё сломали, есть шанс всё вернуть с помощью git reflog.
git reflog — это журнал перемещений указателя HEAD:
git reflog
Вы увидите список действий:
3f4e2c1 HEAD@{0}: rebase finished: returning to refs/heads/feature
a1b2c3d HEAD@{1}: rebase: Добавление новой функции
e4f5g6h HEAD@{2}: checkout: moving from main to feature
...
Если вы хотите вернуться к состоянию до rebase:
# Предположим, нужное состояние - это HEAD@{2}
git reset --hard HEAD@{2}
# Комментарий: мы жестко двигаем ветку к старому состоянию
Это полезно, когда вы сделали что-то сложное с rebase и хотите отменить последствия, но уже закрыли терминал и не можете использовать git rebase --abort.
Практические советы и паттерны использования
Небольшие коммиты — проще rebase
Интерактивный rebase становится особенно удобным, если вы делаете:
- небольшие, логически завершенные коммиты;
- осмысленные сообщения к коммитам.
Тогда:
- проще менять порядок;
- проще объединять коммиты;
- проще понимать, что происходит при конфликте.
Не бойтесь rebase на локальных ветках
На локальных ветках вы можете экспериментировать довольно свободно:
- запустить
git rebase -i; - попробовать поменять порядок коммитов;
- что-то испортить и восстановиться через
git rebase --abortилиgit reflog.
Так вы постепенно начнете лучше понимать, как ведет себя история и какие операции безопасны.
Комбинируйте rebase и merge
Не обязательно быть «в лагере rebase» или «в лагере merge». Часто удобнее:
- в личной работе активно использовать
git rebaseдля чистой истории; - на уровне общих веток (
main,develop) использоватьgit merge, чтобы явно фиксировать, когда какая ветка была влита.
Например:
- внутри своей ветки вы делаете
git rebase main; - после ревью вы сливаете ветку в
mainчерезmerge(или через squash merge в системе контроля вроде GitHub/GitLab).
Так история main будет аккуратной, а структура работы — прозрачной.
Заключение
Перебазирование в Git — это мощный инструмент для управления историей коммитов. С его помощью вы можете:
- выстраивать линейную историю без лишних merge-коммитов;
- переносить свой код на актуальную базу (
main,develop,release); - чистить историю перед публикацией, объединяя и исправляя коммиты;
- проще проводить ревью, показывая только осмысленные изменения.
При этом важно помнить о ключевых ограничениях:
- rebase переписывает историю, меняет хэши коммитов;
- перебазировать опубликованные ветки нужно крайне осторожно;
- лучше использовать
git push --force-with-lease, а не «голый»--force.
Если вы будете применять rebase в основном для локальной работы и подготовки аккуратной истории, он станет удобным и безопасным инструментом. Постепенно вы научитесь использовать и базовый, и интерактивный режим, уверенно решать конфликты и разбираться в сложных ситуациях с историей.
Частозадаваемые технические вопросы по git rebase
Вопрос 1. Как перебазировать только часть коммитов, а не все подряд?
Если вам нужно перебазировать не последние N коммитов, а диапазон, можно использовать явные хэши:
# Начать интерактивный rebase с коммита после abc123
git rebase -i abc123
# Комментарий: в списке будут все коммиты после abc123 до HEAD
Затем в интерактивном режиме вы можете drop те коммиты, которые не хотите трогать, или оставить только нужные с pick.
Вопрос 2. Как сделать rebase так, чтобы мои изменения применялись первыми, а потом шел main?
Такое поведение не поддерживается напрямую, но можно воспользоваться временной веткой:
git checkout feature
git checkout -b temp-feature
git checkout main
git merge temp-feature # или git rebase temp-feature, если подходит
# Теперь история main содержит ваши изменения раньше других
Обычно проще пересмотреть саму стратегию ветвления — такие сценарии нужны редко.
Вопрос 3. Как остановить автоматический запуск редактора при rebase -i и задать сценарий из файла?
Можно заранее подготовить сценарий rebase в файле и запустить:
GIT_SEQUENCE_EDITOR="cat my-plan.txt >" git rebase -i HEAD~5
Где my-plan.txt содержит команды вида pick, squash и т.д. Это уже продвинутый сценарий, применимый в автоматизации.
Вопрос 4. Как сделать rebase сразу нескольких веток, зависящих друг от друга?
Обычно перебазируют цепочку веток по очереди:
# feature-1 основана на main
git checkout feature-1
git rebase main
# feature-2 основана на feature-1
git checkout feature-2
git rebase feature-1
Важно соблюдать порядок — сначала перебазировать нижний уровень, затем следующий.
Вопрос 5. Как понять, какие именно коммиты будут затронуты при rebase?
Перед rebase можно посмотреть диапазон:
# Показываем коммиты, которые будут перемещены
git log --oneline main..feature
# Комментарий: здесь main - база, feature - текущая ветка
Именно этот набор коммитов Git попытается перенести на новую базу при git rebase main.
Постройте личный план изучения Git до уровня Middle — бесплатно!
Git — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Git
Лучшие курсы по теме

Основы Git
Антон Ларичев
TypeScript с нуля
Антон Ларичев