Как разрешать конфликты слияния в Git
Практика4 минРедакция Innovialabs
Коротко
Конфликт возникает, когда две ветки по-разному изменили одни и те же строки; вы выбираете итоговый вариант, удаляете маркеры, делаете git add и завершаете слияние.
Короткий ответ
Конфликт слияния — это не ошибка, а вопрос Git к вам: «две ветки изменили одно и то же место по-разному, какой вариант оставить?». Алгоритм всегда одинаковый:
- Запустите
git statusи найдите файлы в разделе Unmerged paths. - Откройте каждый файл, найдите маркеры
<<<<<<<,=======,>>>>>>>. - Оставьте правильный код и удалите маркеры.
- Выполните
git add <файл>для каждого исправленного файла. - Завершите операцию:
git commitдля merge илиgit rebase --continueдля rebase.
Почему возникают конфликты
Git умеет автоматически объединять изменения, если они затрагивают разные строки или разные файлы. Конфликт появляется, когда:
- в обеих ветках изменили одни и те же строки одного файла;
- в одной ветке файл удалили, а в другой — изменили;
- обе ветки создали файл с одинаковым именем и разным содержимым.
Чем дольше ветка живет отдельно от основной, тем выше шанс, что кто-то поменяет тот же код.
Как читать маркеры конфликта
Внутри файла Git показывает обе версии:
<<<<<<< HEAD
const timeout = 3000;
=======
const timeout = 5000;
>>>>>>> feature/retry
- Между
<<<<<<< HEADи=======— ваша текущая ветка (та, в которую вы сливаете). - Между
=======и>>>>>>> feature/retry— входящие изменения.
Важная деталь: при rebase роли меняются местами. HEAD там — это ветка, на которую вы перебазируетесь, а «входящие» — ваши собственные коммиты.
Если включить стиль diff3 (или zdiff3 в новых версиях Git), появится еще и исходная версия до изменений — это сильно упрощает понимание, кто что поменял:
git config --global merge.conflictStyle diff3
Разрешение в терминале
git merge feature/retry
# CONFLICT (content): Merge conflict in src/config.js
git status # список конфликтных файлов
# правим файл вручную
git add src/config.js
git commit # Git предложит готовое сообщение merge-коммита
Если нужно целиком взять одну из сторон для файла:
git checkout --ours src/config.js # оставить версию текущей ветки
git checkout --theirs src/config.js # взять входящую версию
git add src/config.js
Помните про подмену ролей при rebase: там --ours и --theirs тоже меняются местами.
Разрешение в VS Code
VS Code подсвечивает конфликты и показывает над ними кнопки:
- Accept Current Change — оставить вашу версию;
- Accept Incoming Change — взять входящую;
- Accept Both Changes — оставить обе (часто потом нужно доработать руками);
- Compare Changes — открыть сравнение.
Для сложных случаев есть Merge Editor: три панели — входящие изменения, текущие и итоговый результат. После правки сохраните файл и нажмите «+» (stage) в панели Source Control.
Как безопасно отменить
Если запутались, можно вернуться в исходное состояние:
git merge --abort # отменить слияние
git rebase --abort # отменить rebase
git cherry-pick --abort
Эти команды возвращают ветку к состоянию до начала операции. Перед слиянием лучше закоммитить или спрятать (git stash) незавершенную работу, чтобы отмена ничего не задела.
Частые ошибки
- Забытые маркеры. Код с
<<<<<<<закоммитили — сборка падает. Перед коммитом выполните поиск по маркерам илиgit diff --check. - «Accept Both» без проверки. Две версии функции рядом — это часто дубликат или сломанная логика.
- Не запустили тесты. Конфликт решен синтаксически, но поведение могло измениться. Прогоните тесты и соберите проект.
- Решение наугад. Если не понимаете, зачем коллега изменил код, спросите его — это быстрее, чем искать баг потом.
Как снизить количество конфликтов
- Маленькие PR. Чем меньше изменений и чем быстрее они вливаются, тем меньше пересечений.
- Регулярно подтягивайте основную ветку в свою (
git pull --rebaseили merge изmain). - Единое форматирование. Prettier, Black и подобные инструменты убирают конфликты из-за пробелов и кавычек.
- Не смешивайте рефакторинг и фичи. Массовое переименование лучше вынести в отдельный быстрый PR.
- Договоритесь о зонах ответственности, когда несколько человек работают в одном модуле.