← Назад к вопросам

Что происходит с commits при выполнении rebase?

2.0 Middle🔥 192 комментариев
#JavaScript Core

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Git rebase: что происходит с коммитами

Rebase - это один из самых мощных инструментов Git, но и самый запутанный для новичков. Понимание того, что происходит с коммитами во время rebase, критично для безопасной работы.

1. Основная идея rebase vs merge

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

// ИСХОДНАЯ ИСТОРИЯ
// main:   A -- B -- C
//              \
// feature:      D -- E

// MERGE - создаёт commit с двумя parents
git merge feature
// main:   A -- B -- C -- M (merge commit)
//              \       /
// feature:      D -- E
// История: линейная, но М имеет двух родителей

// REBASE - переписывает историю commits
git rebase main feature
// main:   A -- B -- C
// feature:            D' -- E'
// История: полностью линейная, D и E переписаны

2. Как работает rebase: пошагово

// ШАГ 1: Найти общего предка (merge base)
// main:   A -- B -- C
//              \
// feature:      D -- E
// Общий предок: B

// ШАГ 2: Сохранить коммиты из feature (D, E)
// Git берёт разницу между B и E:
// patch1 = изменения из D
// patch2 = изменения из E

// ШАГ 3: Переместить feature на последний коммит main (C)
checkout feature  // находимся на E
rebase main
// Теперь feature указывает на C

// ШАГ 4: Применить сохранённые patches
// Применить patch1 на C -> создать D' (новый коммит!)
// Применить patch2 на D' -> создать E' (новый коммит!)

// РЕЗУЛЬТАТ:
// main:   A -- B -- C
// feature:            D' -- E'
// D и E больше не существуют (если нет других ветвей на них)

3. Коммиты полностью переписываются

Важное свойство: при rebase создаются НОВЫЕ коммиты, старые не изменяются:

// Исходный коммит D
// hash: abc123
// message: "Добавил feature"
// parent: B (hash xyz789)
// changes: изменил файл.js

// После rebase создаётся D'
// hash: def456 (ДРУГОЙ хеш!)
// message: "Добавил feature" (сообщение то же)
// parent: C (hash qwe123) (ДРУГОЙ родитель!)
// changes: изменил файл.js (изменения те же)

// ВАЖНО: D и D' - это РАЗНЫЕ коммиты
// Если кто-то в другой ветке ссылается на D, это станет проблемой!

4. Проблемы при rebase: конфликты и история

// ПРОБЛЕМА 1: Конфликты при rebase
git rebase main feature
// Если D и E изменяют файлы, которые тоже изменились в main
// произойдет CONFLICT

// КОНФЛИКТ в файле:
// <<<<<<< HEAD (main версия)
// console.log('main version');
// =======
// console.log('feature version');
// >>>>>>> feature

// Нужно вручную разрешить конфликт
git add файл.js
git rebase --continue

// Или отменить rebase
git rebase --abort

// ПРОБЛЕМА 2: Потеря истории
feature branch имела 5 коммитов
После rebase эти 5 коммитов переписаны
Если кто-то потом попробует merge старую feature
произойдет УЖАС (двойные коммиты, конфликты)

5. Interactive rebase: переписывание истории

// СИНТАКСИС
git rebase -i HEAD~3
// Позволяет переписать последние 3 коммита

// ИНТЕРАКТИВНЫЙ РЕДАКТОР:
// pick d12345 Первый коммит
// pick e23456 Второй коммит
// pick f34567 Третий коммит

// КОМАНДЫ:
// pick   - оставить коммит
// reword - изменить сообщение
// edit   - остановиться перед этим коммитом
// squash - объединить с предыдущим
// fixup  - объединить без сохранения сообщения
// drop   - удалить коммит
// exec   - выполнить команду

// ПРИМЕР: объединить 2 коммита в 1
// pick d12345 Первый коммит
// squash e23456 Второй коммит (объединится с первым)
// pick f34567 Третий коммит

// РЕЗУЛЬТАТ:
// Коммиты d и e объединены в один
// Появляется диалог редактирования сообщения
// Создаётся новый коммит вместо двух старых

// ПРИМЕР: изменить порядок коммитов
// pick f34567 Третий коммит (ПЕРЕМЕСТИЛИ СЮДА)
// pick d12345 Первый коммит
// pick e23456 Второй коммит

// РЕЗУЛЬТАТ: коммиты переписаны в новом порядке!

6. Когда использовать rebase vs merge

// REBASE лучше когда:
// 1. Работаешь один на фиче
// 2. Хочешь чистую, линейную историю
// 3. Коммиты ещё не запушены в public branch

git checkout feature
git rebase main  // переписываем feature
// История чистая, без merge коммитов

// MERGE лучше когда:
// 1. Фича уже в shared branch (другие могут зависеть)
// 2. Коммиты уже запушены (git push)
// 3. Хочешь сохранить историю интеграции

git merge feature
// Создаётся merge коммит, старые коммиты не меняются

// ЗОЛОТОЕ ПРАВИЛО: никогда не rebase публичные коммиты!
// Если другие зависят от твоих коммитов, не переписывай их

7. Git reflog: восстановление после rebase

// Сделал rebase и пожалел? Не волнуйся!

// reflog хранит все движения HEAD
git reflog
// output:
// abc123 HEAD@{0}: rebase (finish)
// def456 HEAD@{1}: rebase (pick)
// ghi789 HEAD@{2}: checkout
// jkl012 HEAD@{3}: commit

// Вернуться к состоянию ДО rebase
git reset --hard jkl012
// Теперь как будто rebase не было

// Эта команда спасала мне жизнь много раз!

8. Проблемы и ошибки при rebase

// ОШИБКА 1: Rebasing публичного branch
git push origin feature
# После этого другие разработчики могли запулить feature

git rebase -i HEAD~3  # ОПАСНО!
git push --force origin feature  # ЕЩЕ ОПАСНЕЕ!

// Последствия:
// - Другие разработчики имеют старые коммиты (abc123)
// - Ты перезаписал их на новые коммиты (def456)
// - Когда они попытаются push, будет конфликт
// - История запутается, дублируются коммиты

// РЕШЕНИЕ: используй git push --force-with-lease
// Это проверит, что никто не push'ил с момента твоего fetch

// ОШИБКА 2: Rebase с неправильным базисом
git rebase main feature
// Если main сам был переписан (кто-то rebasil его)
// Это может привести к странным результатам

// ОШИБКА 3: Потеря коммитов
git rebase -i HEAD~10
# Случайно выбрал 'drop' на важном коммите
# Чтобы восстановить:
git reset --hard HEAD@{1}  # reflog спасает!

9. Rebase в practice: шаг за шагом

// СЦЕНАРИЙ: обновить feature с изменениями из main

// ШАГ 1: убедиться, что все коммиты закоммичены
git status  # должно быть чисто

// ШАГ 2: fetched последние изменения
git fetch origin

// ШАГ 3: перейти на feature
git checkout feature

// ШАГ 4: начать rebase
git rebase origin/main
// Git автоматически:
// - найдёт общего предка
// - сохранит коммиты feature
// - переместит их на origin/main
// - применит patches

// ШАГ 5: если конфликты
git status  # посмотри конфликты
# отредактируй файлы с <<<<<<
git add файл.js
git rebase --continue

// ШАГ 6: push (осторожно!)
git push origin feature --force-with-lease
// --force-with-lease безопаснее чем --force

10. Сравнение истории: rebase vs merge

// MERGE история (сложнее читать, но безопаснее)
A -- B -- C -- M -- F -- G
       \       /
        D -- E

// Коммиты остаются как есть
// Легко видеть, что когда-то была отдельная ветка
// Безопасно для публичных веток

// REBASE история (проще читать, но опаснее)
A -- B -- C -- D' -- E' -- F -- G

// Полностью линейная история
// Легко понять последовательность изменений
// Но оригинальные D и E потеряны

Практические советы

  1. Используй rebase для локальных фич
git rebase main feature  # перед PR
  1. Никогда не rebase коммиты, которые уже в shared branch
# ПЛОХО
git push
git rebase -i HEAD~5
git push --force

# ХОРОШО
git rebase -i HEAD~5
git push  # push один раз, до других
  1. Используй reflog для спасения
git reflog  # найти старое состояние
git reset --hard HEAD@{N}  # вернуться
  1. Проверь историю перед rebase
git log --oneline main..feature
# посмотри какие коммиты будут переписаны

Rebase - это мощный инструмент для чистой истории, но требует уважения и понимания. Когда сомневаешься - используй merge!