Чтобы безопасно разрешать конфликты слияния с ИИ, сначала сделайте конфликт читаемым, затем дайте агенту для кода цели обеих сторон и чёткие правила, а результат принимайте только после того, как Git подтвердит, что маркеров конфликта не осталось, и тесты пройдут. Агенты вроде Claude Code и Codex здесь действительно полезны: они читают обе версии, общего предка и окружающий код быстрее вас и умеют совмещать два изменения, а не выбирать одно. Их типичная ошибка так же очевидна: если цель одной стороны неясна, они оставляют ту сторону, которая компилируется, и молча выбрасывают другую.
В этом руководстве — почему возникают конфликты (и почему параллельные агенты делают их чаще), пошаговый процесс от git status до git merge --continue, шаблон промпта, rebase против merge, git rerere, проверка до слияния и способы избежать большинства конфликтов заранее. Команды Git сверены с официальной документацией на git-scm.com по состоянию на октябрь 2026 года.
Откуда берутся конфликты слияния
Сливая две ветки, Git сравнивает обе с их общим предком. Изменения в разных местах файла объединяются автоматически. Если обе стороны изменили одно и то же место, Git не может выбрать: он останавливается и записывает в файл обе версии между маркерами конфликта. Это текстовый конфликт.
Самые частые виды:
- Конфликт содержимого: обе ветки изменили одни и те же строки одного файла.
- Изменение/удаление: одна ветка изменила файл, другая удалила или переместила его.
- Генерируемые файлы: lock-файлы, снапшоты, результаты сборки или пакеты переводов, которые перегенерировали обе ветки.
Есть и конфликт, которого Git не видит, — семантический. Две ветки трогают разные файлы, сливаются без единого конфликта и всё равно ломают друг друга: например, одна меняет сигнатуру функции, а другая добавляет новый её вызов. Поймать такое могут только тесты на слитом результате; в статье об очереди слияния это разобрано на примере.
Почему с параллельными агентами конфликтов больше
Несколько агентов одновременно, каждый в своём git worktree, не перезаписывают файлы друг друга во время работы. Но конфликт от этого не исчезает — он переносится на момент слияния. Каждый агент работает от той базы, с которой начал, и не видит, что изменили остальные. Некоторые файлы притягивают конфликты снова и снова:
- таблицы маршрутов, настройка внедрения зависимостей и другие реестры, куда каждая фича добавляет по строке,
- barrel-файлы (
index.ts) и общие типы, package.jsonи его lock-файл,- файлы переводов, changelog и конфиги.
Старательность агентов здесь тоже вредит: они переформатируют файл, который должны были лишь слегка задеть, «заодно» переименовывают хелпер или чинят посторонний линт-варнинг. Каждое такое действие расширяет область, которую изменили обе ветки.
Шаг 1: остановитесь и посмотрите
Когда merge или rebase останавливается, сначала выясните, где вы находитесь, и только потом просите кого-либо, человека или агента, всё исправить:
git status # какая операция, какие файлы "both modified"
git diff --name-only --diff-filter=U # только неслитые файлы
--diff-filter=U выбирает неслитые пути, и вы получаете чистый список, который можно отдать агенту. Помните и о путях отступления. Если конфликт оказался больше, чем вы думали, ещё ничего не потеряно:
git merge --abort # вернуться к состоянию до слияния
git rebase --abort # вернуть ветку туда, где она была до rebase
Документация Git советует закоммитить или спрятать (stash) свои изменения перед git merge, потому что --abort не всегда может восстановить незакоммиченную работу. То же касается worktree агента: пусть он закоммитит работу, прежде чем вы что-то в него сольёте.
Шаг 2: переключитесь на zdiff3, чтобы видеть базу
По умолчанию Git показывает только две стороны:
<<<<<<< HEAD
const timeout = config.timeout ?? 30_000;
=======
const timeout = getTimeout(config);
>>>>>>> feature/retry
Отсюда не понять, какой была исходная строка, а значит, и чего хотела каждая сторона. Переключите стиль конфликтов на zdiff3 (появился в Git 2.35; в более старых версиях используйте diff3):
git config --global merge.conflictStyle zdiff3
Теперь после маркера ||||||| в конфликте виден и общий предок:
<<<<<<< HEAD
const timeout = config.timeout ?? 30_000;
||||||| base
const timeout = config.timeout;
=======
const timeout = getTimeout(config);
>>>>>>> feature/retry
Теперь всё читается: одна сторона добавила значение по умолчанию, другая вынесла чтение в функцию. Правильное решение, скорее всего, сохраняет оба изменения — например, значение по умолчанию переезжает внутрь getTimeout. По документации git-config, zdiff3 похож на diff3, но выносит совпадающие строки в начале и конце конфликта за его пределы, так что фрагменты остаются компактными.
Если слияние уже идёт, маркеры отдельного файла можно пересоздать в новом стиле:
git checkout --conflict=zdiff3 src/http/client.ts
Для агента это ещё важнее, чем для вас. Без базы модели приходится угадывать, какая сторона что изменила, а именно с угадывания начинается тихая потеря данных.
Шаг 3: дайте агенту нужный контекст
Маркеры показывают строки. Но агенту нужно знать ещё и то, зачем каждая сторона их изменила. Git умеет показать историю обеих сторон для файла:
git log --merge -p src/http/client.ts # сначала диффы со стороны HEAD, потом со стороны MERGE_HEAD
git show :1:src/http/client.ts # общий предок
git show :2:src/http/client.ts # HEAD (ours)
git show :3:src/http/client.ts # MERGE_HEAD (theirs)
Добавьте человеческий контекст: описание задачи или pull request каждой ветки. Затем используйте примерно такой промпт:
Мы посреди git merge ветки feature/retry в main. В этих файлах конфликты
(merge.conflictStyle — zdiff3, база видна):
<вывод git diff --name-only --diff-filter=U>
Цель main: <одно-два предложения или описание PR>
Цель feature/retry: <одно-два предложения или описание PR>
Для каждого конфликтного файла:
1. Прочитай секции ours, base и theirs и одной строкой скажи, что каждая
сторона изменила относительно базы.
2. Напиши решение, которое сохраняет обе цели. Если они действительно
противоречат друг другу, не выбирай: остановись и спроси меня.
3. Правь только конфликтные фрагменты. Никакого переформатирования,
переименований и посторонних исправлений.
4. Не меняй тесты, чтобы они прошли, и не удаляй код, который не понимаешь.
Для package-lock.json / pnpm-lock.yaml: разреши только package.json, затем
перегенерируй lock-файл пакетным менеджером.
В конце: никаких маркеров конфликта, запусти проверку типов и тесты и дай
сводку по каждому файлу — что ты оставил от каждой стороны. Не запускай
git add, git commit и git merge --continue.
Последняя строка оставляет финальный шаг за вами. Сводка по файлам ускоряет ревью: вы сверяете каждое утверждение с диффом, а не читаете каждую строку с нуля.
Шаг 4: по одному файлу, по чётким правилам
Несколько правил отделяют чистое слияние от тихой регрессии:
- Никаких массовых
--oursи--theirs.git checkout --ours <путь>берёт одну сторону файла целиком и отбрасывает другую. Используйте это, только если точно знаете, что одна сторона должна победить, например для генерируемого файла, который вы всё равно перегенерируете. - Помните о подмене при rebase. Документация Git отмечает, что во время
git rebaseиgit pull --rebase--ours— это ветка, на которую вы перебазируетесь, а--theirs— ваша собственная работа, применяемая заново. Агенты путаются в этом удивительно часто, так что называйте им ветки по именам. - Генерируемые файлы перегенерируйте, а не сливайте. Lock-файлы, снапшоты и результаты сборки должны создаваться своим инструментом уже после того, как разрешены исходники.
- Изменение/удаление требует решения. Если одна ветка удалила файл, который изменила другая, агент должен сообщить об этом и спросить, а не восстанавливать или удалять его по своему усмотрению.
- Большие конфликты делите. Если в одном файле десятки фрагментов, попросите агента разрешать и объяснять их по частям или отступите на шаг и пересмотрите порядок слияний.
Шаг 5: проверьте, прежде чем продолжать
Слова агента «разрешено» — это утверждение, а не доказательство. Проверьте:
git diff --check # предупреждает о забытых маркерах конфликта (и ошибках пробелов)
git diff # просмотреть решение
pnpm typecheck && pnpm test # или проверка вашего проекта
git diff --check завершается с ненулевым кодом, если изменения вносят маркеры конфликта, поэтому это дешёвый барьер, который стоит запускать каждый раз. В свежих версиях Git git diff AUTO_MERGE показывает только изменения, сделанные на данный момент для разрешения текстовых конфликтов, — то есть ровно то, что написал агент.
Затем завершите операцию сами:
git add src/http/client.ts
git merge --continue # или: git rebase --continue
Не принимайте вслепую: тревожные признаки
| Тревожный признак | Что это обычно значит |
|---|---|
| Изменение одной из сторон просто пропало | Агент выбрал одну сторону вместо того, чтобы объединить их |
| Изменился тест или проверка в нём | Решение сломало поведение, а тест подогнали |
| Изменились файлы вне списка конфликтов | Расползание задачи: переформатирование или «исправления», которым нужно отдельное ревью |
| Код повторяется дважды | Обе стороны оставлены как есть, без слияния |
Маркеры или одинокий ======= в файле, который не является кодом | Никто не запустил git diff --check |
Если видите что-то из этого, попросите агента объяснить фрагмент или переделайте файл вручную. ИИ-ревью кода слитого результата — полезный второй взгляд, но запуск тестов оно не заменяет.
Rebase или merge, если конфликты вероятны
Работают оба; ломаются по-разному.
| Merge | Rebase | |
|---|---|---|
| Где вы разрешаете | Один раз, в коммите слияния | Покоммитно, при повторном применении каждого |
| Тот же конфликт дважды | Нет | Возможно, в нескольких коммитах |
--ours / --theirs | Ваша ветка / другая ветка | Наоборот: целевая ветка / ваша работа |
| История | Остаётся коммит слияния | Линейная, коммиты ветки переписываются |
| Безопасно для общих веток | Да | Только если на этой ветке больше никто не строит работу |
Для короткоживущих веток агентов, которыми пользуетесь только вы, частый rebase на main обычно удобнее: конфликты проявляются рано, пока они маленькие. Для долгоживущих или общих веток используйте merge. Во время rebase git rebase --skip полностью отбрасывает текущий коммит, поэтому используйте его, только если уверены, что этот коммит не нужен.
Пусть Git запомнит: rerere
Если вы снова и снова перебазируете одну и ту же ветку, один и тот же конфликт придётся разрешать не раз. git rerere («reuse recorded resolution») запоминает, как вы разрешили конфликт, и повторяет это решение, когда тот же конфликт возникает снова:
git config --global rerere.enabled true
После включения git merge и git commit вызывают его автоматически. Полезные команды:
git rerere status # пути, решение для которых будет записано
git rerere diff # как сейчас выглядит решение
git rerere remaining # конфликты, которые rerere не смог разрешить
git rerere forget src/http/client.ts # забыть неверно записанное решение
Две вещи стоит знать. rerere не трогает индекс: вы по-прежнему проверяете результат и делаете git add. И это хорошо, ведь неверное решение повторяется так же точно, как и верное. А старые записи удаляет git rerere gc: по умолчанию неразрешённые через 15 дней, разрешённые через 60.
Проверьте конфликты до слияния
Чтобы узнать, будет ли конфликт, не обязательно начинать слияние. Начиная с Git 2.38, git merge-tree --write-tree выполняет слияние, не трогая рабочее дерево и индекс:
git merge-tree --write-tree --name-only main feature/retry
echo $? # 0 = чисто, 1 = есть конфликты
С --name-only раздел конфликтов содержит только имена файлов. Документация Git советует опираться на код возврата, а не на пустой список файлов. Это удобное первое задание для агента, прежде чем он что-то тронет: «проверь, сливается ли feature/retry в main без конфликтов, а если нет — перечисли конфликтные файлы и что изменила каждая сторона».
Профилактика: меньше конфликтов с параллельными агентами
Самый дешёвый конфликт — тот, которого не случилось:
- Маленькие задачи. Агент, меняющий пять файлов, конфликтует реже, чем агент, меняющий пятьдесят. Делите крупные задачи по областям.
- Один владелец на область. Решите, какой агент может менять роутер, схему, общие типы, и пропишите это в инструкциях каждого агента.
- Часто сливайте или перебазируйте. Ветка, живущая час, конфликтует редко; ветка, живущая неделю, — почти всегда.
- Никаких правок по пути. Скажите агентам не переформатировать, не переименовывать и не чинить ничего за пределами их задачи.
- Одно изменение lock-файла за раз. Пусть зависимости добавляет только одна ветка, или перегенерируйте lock-файл после каждого слияния.
- Очередь слияния. Ветки попадают в основную по одной, на самую свежую вершину, с тестами на слитом результате, поэтому ловятся и семантические конфликты.
О том, как запускать агентов в отдельных папках, читайте в статье Git worktree для параллельных агентов.
Чем помогает AgentVera
AgentVera — десктопное приложение, которое запускает CLI агентов для кода, например Claude Code и Codex, бок о бок, со встроенным Git. С описанным выше процессом связаны такие функции:
- Worktree и Git-панель. Создайте worktree под каждую фичу и запустите в нём агента; индексируйте по файлам, коммитьте, делайте fetch, pull и push, читайте диффы в стиле GitHub, единым видом или в две колонки.
- Ранние предупреждения. По умолчанию AgentVera каждые 5 минут выполняет
git fetchв фоне. Когда в вашу ветку приходят новые коммиты, баннер предлагает fast-forward в один клик или rebase, если коммиты есть с обеих сторон. Когда два агента в одном проекте правят один и тот же файл, координация агентов предупреждает вас; для агентов в разных worktree она отмечает, что при слиянии возможен конфликт. - Владение файлами. Через владение файлами вы назначаете агенту пути или glob-шаблоны вроде
src/api/**. Пока он работает, агенты Claude Code останавливаются до правки этих файлов и получают указание спросить владельца; Codex и другие CLI получают правила в своих инструкциях. - Параллельная команда. Параллельная команда (Pro) делит задачу на части, каждая из которых работает в своём worktree и сливается в интеграционную ветку. Если слияние даёт конфликт или финальная проверка падает, подключается агент интеграции.
- Очередь слияния. Очередь слияния (Pro) сливает ветки по одной на свежую вершину целевой ветки. Конфликты разрешает агент Claude, проверка идёт на слитом результате с не более чем 2 раундами исправлений, а ветка, которую не удалось починить, возвращается. По умолчанию приземление ждёт вашего подтверждения.
Если вы работаете один и над одной веткой за раз, обычного Git с zdiff3 и rerere вполне достаточно. Эти инструменты окупаются, когда несколько агентов одновременно производят ветки.
Частые ошибки
- Разрешать без базы. Стиль конфликтов по умолчанию скрывает, каким код был раньше; включите
zdiff3. - «Исправь конфликты» как весь промпт. Не зная цели каждой ветки, агент выбирает сторону, которая выглядит полнее.
- Позволять агенту запускать
git addи--continue. Оставьте финальный шаг на послеgit diff --checkи тестов. - Сливать lock-файлы вручную. Разрешите манифест, затем перегенерируйте.
- Делать rebase общей ветки. Всем, кто строил работу на ней, придётся распутывать переписанные коммиты.
Итог
Разрешение конфликтов слияния с ИИ работает, когда агент видит всю картину, а доказательства остаются за вами: маркеры zdiff3 с базой, промпт, который называет обе цели и запрещает обходные пути, git diff --check и тесты до git add и rerere для повторяющихся конфликтов. Остальное делает профилактика: маленькие задачи, понятное владение файлами и ветки, которые приземляются по одной.
Чтобы попробовать это со своими агентами, скачайте AgentVera.