конфликты слияния
Узнайте о причинах конфликтов слияния в Git, маркерах конфликтов и способах их разрешения, отмены или предотвращения с помощью нужных команд.
Конфликт слияния возникает, когда Git не может автоматически согласовать два набора изменений. Это происходит, когда две ветки изменяют одни и те же строки одного и того же файла по-разному, или когда одна ветка удаляет файл, который другая ветка изменила. Поскольку Git не знает, какая версия правильная, он приостанавливает слияние и передаёт это решение вам.
На этой странице объясняется, почему возникают конфликты, как воспроизвести конфликт, как читать маркеры, которые Git вставляет в файлы, и различные способы разрешить, прервать или даже предотвратить конфликт. Команда git merge объединяет две ветки, и именно здесь чаще всего возникают конфликты.
Git справляется со слиянием лучше большинства систем контроля версий и автоматически интегрирует изменения, когда обе стороны затрагивают разные части файла. Конфликт — это не ошибка в вашем процессе работы: это Git просит вас принять решение. Когда он не может решить сам, он помечает файл как конфликтующий и останавливает слияние, чтобы ничего не было потеряно.

Когда возникают конфликты?
Не каждое слияние приводит к конфликту. Знание того, когда он вероятен, поможет вам избежать неожиданностей:
- Одни и те же строки изменены с обеих сторон — классический случай. Две ветки по-разному изменяют одну и ту же строку в
example.txt. - Файл изменён в одной ветке и удалён в другой — Git не может решить, сохранить изменения или выполнить удаление.
- Несоответствия пробелов, окончаний строк или кодировки — изменения, которые выглядят одинаково, всё равно могут конфликтовать, если байты различаются.
Когда две ветки затрагивают разные строки или разные файлы, Git объединяет их автоматически без каких-либо конфликтов.
Распространённые прерывания слияния
Git может остановить слияние в двух различных точках, и каждый из случаев требует разного подхода к исправлению. Полезно знать, с каким именно случаем вы столкнулись.
Сбой слияния при запуске
Git отказывается начинать слияние, когда незафиксированные изменения в рабочем каталоге или области индексирования будут перезаписаны входящими коммитами. Это не конфликт содержимого — Git защищает работу, которую вы ещё не зафиксировали. Чтобы взять под контроль своё локальное состояние, используйте git stash (отложить изменения), git commit (сохранить их), git checkout или git reset (отменить их), а затем снова выполните слияние. Сообщение выглядит примерно так:
error: Your local changes to the following files would be overwritten by merge:
example.txt
Please commit your changes or stash them before you merge.
AbortingСбой во время слияния
Сбой во время слияния означает, что Git начал объединять ветки, но столкнулся с реальным конфликтом содержимого между вашей текущей веткой и веткой, которую вы сливаете. Слияние остаётся незавершённым, чтобы вы могли его разрешить. Сообщение выглядит примерно так:
CONFLICT (content): Merge conflict in example.txt
Automatic merge failed; fix conflicts and then commit the result.Создание конфликта слияния
Вы можете воспроизвести конфликт во временном репозитории, чтобы безопасно потренироваться в его разрешении. Начните с создания репозитория с одним зафиксированным файлом:
mkdir test-dir
cd test-dir
git init .
echo "some content" > example.txt
git add example.txt
git commit -m "initial commit"
[master (root-commit) a45c22d] initial commit
1 file changed, 1 insertion(+)
create mode 100644 example.txtЭто создаёт каталог test-dir, инициализирует репозиторий и фиксирует example.txt со строкой some content. Теперь у нас есть одна ветка (master) и один файл. Далее создайте вторую ветку и измените ту же строку — именно это делает конфликт возможным:
git checkout -b branch_to_merge
echo "completely different content to merge later" > example.txt
git commit -m "edit the content of example.txt to make a conflict"
[branch_to_merge 4221135] edit the content of example.txt to make a conflict
1 file changed, 1 insertion(+), 1 deletion(-)git checkout -b branch_to_merge создаёт ветку и переключается на неё. Мы перезаписываем example.txt и фиксируем изменение, поэтому теперь в этой ветке другая версия этой строки. Вернитесь на master и измените тот же файл иначе:
git checkout master
Switched to branch 'master'
echo "content to add" >> example.txt
git commit -m "added content to example.txt"
[master 11ab34b] added content to example.txt
1 file changed, 1 insertion(+)Теперь обе ветки имеют собственный коммит, затрагивающий example.txt. Запустите слияние, и Git сообщит о конфликте, который не может разрешить самостоятельно:
git merge branch_to_merge
Auto-merging example.txt
CONFLICT (content): Merge conflict in example.txt
Automatic merge failed; fix conflicts and then commit the result.Определение конфликтов слияния
Помимо сообщения, выводимого при слиянии, команда git status показывает, какие именно файлы конфликтуют, перечисляя их в разделе Unmerged paths:
git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: example.txtboth modified означает, что файл изменён с обеих сторон. Откройте файл или выполните cat, чтобы увидеть маркеры конфликта, вставленные Git:
cat example.txt
<<<<<<< HEAD
some content
content to add
=======
completely different content to merge later
>>>>>>> branch_to_mergeЧитайте три маркера следующим образом:
<<<<<<< HEAD— начало конфликта. Всё, что ниже до=======, — это содержимое вашей текущей ветки (master, на которую указываетHEAD).=======— разделительная линия между двумя версиями.>>>>>>> branch_to_merge— конец конфликта. Строки между=======и этим маркером взяты из ветки, которую вы сливаете.
Чтобы полностью отменить слияние и вернуться к точному состоянию до его начала, выполните git merge --abort. Подробнее о маркерах можно прочитать на странице git merge.
Разрешение конфликтов слияния
Разрешение вручную
Откройте конфликтующий файл в редакторе, решите, каким должно быть итоговое содержимое, и удалите все три маркера (<<<<<<<, =======, >>>>>>>). Вы можете сохранить одну из сторон, другую или вручную объединить обе. Например, сохранение обоих частей содержимого даёт:
some content
content to add
completely different content to merge laterКогда маркеры удалены и содержимое правильно, добавьте файл в индекс с помощью git add и создайте коммит для завершения слияния:
git add example.txt
git commit -m "resolve merge conflict in example.txt"Выполнение git commit после конфликта создаёт коммит слияния, который связывает историю обеих веток.
Разрешение путём выбора одной стороны
Когда вы просто хотите сохранить версию файла из одной ветки целиком, вам не нужно редактировать вручную. Извлеките нужную сторону, затем добавьте в индекс и создайте коммит:
git checkout --ours example.txt # keep the version from the current branch (master)
git checkout --theirs example.txt # keep the version from the incoming branch
git add example.txt--ours сохраняет содержимое вашей текущей ветки; --theirs сохраняет содержимое входящей ветки.
Разрешение с помощью визуального инструмента
Для крупных конфликтов git mergetool открывает настроенный инструмент сравнения бок о бок, чтобы вы могли разрешать конфликты в интерактивном режиме, а не редактировать маркеры вручную. Запустите git mergetool, чтобы пройтись по всем конфликтующим файлам, или git mergetool example.txt для конкретного файла.
Прерывание слияния
Если вы решили, что слияние было ошибкой, или хотите начать заново с чистым рабочим деревом, прервите его:
git merge --abortЭто восстанавливает вашу ветку и рабочий каталог до точного состояния до запуска git merge — без маркеров, без полуслитых файлов. Используйте это, когда конфликт слишком сложен, чтобы разбираться с ним прямо сейчас.
Предотвращение конфликтов слияния
Конфликты — это нормально, но вы можете сократить их частоту и серьёзность:
- Часто выполняйте слияние или rebase из основной ветки, чтобы ваша ветка не уходила далеко от неё.
- Делайте коммиты небольшими и целенаправленными и избегайте масштабного форматирования в том же изменении, что и правки логики.
- Общайтесь, чтобы два человека не переписывали один и тот же файл одновременно.
- Просматривайте пересечения с помощью git diff перед слиянием, чтобы увидеть, какие строки могут столкнуться.
Шпаргалка по командам
Это команды, к которым чаще всего обращаются при работе с конфликтами:
| Инструмент | Описание |
|---|---|
git status | Помогает найти конфликтующие файлы. |
git mergetool | Открывает визуальный инструмент сравнения для интерактивного разрешения конфликтов. |
git diff | Показывает различия между коммитами, ветками или файлами, помогая выявить потенциальные конфликты до слияния. |
git checkout --ours/--theirs | Заменяет конфликтующий файл содержимым из текущей или входящей ветки. |
git reset --mixed | Убирает файлы из индекса, оставляя рабочий каталог без изменений. |
git merge --abort | Прерывает текущее слияние и восстанавливает рабочий каталог до состояния до начала слияния. |
git reset | Сбрасывает индекс до состояния HEAD, помогая убрать конфликтующие файлы из индекса. |