# Как безопасно разрешать конфликты слияния с ИИ

URL: https://agentvera.dev/ru/blog/resolve-merge-conflicts-with-ai/ · Опубликовано: 2026-10-29

Разрешение конфликтов слияния с ИИ без потери работы: zdiff3, промпт для агента, тесты до git add, rerere, rebase или merge и профилактика конфликтов в Git.

**Коротко:**

- Сначала переключите merge.conflictStyle на zdiff3: тогда конфликт показывает общего предка, и вы вместе с агентом видите, что на самом деле изменила каждая сторона.
- Дайте агенту цель обеих веток, список конфликтных файлов и жёсткие правила: сохранить оба изменения, трогать только эти файлы, не оставлять маркеров и не подгонять тесты.
- Проверяйте до git add: git diff --check на забытые маркеры, затем проверка типов, тесты и сборка, а в конце вы сами читаете итоговый diff.
- Большинство конфликтов можно предотвратить: маленькие задачи, один владелец на область файлов, частый rebase на main и очередь слияния, которая тестирует каждую ветку на свежей вершине.

**Чтобы безопасно разрешать конфликты слияния с ИИ, сначала сделайте конфликт читаемым, затем дайте агенту для кода цели обеих сторон и чёткие правила, а результат принимайте только после того, как Git подтвердит, что маркеров конфликта не осталось, и тесты пройдут.** Агенты вроде Claude Code и Codex здесь действительно полезны: они читают обе версии, общего предка и окружающий код быстрее вас и умеют совмещать два изменения, а не выбирать одно. Их типичная ошибка так же очевидна: если цель одной стороны неясна, они оставляют ту сторону, которая компилируется, и молча выбрасывают другую.

В этом руководстве — почему возникают конфликты (и почему параллельные агенты делают их чаще), пошаговый процесс от `git status` до `git merge --continue`, шаблон промпта, rebase против merge, `git rerere`, проверка до слияния и способы избежать большинства конфликтов заранее. Команды Git сверены с официальной документацией на git-scm.com по состоянию на октябрь 2026 года.

## Откуда берутся конфликты слияния

Сливая две ветки, Git сравнивает обе с их общим предком. Изменения в разных местах файла объединяются автоматически. Если обе стороны изменили **одно и то же место**, Git не может выбрать: он останавливается и записывает в файл обе версии между маркерами конфликта. Это текстовый конфликт.

Самые частые виды:

- **Конфликт содержимого:** обе ветки изменили одни и те же строки одного файла.
- **Изменение/удаление:** одна ветка изменила файл, другая удалила или переместила его.
- **Генерируемые файлы:** lock-файлы, снапшоты, результаты сборки или пакеты переводов, которые перегенерировали обе ветки.

Есть и конфликт, которого Git не видит, — **семантический**. Две ветки трогают разные файлы, сливаются без единого конфликта и всё равно ломают друг друга: например, одна меняет сигнатуру функции, а другая добавляет новый её вызов. Поймать такое могут только тесты на слитом результате; в [статье об очереди слияния](/ru/blog/merge-queue-for-ai-agents/ "Очередь слияния для ИИ-агентов") это разобрано на примере.

### Почему с параллельными агентами конфликтов больше

Несколько агентов одновременно, каждый в своём [git worktree](/ru/glossary/git-worktree/ "Что такое git worktree"), не перезаписывают файлы друг друга во время работы. Но конфликт от этого не исчезает — он переносится на момент слияния. Каждый агент работает от той базы, с которой начал, и не видит, что изменили остальные. Некоторые файлы притягивают конфликты снова и снова:

- таблицы маршрутов, настройка внедрения зависимостей и другие реестры, куда каждая фича добавляет по строке,
- barrel-файлы (`index.ts`) и общие типы,
- `package.json` и его lock-файл,
- файлы переводов, changelog и конфиги.

Старательность агентов здесь тоже вредит: они переформатируют файл, который должны были лишь слегка задеть, «заодно» переименовывают хелпер или чинят посторонний линт-варнинг. Каждое такое действие расширяет область, которую изменили обе ветки.

## Шаг 1: остановитесь и посмотрите

Когда merge или rebase останавливается, сначала выясните, где вы находитесь, и только потом просите кого-либо, человека или агента, всё исправить:

```bash
git status                                  # какая операция, какие файлы "both modified"
git diff --name-only --diff-filter=U        # только неслитые файлы
```

`--diff-filter=U` выбирает неслитые пути, и вы получаете чистый список, который можно отдать агенту. Помните и о путях отступления. Если конфликт оказался больше, чем вы думали, ещё ничего не потеряно:

```bash
git merge --abort      # вернуться к состоянию до слияния
git rebase --abort     # вернуть ветку туда, где она была до rebase
```

Документация Git советует закоммитить или спрятать (stash) свои изменения перед `git merge`, потому что `--abort` не всегда может восстановить незакоммиченную работу. То же касается worktree агента: пусть он закоммитит работу, прежде чем вы что-то в него сольёте.

## Шаг 2: переключитесь на zdiff3, чтобы видеть базу

По умолчанию Git показывает только две стороны:

```text
<<<<<<< HEAD
const timeout = config.timeout ?? 30_000;
=======
const timeout = getTimeout(config);
>>>>>>> feature/retry
```

Отсюда не понять, какой была исходная строка, а значит, и чего хотела каждая сторона. Переключите стиль конфликтов на `zdiff3` (появился в Git 2.35; в более старых версиях используйте `diff3`):

```bash
git config --global merge.conflictStyle zdiff3
```

Теперь после маркера `|||||||` в конфликте виден и общий предок:

```text
<<<<<<< HEAD
const timeout = config.timeout ?? 30_000;
||||||| base
const timeout = config.timeout;
=======
const timeout = getTimeout(config);
>>>>>>> feature/retry
```

Теперь всё читается: одна сторона добавила значение по умолчанию, другая вынесла чтение в функцию. Правильное решение, скорее всего, сохраняет оба изменения — например, значение по умолчанию переезжает внутрь `getTimeout`. По документации git-config, `zdiff3` похож на `diff3`, но выносит совпадающие строки в начале и конце конфликта за его пределы, так что фрагменты остаются компактными.

Если слияние уже идёт, маркеры отдельного файла можно пересоздать в новом стиле:

```bash
git checkout --conflict=zdiff3 src/http/client.ts
```

Для агента это ещё важнее, чем для вас. Без базы модели приходится угадывать, какая сторона что изменила, а именно с угадывания начинается тихая потеря данных.

## Шаг 3: дайте агенту нужный контекст

Маркеры показывают строки. Но агенту нужно знать ещё и то, **зачем** каждая сторона их изменила. Git умеет показать историю обеих сторон для файла:

```bash
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 каждой ветки. Затем используйте примерно такой промпт:

```text
Мы посреди 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: проверьте, прежде чем продолжать

Слова агента «разрешено» — это утверждение, а не доказательство. Проверьте:

```bash
git diff --check                 # предупреждает о забытых маркерах конфликта (и ошибках пробелов)
git diff                         # просмотреть решение
pnpm typecheck && pnpm test      # или проверка вашего проекта
```

`git diff --check` завершается с ненулевым кодом, если изменения вносят маркеры конфликта, поэтому это дешёвый барьер, который стоит запускать каждый раз. В свежих версиях Git `git diff AUTO_MERGE` показывает только изменения, сделанные на данный момент для разрешения текстовых конфликтов, — то есть ровно то, что написал агент.

Затем завершите операцию сами:

```bash
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») запоминает, как вы разрешили конфликт, и повторяет это решение, когда тот же конфликт возникает снова:

```bash
git config --global rerere.enabled true
```

После включения `git merge` и `git commit` вызывают его автоматически. Полезные команды:

```bash
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` выполняет слияние, не трогая рабочее дерево и индекс:

```bash
git merge-tree --write-tree --name-only main feature/retry
echo $?    # 0 = чисто, 1 = есть конфликты
```

С `--name-only` раздел конфликтов содержит только имена файлов. Документация Git советует опираться на код возврата, а не на пустой список файлов. Это удобное первое задание для агента, прежде чем он что-то тронет: «проверь, сливается ли feature/retry в main без конфликтов, а если нет — перечисли конфликтные файлы и что изменила каждая сторона».

## Профилактика: меньше конфликтов с параллельными агентами

Самый дешёвый конфликт — тот, которого не случилось:

- **Маленькие задачи.** Агент, меняющий пять файлов, конфликтует реже, чем агент, меняющий пятьдесят. Делите крупные задачи по областям.
- **Один владелец на область.** Решите, какой агент может менять роутер, схему, общие типы, и пропишите это в инструкциях каждого агента.
- **Часто сливайте или перебазируйте.** Ветка, живущая час, конфликтует редко; ветка, живущая неделю, — почти всегда.
- **Никаких правок по пути.** Скажите агентам не переформатировать, не переименовывать и не чинить ничего за пределами их задачи.
- **Одно изменение lock-файла за раз.** Пусть зависимости добавляет только одна ветка, или перегенерируйте lock-файл после каждого слияния.
- **Очередь слияния.** Ветки попадают в основную по одной, на самую свежую вершину, с тестами на слитом результате, поэтому ловятся и семантические конфликты.

О том, как запускать агентов в отдельных папках, читайте в статье [Git worktree для параллельных агентов](/ru/blog/git-worktree-parallel-agents/ "Git worktree для параллельных агентов").

## Чем помогает AgentVera

AgentVera — десктопное приложение, которое запускает CLI агентов для кода, например Claude Code и Codex, бок о бок, со встроенным Git. С описанным выше процессом связаны такие функции:

- **Worktree и Git-панель.** Создайте worktree под каждую фичу и запустите в нём агента; индексируйте по файлам, коммитьте, делайте fetch, pull и push, читайте диффы в стиле GitHub, единым видом или в две колонки.
- **Ранние предупреждения.** По умолчанию AgentVera каждые 5 минут выполняет `git fetch` в фоне. Когда в вашу ветку приходят новые коммиты, баннер предлагает fast-forward в один клик или rebase, если коммиты есть с обеих сторон. Когда два агента в одном проекте правят один и тот же файл, [координация агентов](/ru/features/agent-coordination/ "Координация агентов") предупреждает вас; для агентов в разных worktree она отмечает, что при слиянии возможен конфликт.
- **Владение файлами.** Через [владение файлами](/ru/features/file-ownership/ "Владение файлами") вы назначаете агенту пути или glob-шаблоны вроде `src/api/**`. Пока он работает, агенты Claude Code останавливаются до правки этих файлов и получают указание спросить владельца; Codex и другие CLI получают правила в своих инструкциях.
- **Параллельная команда.** [Параллельная команда](/ru/features/parallel-team/ "Параллельная команда") (Pro) делит задачу на части, каждая из которых работает в своём worktree и сливается в интеграционную ветку. Если слияние даёт конфликт или финальная проверка падает, подключается агент интеграции.
- **Очередь слияния.** [Очередь слияния](/ru/features/merge-queue/ "Очередь слияния") (Pro) сливает ветки по одной на свежую вершину целевой ветки. Конфликты разрешает агент Claude, проверка идёт на слитом результате с не более чем 2 раундами исправлений, а ветка, которую не удалось починить, возвращается. По умолчанию приземление ждёт вашего подтверждения.

Если вы работаете один и над одной веткой за раз, обычного Git с `zdiff3` и `rerere` вполне достаточно. Эти инструменты окупаются, когда несколько агентов одновременно производят ветки.

## Частые ошибки

- **Разрешать без базы.** Стиль конфликтов по умолчанию скрывает, каким код был раньше; включите `zdiff3`.
- **«Исправь конфликты» как весь промпт.** Не зная цели каждой ветки, агент выбирает сторону, которая выглядит полнее.
- **Позволять агенту запускать `git add` и `--continue`.** Оставьте финальный шаг на после `git diff --check` и тестов.
- **Сливать lock-файлы вручную.** Разрешите манифест, затем перегенерируйте.
- **Делать rebase общей ветки.** Всем, кто строил работу на ней, придётся распутывать переписанные коммиты.

## Итог

Разрешение конфликтов слияния с ИИ работает, когда агент видит всю картину, а доказательства остаются за вами: маркеры `zdiff3` с базой, промпт, который называет обе цели и запрещает обходные пути, `git diff --check` и тесты до `git add` и `rerere` для повторяющихся конфликтов. Остальное делает профилактика: маленькие задачи, понятное владение файлами и ветки, которые приземляются по одной.

Чтобы попробовать это со своими агентами, [скачайте AgentVera](/ru/download/ "Скачать AgentVera").

## Частые вопросы

**Может ли ИИ-агент разрешать конфликты слияния в Git?** Да, и часто хорошо, если он видит общего предка (стиль zdiff3), знает, чего добивалась каждая ветка, и есть набор тестов, который подтвердит результат. Без этого он склонен оставить одну сторону и молча выбросить другую.

**Что такое стиль конфликтов zdiff3?** Значение merge.conflictStyle, которое, как и diff3, добавляет секцию ||||||| с исходным текстом общего предка, но выносит за пределы конфликта строки, одинаково изменённые обеими сторонами. Он появился в Git 2.35.

**Почему при rebase ours и theirs меняются местами?** Документация Git поясняет: во время git rebase и git pull --rebase --ours означает ветку, на которую вы перебазируетесь, а --theirs — вашу собственную работу, которая применяется заново. Уже поэтому массовое --ours или --theirs рискованно.

**Что делает git rerere?** При включённом rerere.enabled Git запоминает, как вы разрешили конфликт, и применяет это решение снова, когда тот же конфликт возникает повторно, например при очередном rebase. Индекс он не трогает, так что вы по-прежнему проверяете результат и делаете git add.

**Как узнать о конфликтах до слияния?** Запустите git merge-tree --write-tree --name-only main feature. Команда выполняет слияние, не трогая рабочее дерево и индекс, при конфликтах завершается с кодом 1 и выводит список конфликтных файлов. Нужен Git 2.38 или новее.

---

**Об AgentVera.** AgentVera — настольное приложение для macOS, Windows и Linux от Solviera Teknoloji. Оно запускает 18 ИИ-агентов для программирования (Claude Code, Codex, Gemini CLI, Copilot, Cursor, Antigravity, opencode, Hermes, Qwen Code, Kimi Code, Kiro, Amp, Droid, Goose, Vibe, Crush, Aider, Cline) рядом, по проектам, в настоящих терминалах с собственными установками CLI и входами пользователя. В бесплатном тарифе количество агентов не ограничено. Скачать: https://agentvera.dev/ru/download/ · Цены: https://agentvera.dev/ru/pricing/
