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

Что произойдет при поочередном выполнении rebase для слияния двух веток с родительской веткой?

1.8 Middle🔥 201 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью#ORM и Hibernate

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

Git Rebase: история коммитов и слияние веток

Что такое Rebase

Rebase (перебазирование) - это процесс перемещения коммитов одной ветки на вершину другой ветки. Это отличается от merge тем, что создаёт линейную историю вместо параллельных веток.

Исходная ситуация

Предположим, у нас есть три ветки:

Условие: есть родительская ветка (parent), две ветки (feature1, feature2)

ВЕТКА: parent
    C1 -> C2 -> C3 (основная ветка)
          ^
          |
          Точка отделения

ВЕТКА: feature1 (отделилась от C2)
    C4 -> C5

ВЕТКА: feature2 (отделилась от C2)
    C6 -> C7

Сценарий: поочередный Rebase обеих веток

Шаг 1: Исходное состояние

parent:   C1 -> C2 -> C3
                 |
                 C4 -> C5 (feature1)
                 |
                 C6 -> C7 (feature2)

Шаг 2: Rebase первой ветки (feature1)

git checkout feature1
git rebase parent

Что происходит:

  1. Git находит общего предка feature1 и parent (это C2)
  2. Вычисляет коммиты feature1 (C4, C5) как набор изменений (patches)
  3. Откатывает feature1 к C2
  4. Перемещает feature1 на вершину parent (на C3)
  5. Применяет patches (C4, C5) поверх C3
  6. Создаёт новые коммиты C4', C5'
ДО rebase:
parent:   C1 -> C2 -> C3
                 |
                 C4 -> C5 (feature1)

ПОСЛЕ rebase:
parent:   C1 -> C2 -> C3 -> C4' -> C5' (feature1)

Полный вид после первого rebase:

parent:   C1 -> C2 -> C3 -> C4' -> C5'
                 |
                 C6 -> C7 (feature2) - всё ещё указывает на C2

Шаг 3: Rebase второй ветки (feature2)

git checkout feature2
git rebase parent

Что происходит:

  1. Git находит общего предка feature2 и parent (всё ещё C2, но parent теперь указывает на C5')
  2. Вычисляет коммиты feature2 (C6, C7) как patches
  3. Откатывает feature2 к C2
  4. Перемещает feature2 на вершину parent (на C5')
  5. Применяет patches (C6, C7) поверх C5'
  6. Создаёт новые коммиты C6', C7'
ДО rebase:
parent:   C1 -> C2 -> C3 -> C4' -> C5'
                 |
                 C6 -> C7 (feature2)

ПОСЛЕ rebase:
parent:   C1 -> C2 -> C3 -> C4' -> C5' -> C6' -> C7' (feature2)

Итоговое состояние

Итого после двух rebase:

parent:   C1 -> C2 -> C3 -> C4' -> C5' -> C6' -> C7'
                             ^parent       ^feature1
                                          ^feature2

История полностью ЛИНЕЙНА - нет параллельных веток
Все коммиты расположены в одну линию

Практический пример

# Создаём исходную ситуацию
git init
echo "1" > file.txt && git add . && git commit -m "C1"
echo "2" > file.txt && git add . && git commit -m "C2"
echo "3" > file.txt && git add . && git commit -m "C3"
# HEAD на C3 (parent ветка по умолчанию master)

# Создаём первую ветку и добавляем коммиты
git checkout -b feature1 HEAD~1  # отделяемся от C2
echo "4" > file.txt && git add . && git commit -m "C4"
echo "5" > file.txt && git add . && git commit -m "C5"

# Создаём вторую ветку от той же точки
git checkout -b feature2 HEAD~2  # отделяемся от C2
echo "6" > file.txt && git add . && git commit -m "C6"
echo "7" > file.txt && git add . && git commit -m "C7"

# REBASE feature1
git checkout feature1
git rebase master  # master это C3
# Теперь: master -> C1->C2->C3, feature1 -> C1->C2->C3->C4'->C5'

# REBASE feature2
git checkout feature2
git rebase master
# Теперь feature2 перебазируется на master
# Но master ещё указывает на C3, а не на feature1

Важный момент: что такое "parent ветка"

Если под "родительской веткой" понимается main/master:

# Если хотим rebase feature1 на main
git checkout feature1
git rebase main  # перемещает feature1 на вершину main

# Если хотим rebase feature2 на main
git checkout feature2
git rebase main  # перемещает feature2 на вершину main

Результат:

ДО:
main:      C1 -> C2 -> C3
                 |
                 C4 -> C5 (feature1)
                 |
                 C6 -> C7 (feature2)

ПОСЛЕ rebase feature1:
main:      C1 -> C2 -> C3 -> C4' -> C5'
                 |
                 C6 -> C7 (feature2)

ПОСЛЕ rebase feature2:
main:      C1 -> C2 -> C3 -> C4' -> C5'
                           ^
                           C6' -> C7' (feature2)

КОНЕЧНЫЙ РЕЗУЛЬТАТ:
две ветки отдельно, но обе на вершине main:
feature1: main -> C3 -> C4' -> C5'
feature2: main -> C3 -> C6' -> C7'

Сравнение Rebase vs Merge

РEBASE:
parent:   C1 -> C2 -> C3 -> C4' -> C5' -> C6' -> C7'
          Преимущества: линейная история, чистая
          Недостатки: меняет хеши коммитов

MERGE:
parent:   C1 -> C2 -> C3 -> M1 (merge commit)
                 |                ^
                 C4 -> C5 --------+
                 |
                 C6 -> C7 --------+
          Преимущества: сохраняет полную историю
          Недостатки: много merge коммитов

Риски Rebase

1. Конфликты при применении patches:

git rebase main
# CONFLICT: изменение C4' конфликтует с C3
# Нужно разрешить конфликт вручную

git status
# On branch feature1, rebasing
# both modified: file.txt

# Исправляем конфликт
# Затем:
git add file.txt
git rebase --continue

2. Изменение истории - ОПАСНО для shared веток:

# Если feature1 уже pushed на remote
# и кто-то на неё ссылается
# То rebase создаст несовместимость

git rebase main
# История feature1 переписана
# Нужно force push (ОПАСНО)
git push origin feature1 --force-with-lease

3. Потеря коммитов если что-то пошло не так:

# Во время rebase конфликты
# Можно по ошибке стереть коммиты

git rebase --abort  # отменить rebase

Best Practices

1. Rebase только локальные ветки, которые ещё не pushed:

# ХОРОШО: локальная ветка
git rebase main  # никто не зависит

# ПЛОХО: ветка на remote, на которую ориентируются другие
git push origin feature1  # уже pushed
git rebase main          # изменяет историю
git push --force         # БЕДА для других разработчиков

2. Используй merge для shared веток, rebase для локальных:

# Поток разработки
git checkout -b feature/new-feature main
# Работаем локально
git rebase main  # rebase - локально, перед push
git push origin feature/new-feature  # push

# В PR: merge, не rebase
# Это сохраняет историю

3. Interactive rebase для очистки истории перед push:

git rebase -i main  # интерактивный rebase
# Можно:
# - переупорядочить коммиты
# - объединить коммиты (squash)
# - переименовать коммиты
# - удалить коммиты

Вывод

При поочередном rebase двух веток на parent:

  1. feature1 перебазируется на parent: коммиты C4, C5 применяются поверх C3, создаются C4', C5'
  2. feature2 перебазируется на parent: коммиты C6, C7 применяются поверх новой вершины (где теперь feature1), создаются C6', C7'
  3. Результат: полностью линейная история вместо параллельных веток
  4. Хеши меняются: все коммиты получают новые SHA-1
  5. Осторожно: может создать конфликты, которые нужно разрешать вручную
  6. Правило: используй rebase для локальных веток, merge для публичных

Это часто делается для очистки истории перед pull request в shared repository.

Что произойдет при поочередном выполнении rebase для слияния двух веток с родительской веткой? | PrepBro