Ветвление
1Что такое ветки и зачем они нужны2Слияние веток и конфликты3Конфликты слияния← вы здесь
Урок 3~12 минут

Конфликты слияния

Ты сделал git merge — и Git остановился с сообщением CONFLICT. Это не сломано. Это нормальная часть работы с ветками.

Когда возникает конфликт

Git умеет объединять изменения автоматически, если они не пересекаются:

  • Один разработчик изменил header.js, другой — footer.js → Git смержит без конфликта
  • Один добавил строку в начало файла, другой — в конец → Git смержит без конфликта

Конфликт возникает, когда оба изменили одно и то же место в одном файле. Git не знает, какую версию сохранить — и просит тебя решить.

bash
git switch main
git merge feature
# Auto-merging index.html
# CONFLICT (content): Merge conflict in index.html
# Automatic merge failed; fix conflicts and then commit the result.

Маркеры конфликта

Git вставляет в файл специальные маркеры:

<<<<<<< HEAD
Привет, мир!
=======
Hello, world!
>>>>>>> feature

Разберём:

  • <<<<<<< HEAD — начало твоей версии (текущая ветка)
  • ======= — разделитель
  • >>>>>>> feature — версия из вливаемой ветки
  • Всё между маркерами нужно заменить на то, что должно быть

Никакой магии — это просто текст в файле. Твоя задача: убрать маркеры и оставить правильный результат.

Пошаговое разрешение

bash
# 1. Смотришь какие файлы конфликтуют:
git status
# both modified: index.html
 
# 2. Открываешь файл, находишь маркеры, правишь вручную
# (или используешь инструмент — см. ниже)
 
# 3. После правки убираешь маркеры, сохраняешь файл
 
# 4. Помечаешь файл как разрешённый:
git add index.html
 
# 5. Завершаешь merge:
git commit
# Git сам предложит сообщение типа "Merge branch 'feature' into main"

Если конфликтных файлов несколько — разрешаешь каждый, потом git add для каждого, и один git commit в конце.

Merge editor в VS Code

VS Code понимает конфликтные маркеры и показывает удобный интерфейс:

Accept Current Change  |  Accept Incoming Change  |  Accept Both Changes  |  Compare Changes
─────────────────────────────────────────────────────────
<<<<<<< HEAD
Привет, мир!
=======
Hello, world!
>>>>>>> feature

Нажимаешь нужную кнопку над конфликтом — VS Code сам убирает маркеры и оставляет выбранный вариант. Не нужно редактировать маркеры вручную.

В IntelliJ/WebStorm — аналогичный трёхпанельный merge editor: слева текущая ветка, справа вливаемая, посередине результат.

Если хочешь отступить

Не нравится, что merge пошёл не так? Можно всё отменить:

bash
git merge --abort

Репозиторий вернётся в состояние до git merge. Ни одно изменение не потеряется — как будто merge не начинался. Потом можно обновить ветки, подготовиться и попробовать снова.

Как реже попадать в конфликты

Конфликты не исчезнут полностью, но их можно минимизировать:

Мержь часто. Ветка живёт неделю — накапливает конфликты. Мержи main в свою ветку каждый день.

bash
git switch feature
git merge main    # берём последнее из main в свою ветку

Общайся с командой. Если видишь, что коллега правит тот же файл — скажи. Договоритесь кто что делает.

Пиши атомарные коммиты. Один коммит — одна логическая задача. Легче понять что с чем конфликтует.

Три-точечный merge изнутри

Когда Git мержит ветки, он ищет общего предка — коммит, от которого оба ответвились:

         C1 (общий предок)
        /    \
      C2      C3
      |       |
    main   feature

Git сравнивает: что изменилось от C1 до main, что изменилось от C1 до feature. Если изменения не пересекаются — объединяет автоматически. Если пересекаются — конфликт.

Альтернатива: rebase

Вместо merge можно использовать rebase — он переносит коммиты feature поверх main, давая линейную историю без merge-коммитов:

bash
git switch feature
git rebase main

Конфликты при rebase тоже бывают, но разрешаются по одному на каждый коммит. Подробнее — в модуле «Продвинутый Git».

В следующем модуле — удалённые репозитории: как подключить GitHub, работать с push, pull и форками.

Конфликт — это не Git сломался. Это Git честно говорит: две версии, выбор за тобой.
Две ветки изменили одну и ту же строку в config.js:
mainconfig.js
const config = {
appName: "MyApp",
version: "2.0",
debug: false,
}
featureconfig.js
const config = {
appName: "MyApp",
version: "2.1-beta",
debug: false,
}
🎯
Миссия 1 из 4
Что нужно сделать с файлом после ручного разрешения конфликта?