Баннер мобильный (3) Пройти тест

Git rebase: интерактивный rebase, squash, fixup и решение конфликтов

Как аккуратно переписать историю, объединить коммиты и не потерять изменения при конфликте

Разбор

25 февраля 2026

Поделиться

Скопировано
Git rebase: интерактивный rebase, squash, fixup и решение конфликтов

Содержание

    Разбираемся, что такое rebase, чем она отличается от merge, что именно меняется в истории и как безопасно откатить изменения.

    Что такое git rebase и зачем она нужна

    Команда git rebase помогает перенести коммиты текущей ветки в Git на вершину ветки main (master) или на конкретный коммит.

    При переносе Git коммиты воспроизводятся заново, поэтому история становится линейной, а хеши перенесенных коммитов меняются.

    Команда rebase пригодится, когда нужно:

    • подтянуть изменения из ветки main (master) в свою ветку без merge-коммита;
    • использовать git pull —rebase, чтобы локальные коммиты оказались поверх свежих изменений из удаленного репозитория.
    • навести порядок в истории перед pull request, например объединить мелкие коммиты через squash или fixup, переименовать или изменить порядок коммитов;

    Различия merge и rebase

    И merge, и rebase решают одну задачу: объединить изменения из разных веток. Слияние нужно, когда вы хотите взять изменения из одной ветки и добавить их в другую. Самый частый сценарий:

    • Есть базовая ветка main (master).
    • Вы работаете в feature/*.
    • Нужно либо обновить feature/* свежими изменениями из main, либо влить feature/* в main.

    Разница между merge и rebase в том, как слияние отражается в истории коммитов:

    • Merge объединяет две линии разработки, сохраняет факт слияния и ничего не переписывает. История получается ветвистая.
    • Rebase переносит коммиты одной ветки поверх другой и тем самым делает историю линейной. При этом переносимые коммиты создаются заново, а их хеши меняются.
    Схема отличий Git merge и rebase
    Разница между merge и rebase

    Как git rebase «переписывает историю»

    Git берет ваши коммиты из текущей ветки и воспроизводит их поверх другой точки в истории. Ветка будет выглядеть так, будто вы начали работу от более свежего состояния ветки main (master).

    Фактически Git не переносит старые коммиты, а воссоздает их с другим родителем. Хеш коммита зависит от содержимого, метаданных и родительского коммита. Так как после переноса меняется родитель, то хеш тоже меняется.

    Пример

    Вы находитесь на feature и запускаете:

    git rebase main

    Git выполняет последовательность действий:

    1. Находит общего предка веток feature и main (коммит, от которого feature когда-то ответвилась).
    2. Определяет набор коммитов feature, которых нет в main. Условно это D, E, F.
    3. Перемещает указатель ветки feature на вершину main (например, на C). Эта вершина становится новой базой.
    4. Применяет изменения из D, E, F заново поверх C.

    Если нужно перебазироваться на актуальное состояние удаленной ветки

    С помощью команды git rebase origin/main (или origin/master) можно перенести коммиты поверх состояния ветки на сервере. 

    Перед этим стоит обновить локальные ссылки на удаленные ветки, иначе origin/main (origin/master) может оказаться неактуальным:

    git fetch origin
    git checkout feature
    git rebase origin/main

    Перенос части истории на другую базу с git rebase —onto

    Команду git rebase —onto используют, когда нужно перенести не всю ветку целиком, а только часть коммитов. 

    Это пригодится, например, если ветка разрослась и часть изменений нужно перенести на другую ветку.

    Общий вид команды

    git rebase --onto <newbase> <upstream> <branch>

    <newbase> — туда переносим коммиты («новая база»).

    <upstream> — граница, от которой начинаем перенос (коммиты до нее не трогаем).

    <branch> — какую ветку перебазируем (если не указать, берется текущая).

    Git берет коммиты из <branch>, которые идут после <upstream>, и применяет их заново поверх <newbase>.

    Пример

    Допустим, у вас в feature есть цепочка коммитов D — E — F — G, но ее первые коммиты (D и E) относятся к другой задаче. Коммиты F и G нужно перенести поверх актуального main, а D и E сохранить отдельно.

    Схема переноса части истории в другую базу Git
    1. Сначала фиксируем D и E отдельной веткой.

    Чтобы сохранить часть ветки с D — E, лучше зафиксировать ее на отдельной ветке до rebase:

    git branch other-task E
    1. Затем переносим коммиты после E.
    git checkout feature
    git rebase --onto main E

    main — это <newbase>.

    E — это <upstream> (граница).

    Ветка по умолчанию — текущая (feature).

    В результате в feature появятся новые коммиты F’ и G’ поверх C, то есть история станет C — F’ — G’.

    Ветка feature больше не указывает на старую цепочку D — E — F — G, а ветка other-task указывает на …— D — E и сохраняет доступ к этой части истории.

    Полезные команды управления процессом

    Во время rebase Git может остановиться, если нужно разрешить конфликт, отредактировать изменения или подтвердить применение очередного коммита. В этот момент важно понимать, как корректно продолжить процесс или откатиться.

    • git rebase —continue

    Команда продолжает rebase после паузы. Используется чаще всего после разрешения конфликтов.

    Порядок действий:

    1. Посмотреть, что именно требует внимания:
    git status
    1. Исправить конфликтующие файлы (вручную или через mergetool).
    2. Добавить исправленные файлы в индекс:
    git add <files>
    1. Продолжить rebase:
    git rebase --continue

    Важно: без git add Git не считает конфликт решенным и не продолжит rebase.

    • git rebase —abort

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

    git rebase --abort

    —abort безопасен тем, что откатывает весь процесс целиком, не оставляя промежуточных состояний rebase.

    • git rebase —skip

    Пропускает текущий коммит из очереди rebase и продолжает работу со следующим.

    git rebase --skip

    Когда это нужно:

    • изменения из этого коммита уже присутствуют в новой базе (например, их внесли другим коммитом);
    • коммит стал неактуален, и его действительно нужно исключить из истории.

    —skip означает «не применять этот коммит», поэтому на итоговой ветке не будет пропущенных изменений. Применяйте его, только если вы уверены, что пропускаемый коммит не нужен или его изменения уже есть в коде. Потому что если завершить rebase, то восстановить их потом можно через reflog (для определения хеша коммита) и cherry-pick (для выбора коммита).

    Интерактивный rebase: git rebase -i

    Интерактивный rebase — это режим rebase, в котором вы вручную управляете тем, как будут пересобраны коммиты. Механика та же, что и у обычного rebase: Git воспроизводит коммиты заново поверх выбранной базы, поэтому у измененных коммитов меняются хеши.

    Разница в том, что вы можете заранее указать, какие коммиты оставить, переименовать, объединить, переставить или отредактировать.

    Синтаксис git rebase -i HEAD~N и что означает список todo

    Чаще всего интерактивный rebase запускают на месте указателя (HEAD, обычно это последний коммит текущей ветки). Чтобы вывести список коммитов для пересборки, введите команду:

    git rebase -i HEAD~3

    Git откроет временный текстовый файл todo, где каждая строка — это один коммит и действие, которое нужно с ним выполнить. 

    Список выглядит примерно так:

    pick a1b2c3d Add validation for order
    pick b2c3d4e Fix typo in error message
    pick c3d4e5f Add tests for validation

    По умолчанию выбрано условие pick для всех коммитов, что означает «взять коммит и применить его в новой истории». Если вы не будете ничего менять, просто сохраните файл и закроете редактор, то выполнится rebase и пересборка этих же коммитов.

    Чтобы управлять коммитами, разберем специальные команды.

    Основные команды интерактивного rebase: pick, reword, edit, drop

    • pick — оставить коммит и применить его в новой истории без изменений содержимого.
    • reword — оставить содержимое коммита, но изменить его сообщение.

    Пример

    1. Запустите rebase:
    git rebase -i HEAD~3
    1. Откроется todo. Замените pick на reword у нужного коммита, например:
    pick   a1b2c3d Add validation for order
    reword b2c3d4e Fix typo in error message
    pick   c3d4e5f Add tests for validation
    • edit — остановиться на выбранном коммите, чтобы вручную изменить его содержимое (добавить или удалить изменения), а затем продолжить rebase. После правок понадобится git rebase —continue.

    Пример 

    Вы хотите добавить небольшой фикс в коммит, после которого уже были коммиты.

    1. Запускаете интерактивный rebase и ставите edit на нужном коммите:
    git rebase -i HEAD~N
    1. Git остановится на выбранном коммите. Вы вносите изменения в файлы и добавляете их в индекс:
    git add <files>
    1. Обновляете текущий коммит:
    git commit --amend
    1. Продолжаете rebase:
    git rebase --continue
    • drop — исключить коммит из новой истории (не применять его).
      1. Также запусткаеите rebase:
    git rebase -i HEAD~3
    1. Ставите drop на коммит:
    pick a1b2c3d Add validation for order
    drop b2c3d4e Debug logging (temporary)
    pick c3d4e5f Add tests for validation

    Или просто удаляете строку с этим коммитом из todo.

    Перестановка коммитов

    Чтобы поменять коммиты местами, достаточно переставить строки в todo-файле.

    Объединение коммитов с squash и fixup

    Squash и fixup используются в интерактивном rebase, чтобы объединить несколько коммитов в один.

    Обе команды объединяют коммиты, но по-разному работают с сообщениями:

    • squash объединяет изменения и позволяет отредактировать итоговое сообщение.

    Как выглядит в todo:

    pick   a1b2c3d Add validation for order
    squash b2c3d4e Fix typo in validation message

    Git остановится и откроет редактор сообщения итогового коммита. 

    • fixup объединяет изменения без добавления нового сообщения. В итоге остается сообщение основного коммита.

    Как выглядит в todo:

    pick a1b2c3d Add validation for order
    fixup b2c3d4e Fix typo in validation message

    В итоге изменения из двух коммитов станут одним коммитом с сообщением только от основного коммита (который отмечен pick).

    Команда git pull —rebase и fetch + rebase

    Команда git pull —rebase сначала забирает изменения с сервера (fetch), а затем переносит ваши локальные коммиты поверх обновленной удаленной ветки (rebase). 

    Это эквивалентно связке git fetch + git rebase <upstream>, но одним шагом. Используйте этот вариант, если хотите обновлять ветку без merge-коммитов, но учитывайте, что локальные коммиты будут пересозданы и получат новые хеши.

    Как решать конфликты при rebase

    Во время rebase конфликты возникают в момент, когда Git пытается применить очередной коммит поверх новой базы, но изменения пересекаются с тем, что уже есть в целевой ветке. 

    Важно, что rebase применяет коммиты по одному, поэтому конфликт обычно относится к конкретному коммиту из очереди.

    Примерный алгоритм действия

    1. Определите конфликтующие файлы через git status.
    2. Разрешите конфликты в файлах (вручную или с mergetool).
    3. Добавьте исправленные файлы в индекс (git add).
    4. Продолжите rebase (git rebase —continue).

    Если rebase нужно остановить и откатиться назад, используйте git rebase —abort. Если конкретный коммит не нужен или его изменения уже присутствуют в базе, можно сделать git rebase –skip.

    Что такое git mergetool

    Интерфейс mergetool. Источник

    Команда git mergetool запускает внешний инструмент для разрешения конфликтов. Это полезно, когда:

    • в одном файле много конфликтов и проще разобрать их в визуальном интерфейсе;
    • нужно сравнить две версии построчно.

    Повторяющиеся конфликты и rerere

    Если вы часто сталкиваетесь с одинаковыми конфликтами (например, регулярно делаете rebase на активно меняющийся main), помогает механизм rerere (reuse recorded resolution). Он запоминает, как вы разрешили конфликт, и может автоматически применить такое же решение в следующий раз.

    Включается один раз:

    git config --global rerere.enabled true

    После этого Git будет пытаться переиспользовать ранее сохраненные решения.

    Коротко о git rebase

    • merge объединяет ветки, сохраняет факт слияния и не переписывает историю.
    • rebase переносит коммиты поверх другой базы и воспроизводит их заново, из-за чего история становится линейной, а хеши меняются.
    • git rebase origin/main требует актуальных ссылок на удаленные ветки, поэтому перед ним обычно делают git fetch.
    • git rebase —onto помогает перенести только часть истории, а не всю ветку.
    • Интерактивный git rebase -i позволяет управлять коммитами: менять сообщения (reword), править содержимое старых коммитов (edit), удалять ненужные (drop), менять порядок.
    • squash и fixup объединяют коммиты: squash дает отредактировать итоговое сообщение, fixup оставляет сообщение основного коммита.
    • При конфликтах rebase останавливается на конкретном коммите, конфликт решается стандартно: 

    — исправить конфликт;

    git add;

    git rebase —continue;

    — при необходимости —abort и —skip.

    Разбор

    Поделиться

    Скопировано
    0 комментариев
    Комментарии