Как Git выполняет слияние двух веток (merge)?

«Как Git выполняет слияние двух веток (merge)?» — вопрос из категории DevOps, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Команда git merge <branch-name> интегрирует изменения из указанной ветки в текущую (HEAD). Процесс зависит от истории коммитов и может проходить по трём основным сценариям.

1. Fast-Forward Merge (Быстрая перемотка)

Условие: Текущая ветка (main) является прямым предком целевой ветки (feature). Что происходит: Указатель текущей ветки просто перемещается вперёд до конца целевой ветки. Новый коммит слияния не создаётся. История остаётся линейной.

git checkout main
git merge feature  # HEAD (main) теперь указывает на последний коммит feature

2. Recursive Merge (3-way Merge или Создание коммита слияния)

Условие: Ветки разошлись (у каждой есть уникальные коммиты после общего предка). Что происходит: Git создаёт новый коммит слияния (merge commit), который имеет двух родителей. Изменения автоматически объединяются, если они не конфликтуют.

# История: main (A--B) и feature (A--C)
git checkout main
git merge feature
# Создаётся коммит M с родителями B и C
# История: A--B--M
#               /
#            C--

Алгоритм: Git находит общего предка (base), затем создаёт патч между base и каждой веткой и применяет оба патча к базе.

3. Merge с конфликтами

Условие: Git не может автоматически разрешить противоречивые изменения в одном и том же месте одного файла. Что происходит:

  1. Процесс слияния прерывается.
  2. Файлы с конфликтами помечаются в рабочей директории. В них добавляются специальные маркеры:
    <<<<<<< HEAD (Current Change)
    код из текущей ветки
    =======
    код из сливаемой ветки
    >>>>>>> feature (Incoming Change)
  3. Статус git status покажет "Unmerged paths".
  4. Разработчик должен вручную отредактировать файлы, убрав маркеры и оставив нужный код, затем выполнить git add для разрешённых файлов.
  5. Завершить слияние коммитом: git commit.

Лучшие практики:

  • Перед слиянием убедитесь, что рабочая директория чиста (git status).
  • Используйте git merge --no-ff для запрета fast-forward, чтобы всегда создавался видимый коммит слияния.
  • Для сложных интеграций рассмотрите стратегию git rebase для поддержания линейной истории.