# Конфлікти злиття (merge conflict): чому виникають і що робити

> Що таке merge conflict на GitHub і чому дві гілки можуть зіткнутися на тому самому рядку, що означає «This branch has conflicts that must be resolved», як читати маркери конфлікту й розв'язати простий конфлікт у браузері і чому вибір правильного тексту це продуктове рішення.

- Курс: [Git і GitHub для продактів](https://productovi.com/uk/courses/git-for-product.md)
- Модуль: Гілки та мерджі
- Урок 6/15, ~34 хв
- Джерело: https://productovi.com/uk/courses/git-for-product/merge-conflicts

## Ще одна сіра кнопка

**Alex Tutor:** Звичайний день у продуктовій команді Котокорму, аж поки не приходить повідомлення від Emma.

**Sofia:** Мені щойно в паніці написала Emma. Її pull request #132 каже «This branch has conflicts that must be resolved» («у цій гілці є конфлікти, які треба розв'язати»), а кнопка мерджу сіра. Emma думає, що зламала сайт.

**Alex Tutor:** Нічого не зламано. Конфлікт блокує один мердж, і більше нічого: `main`, живий сайт і робота Emma лишаються такими, як були. Pull request просто чекає, доки хтось ухвалить рішення.

**Alex Tutor:** Ось як зараз виглядають два pull requests про доставку. Повідомлення подано так, як його формулює GitHub станом на 2026 рік.

```text
Pull requests про доставку в catchow/website
- [Open] Rewrite delivery page copy #132: This branch has conflicts that must be resolved: мердж заблоковано. Конфліктний файл: pages/delivery.md
- [Merged] Raise free-delivery threshold to 1000 UAH #133: Змерджено вчора як коміт 6f2c8a1
```

**Sofia:** #133 це зміна порогу від Daniel з минулого уроку. Один із його комітів називався «Update delivery page text».

**Daniel:** Ось і зіткнення. Я змінив на тій сторінці одне речення: рядок про безкоштовну доставку. Emma переписала всю сторінку, і це речення теж. Git отримав дві різні версії одного рядка й не став угадувати.

> **Вправа:** Pull request #132 показує «This branch has conflicts that must be resolved». Це означає, що сторінка доставки на живому сайті зараз зламана.

## «This branch has conflicts that must be resolved»: чому Git зупинився

**Alex Tutor:** Ось як Git мерджить. Він знаходить коміт, де дві лінії роботи розійшлися, тобто їхню спільну точку відліку, і порівнює з нею кожну зі сторін.

**Alex Tutor:** Якщо якесь місце змінила лише одна сторона, Git бере цю зміну. Якщо сторони змінили різні файли або різні частини одного файлу, Git об'єднує їх сам. Так минає більшість мерджів.

```text
Граф комітів, від найстарішого: content/delivery-copy і main сьогодні
1. коміт e7c3b19 у main "Add summer promo banner"
2. гілка content/delivery-copy починається від коміту e7c3b19
3. коміт f02a6d4 у main "Update FAQ answers"
4. коміт e19c3b7 у content/delivery-copy "Rewrite delivery page copy" (виділено)
5. коміт 8be2f70 у main "Add cat treats bundle"
6. коміт 6f2c8a1 у main "Raise free-delivery threshold to 1000 UAH (#133)" (виділено)
```

*Обидві лінії починаються від e7c3b19. Кожен виділений коміт по-своєму змінив рядок 12 у pages/delivery.md. Тепер гілка на 1 коміт ahead і на 3 behind.*

**Alex Tutor:** **Конфлікт** (merge conflict) виникає, лише коли обидві сторони по-різному змінили ті самі рядки або сусідні. Для такого випадку правила немає: яка версія переможе, це рішення, а рішення Git залишає людям.

> **Рядок 12 у `pages/delivery.md`: три версії**
>
> ```text
> e7c3b19, спільна точка:  Free delivery on orders from 800 UAH.
> main, після #133:        Free delivery on orders from 1000 UAH.
> content/delivery-copy:   Orders from 800 UAH ship free, right to your door.
> ```
> Обидві гілки змінили той самий рядок після спільної точки, тож Git зупиняється і питає людину.

**Sofia:** Тобто Daniel змінив число, а Emma слова. І ніхто з них нічого не зробив неправильно.

**Daniel:** Саме так. А тепер порівняй із моєю історією про кнопку в березні. Моя гілка й панель cookie змінювали різні файли, тож Git змерджив їх мовчки, а результат виявився зламаним.

**Alex Tutor:** Запам'ятай обидві половини. Відсутність конфлікту ще не означає, що результат правильний. А конфлікт не означає, що хтось помилився.

![Зміни в різних файлах чи частинах Git об'єднує сам; конфліктом стають лише ті самі рядки, змінені по-різному, — питання, яке вирішує людина.](https://productovi.com/courses/git-for-product/diagrams/m2-l3-when-git-stops-uk.png)

![Двоє людей тягнуться різними шматочками пазла до того самого порожнього місця й зупиняються, дивлячись одне на одного.](https://productovi.com/courses/git-for-product/inline/merge-conflicts-illustration-2.webp)

*Конфлікт — не помилка: це означає, що обидві сторони змінили ті самі рядки.*

> **Вправа:** Відколи гілка Emma відділилася від `main`, обидві сторони щось змінили. У яких випадках GitHub повідомить про конфлікт? Познач усе, що підходить.

**Alex Tutor:** А тепер друга половина березневого уроку Daniel, як швидка перевірка.

> **Вправа:** Два pull requests змерджили в `main` один за одним, і GitHub не повідомив про конфлікт: один змінив кнопку оформлення замовлення, інший додав панель cookie в іншому файлі. Який висновок правильний?

## Читаємо маркери конфлікту

**Alex Tutor:** Повернімося до #132. Під повідомленням про конфлікт є кнопка `Resolve conflicts` («розв'язати конфлікти»). Вона відкриває простий редактор просто на GitHub, уже з конфліктним файлом.

**Sofia:** Emma саме показує свій екран і натискає її. Схоже на звичайний текстовий файл із дивними стрілками посередині.

> **`pages/delivery.md` у редакторі конфліктів GitHub**
>
> ```text
> ## Delivery
>
> <<<<<<< content/delivery-copy
> Orders from 800 UAH ship free, right to your door.
> =======
> Free delivery on orders from 1000 UAH.
> >>>>>>> main
>
> Couriers deliver within one day in Kyiv.
> ```
> Версія з гілки Emma стоїть над розділювачем, версія з `main` під ним. Заголовок і рядок про кур'єрів лежать поза маркерами: там вирішувати нічого.

**Alex Tutor:** Це **маркери конфлікту**. `<<<<<<<` відкриває конфлікт і називає гілку, з якої прийшла верхня версія. `=======` розділяє дві версії. `>>>>>>>` закриває конфлікт і називає другу гілку.

**Alex Tutor:** Усе поза маркерами вже узгоджено. На людину чекає лише частина між стрілками. А самі маркери не текст для сторінки: їх треба прибрати.

**Daniel:** Розробники бачать точнісінько такі маркери у своєму редакторі коду, коли запускають `git merge` у терміналі. Ті самі стрілки, інше вікно.

**Sofia:** По сім символів з кожного боку. Хтось дуже хотів, щоб їх неможливо було не помітити.

> **Вправа:** Зістав кожну частину конфлікту в `pages/delivery.md` з тим, що вона означає.

## Питання про намір

**Sofia:** Перший порив Emma: залишити свою версію. Зрештою, це її pull request.

**Alex Tutor:** Тоді сторінка обіцятиме безкоштовну доставку від 800 грн, а кошик робитиме її безкоштовною лише від 1000. Клієнти платитимуть за доставку, яку сторінка назвала безкоштовною.

**Sofia:** А якщо залишити рядок Daniel, пропаде новий текст Emma. Отже, правильна відповідь ні та, ні інша: слова Emma з числом Daniel.

**Alex Tutor:** У цьому суть уроку. Конфлікт не помилка, а питання: що мав на увазі кожен і що тепер має бути на сторінці? Git на це не відповість. А ти можеш.

![Двоє колег за столом: одна тримає маленький помаранчевий цінник, інший щойно переписану сторінку, і разом вони прикріплюють цінник до нової сторінки.](https://productovi.com/courses/git-for-product/inline/merge-conflicts-illustration-3.webp)

**Daniel:** Чесно кажучи, саме тут мені потрібен продакт. Технічно я розв'яжу будь-який конфлікт. Але я не знаю, чи фінвідділ знову не передумав і яке формулювання погодив маркетинг.

**Alex Tutor:** У редакторі можна залишити одну сторону, обидві або написати щось нове. Тут потрібне нове: один рядок, зібраний з обох.

> **Вправа:** Конфлікт у `pages/delivery.md`:
>
> ```text
> <<<<<<< content/delivery-copy
> Orders from 800 UAH ship free, right to your door.
> =======
> Free delivery on orders from 1000 UAH.
> >>>>>>> main
> ```
>
> Поріг підняли до 1000 грн у #133 на прохання фінвідділу, а маркетинг погодив нове формулювання Emma. Чим треба замінити весь блок разом із маркерами?

## Як розв'язати merge conflict на GitHub

**Alex Tutor:** У редакторі це три кроки: виправити текст, натиснути `Mark as resolved`, потім `Commit merge`. Спершу Emma замінює весь блок погодженим рядком: «Orders from 1000 UAH ship free, right to your door.» Три рядки з маркерами теж зникають.

**Daniel:** Перевір це двічі. У терміналі ніщо не завадить закомітити забутий `=======`, і тоді він з'явиться на живій сторінці. Я таке бачив. В інших людей, звісно.

**Alex Tutor:** Коли файл чистий, кнопка `Mark as resolved` («позначити як розв'язане») повідомляє GitHub, що з цим файлом усе вирішено. Якщо конфліктних файлів кілька, наступний вибирають у списку ліворуч і повторюють те саме.

**Sofia:** Тут він лише один. Тож остання кнопка `Commit merge` («закомітити мердж»).

**Alex Tutor:** Так. Щойно Emma її натисне, перевір результат так, як це зробив би рев'юер: вкладка `Files changed` («змінені файли») у pull request має показувати лише переписаний текст Emma: у рядку 12 стоїть 1000, і жодного сліду 800.

> **Вправа:** Розташуй по порядку кроки, щоб розв'язати конфлікт у #132 на GitHub.

**Alex Tutor:** Застереження Daniel про забуті маркери варте окремої перевірки.

> **Вправа:** Якщо під час розв'язання конфлікту хтось забуде видалити рядок `=======`, Git прибере його автоматично, коли мердж закомітять.

## Куди потрапляє розв'язання

**Sofia:** А що саме комітить `Commit merge`? І куди?

**Alex Tutor:** Мердж-коміт у гілці Emma, а не в `main`. GitHub зливає `main` у `content/delivery-copy`, і твоє рішення всередині. Станом на 2026 рік його повідомлення таке: «Merge branch 'main' into content/delivery-copy».

```text
Граф комітів, від найстарішого: Після Commit merge
1. коміт e7c3b19 у main "Add summer promo banner"
2. гілка content/delivery-copy починається від коміту e7c3b19
3. коміт f02a6d4 у main "Update FAQ answers"
4. коміт e19c3b7 у content/delivery-copy "Rewrite delivery page copy"
5. коміт 8be2f70 у main "Add cat treats bundle"
6. коміт 6f2c8a1 у main "Raise free-delivery threshold to 1000 UAH (#133)"
7. мердж-коміт c90d4e7: main змерджено в content/delivery-copy "Merge branch 'main' into content/delivery-copy" (виділено)
```

*Мердж іде з main у гілку. Тепер у content/delivery-copy є все з main плюс новий текст Emma; сама main не змінилася.*

**Sofia:** Тобто тепер гілка на нуль behind, і повідомлення про конфлікт зникло.

**Daniel:** І кнопка мерджу повернулася, чекає на моє рев'ю. Оскільки ми робимо squash, цей мердж-коміт в історію `main` не потрапить: `main` отримає один чистий коміт для #132.

**Alex Tutor:** І ще одне, що варто прочитати на цьому графі: у який бік іде мердж. Розв'язання конфлікту на GitHub оновлює гілку pull request. Сайт зміниться лише тоді, коли змерджать сам pull request.

> **Вправа:** Хтось розв'язав конфлікт у pull request #145 у редакторі GitHub. Про що говорить виділений коміт b2?

## Хто розв'язує і коли браузера замало

**Alex Tutor:** Вебредактор уміє розв'язувати один вид конфліктів: різні правки тих самих рядків текстового файлу. Цього вистачає для більшості конфліктів у текстах.

**Alex Tutor:** Інші види потребують командного рядка: скажімо, одна гілка видалила файл, який інша редагувала, або обидві замінили те саме зображення. Станом на 2026 рік GitHub тоді вимикає `Resolve conflicts` і пише, що конфлікт надто складний для вебредактора.

**Daniel:** Це моя робота, не твоя. Але я однаково прийду до тебе з тим самим питанням: яким має бути результат?

**Alex Tutor:** То хто ж розв'язує? Зазвичай автор заблокованого pull request разом з автором зміни, з якою сталося зіткнення. Продакт приносить відповідь на питання «що тут має бути написано?».

**Sofia:** Отже, моя частина: з'ясувати, що мала на увазі кожна сторона, погодити результат, записати рішення в pull request і потім перевірити `Files changed`.

**Alex Tutor:** А чого не робити: вибирати сторону, аби кнопка стала зеленою, розв'язувати конфлікти в коді чи конфігурації, яких не розумієш, і редагувати чужу гілку, не спитавши.

> **Вправа:** Конфлікт у тексті блокує pull request для сторінки, за яку ти відповідаєш. Що з цього входить у роботу продакта? Познач усе, що підходить.

## Як мати менше конфліктів

**Alex Tutor:** Чому цей конфлікт узагалі стався? Гілка Emma жила понад тиждень і переписала цілу сторінку. А `main` тим часом пішла вперед без неї.

**Sofia:** Уже три коміти behind. Минулого разу на сторінці гілок було два.

**Daniel:** Кожен із цих комітів був шансом на зіткнення. Що довше живе гілка і що більше вона зачіпає, то більше змінюється `main` у неї під ногами.

**Alex Tutor:** Тому цей модуль весь час повторює ті самі звички: маленькі гілки, одна задача на гілку, мердж за кілька днів. А в довшу гілку варто рано підтягувати свіжу `main`, поки конфлікти ще дрібні.

**Alex Tutor:** І розмовляйте. Один рядок у командному чаті, «цього тижня я переписую сторінку доставки», і Daniel написав би Emma, перш ніж чіпати те речення.

![За сусідніми столами колега нахиляється й коротко щось каже, показуючи на ту саму сторінку на обох екранах.](https://productovi.com/courses/git-for-product/inline/merge-conflicts-illustration-4.webp)

**Sofia:** А це теж робота продакта: знати, що фінвідділ і маркетинг змінюють ту саму сторінку того самого тижня.

> **Вправа:** Який із цих pull requests найімовірніше натрапить на конфлікт?

## Залиш пояснення по собі

**Alex Tutor:** Останнє завдання. Завтра Daniel робитиме рев'ю #132 і побачить рядок, який не збігається ні з його версією, ні з початковою версією Emma.

**Alex Tutor:** Напиши коментар до pull request: що тепер у рядку 12 і чому. Два-три речення.

**Daniel:** Згадай, звідки взялося 1000. Через рік хтось запустить blame на цьому рядку, дійде до #132 і потрапить просто на твій коментар.

**Sofia:** Записка для наступного детектива. Я добре знаю, як це буває.

> **Вправа:** Завтра Daniel робитиме рев'ю pull request #132. Рядок 12 у `pages/delivery.md` тепер такий: «Orders from 1000 UAH ship free, right to your door.» Він не збігається ні з версією Daniel, ні з початковою версією Emma. Напиши коментар до #132 на два-три речення: що в цьому рядку після розв'язання конфлікту і чому.

**Alex Tutor:** Daniel схвалив, Emma змерджила, і тепер на сторінці доставки написано «Orders from 1000 UAH ship free, right to your door.» Це був модуль 2: гілки, мерджі й конфлікти між ними.

**Sofia:** А виправлення кнопки? #134 уже давно висить чернеткою.

**Daniel:** Майже готове до рев'ю. Наступний модуль повністю про pull requests, і ти прочитаєш мій pull request вкладку за вкладкою.

- Попередній урок: [Злиття (git merge): мердж-коміт, squash і rebase на GitHub](https://productovi.com/uk/courses/git-for-product/merging.md)
- Наступний урок: [Що таке pull request: анатомія сторінки PR](https://productovi.com/uk/courses/git-for-product/pull-request-anatomy.md)

---
Productovi Learning. Безкоштовно, читати можна без реєстрації.
