Глиф Учебники

Урок 7. Конфликты слияния и как их решать

Курс «Git» · урок 7 из 9 · 4 минуты чтения

Конфликт возникает, когда две ветки изменили одно и то же место в файле по-разному, и Git не может сам решить, какой вариант верный. Это не ошибка и не поломка: Git останавливает слияние и просит человека выбрать. Конфликт бывает при git merge, при git pull (внутри него тоже слияние) и при git rebase (переносе коммитов на другую ветку).

Как получается конфликт

В файле page.txt первая строка — Заголовок: Привет. В ветке title её заменили на Заголовок: Добро пожаловать, а в main в это же время — на Заголовок: Здравствуйте. Слияние:

Терминал
$ git merge title
Auto-merging page.txt
CONFLICT (content): Merge conflict in page.txt
Automatic merge failed; fix conflicts and then commit the result.
page.txt — было Заголовок: Привет в ветке main Заголовок: Здравствуйте в ветке title Заголовок: Добро пожаловать git merge title CONFLICT одна строка — два варианта Git не знает, какой вариант верный, и останавливает слияние
Строку «Заголовок: Привет» в main заменили на «Здравствуйте», а в title — на «Добро пожаловать», поэтому git merge title останавливается с конфликтом.

Git слил всё, что смог, и остановился на page.txt. Слияние не закончено — git status это показывает:

Терминал
$ git status
On branch main
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:   page.txt

no changes added to commit (use "git add" and/or "git commit -a")

both modified — файл изменён в обеих ветках, и изменения столкнулись.

Как читать файл с конфликтом

Git записал в файл оба варианта и окружил их метками:

Текст
<<<<<<< HEAD
Заголовок: Здравствуйте
=======
Заголовок: Добро пожаловать
>>>>>>> title
Текст
<<<<<<< HEAD Заголовок: Здравствуйте ======= Заголовок: Добро пожаловать >>>>>>> title Текст вариант текущей ветки main — в неё сливаете вариант ветки title её сливаете в main не конфликтует — остаётся Красные строки-метки — удалить все три перед git add
Между <<<<<<< HEAD и ======= стоит вариант текущей ветки main, между ======= и >>>>>>> title — вариант ветки title, а три строки-метки перед git add удаляют.

Решите конфликт

  1. Откройте файл в редакторе.
  2. Оставьте правильный вариант: один из двух, оба или новый, собранный из обоих.
  3. Удалите все три строки-метки: <<<<<<<, ======= и >>>>>>>.
  4. Сохраните файл и отметьте конфликт решённым командой git add.
  5. Когда все файлы решены — завершите слияние коммитом.

Например, оставили Заголовок: Добро пожаловать:

Терминал
$ git add page.txt
$ git status
On branch main
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
    modified:   page.txt

$ git commit --no-edit
[main 0a62421] Merge branch 'title'

--no-edit берёт готовое сообщение Merge branch 'title'. Без него Git откроет редактор с этим сообщением — сохраните и закройте.

Слияние остановлено You have unmerged paths. git merge --abort всё как до слияния поправить page.txt, затем git add page.txt Конфликт решён, слияние не закончено All conflicts fixed but you are still merging. git commit --no-edit Слияние завершено [main 0a62421] Merge branch 'title'
Остановленное слияние после правки файла и git add становится решённым, но ещё не законченным, и только git commit завершает его, а git merge --abort возвращает всё как до слияния.

Не делайте коммит, пока в файлах остались метки <<<<<<< и >>>>>>>: Git примет их как обычный текст, и в историю попадёт сломанный файл — программа перестанет запускаться. Забытые метки находит git diff --check до git add и git diff --staged --check после него.

Взять целиком одну сторону

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

Bash
git checkout --ours page.txt
git checkout --theirs page.txt

Первая оставляет версию текущей ветки, вторая — сливаемой. После неё — git add page.txt, как обычно. То же делают git restore --ours и git restore --theirs.

При git rebase стороны меняются местами: ours — ветка, на которую переносятся коммиты, а theirs — ваши переносимые коммиты. Если сомневаетесь, откройте файл и выберите вариант глазами.

Передумали — отмените слияние

Bash
git merge --abort

Всё вернётся к состоянию до git merge, как будто слияния не было. Это безопасный выход, если конфликт оказался сложнее, чем казалось, и нужно сначала разобраться или спросить автора другой ветки.

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

Редакторы кода подсвечивают конфликты и дают кнопки выбора. Например, Visual Studio Code показывает над конфликтом «Accept Current Change» (свой вариант), «Accept Incoming Change» (вариант другой ветки) и «Accept Both Changes» (оба). Нажатие кнопки только правит файл — git add и коммит всё равно нужны.

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

Коротко

Проверьте себя

1. Когда возникает конфликт слияния?
2. Что находится между <<<<<<< HEAD и =======?
3. Файл с конфликтом исправлен и сохранён. Что дальше?
4. Как отменить слияние, которое закончилось конфликтом?