Пошук уроків, статей та іншого контенту
З’ясуєте причини merge-конфліктів і навчитеся знаходити файли, які потребують ручного втручання.
Під час злиття Git намагається об’єднати зміни з двох гілок автоматично. У більшості випадків це можливо, якщо гілки змінювали різні рядки або різні файли.
Merge-конфлікт виникає, коли Git не може однозначно визначити, яку зміну потрібно залишити. Тоді автоматичне злиття зупиняється і потребує ручного втручання.
Найчастіше конфлікт виникає, коли:
дві гілки змінили ті самі рядки одного файлу;
одна гілка змінила файл, а інша видалила його;
у двох гілках створено файли з однаковим шляхом;
файл було перейменовано або переміщено в одній гілці та змінено в іншій;
зміни стосуються бінарного файлу, який Git не може об’єднати як звичайний текст.
Важливо: конфлікт не означає, що репозиторій пошкоджений. Git просто зупинив операцію в стані, де рішення має прийняти розробник.
Для тристороннього злиття Git порівнює три версії:
спільний предок двох гілок;
поточний стан гілки, у яку виконується злиття;
стан гілки, яку зливають.
Наприклад, файл мав такий рядок у спільному предку:
timeout = 30У поточній гілці його змінили на:
timeout = 60А в іншій гілці — на:
timeout = 120Git не може визначити, яке значення правильне. У такій ситуації він позначає файл як конфліктний.
Якщо гілки змінюють різні частини файлу, Git часто об’єднує їх автоматично:
# Поточна гілка змінила цей рядок
color = "blue"
# Інша гілка змінила інший рядок
timeout = 60Однак автоматичне злиття не гарантує, що результат має правильну бізнес-логіку. Тому після успішного merge також варто перевіряти зміни.
Наведений приклад створює локальний репозиторій, дві гілки та навмисний конфлікт в одному рядку:
#!/usr/bin/env bash
set -e
REPO_DIR="$(mktemp -d)"
cd "$REPO_DIR"
git init -q
git config user.name "Lesson User"
git config user.email "lesson@example.com"
git branch -M main
cat > config.txt <<'EOF'
timeout = 30
mode = normal
EOF
git add config.txt
git commit -qm "Initial configuration"
git switch -q -c feature
sed -i.bak 's/timeout = 30/timeout = 60/' config.txt
rm config.txt.bak
git add config.txt
git commit -qm "Increase timeout in feature"
git switch -q main
sed -i.bak 's/timeout = 30/timeout = 120/' config.txt
rm config.txt.bak
git add config.txt
git commit -qm "Increase timeout in main"
# Git зупиниться через конфлікт, тому помилку команди ігноруємо
git merge feature || true
echo
echo "Репозиторій для перевірки: $REPO_DIR"
echo
echo "Стан репозиторію:"
git status --short
echo
echo "Конфліктні файли:"
git diff --name-only --diff-filter=UПісля виконання git merge feature Git повідомить про конфлікт у config.txt. Поточний стан файлу може виглядати так:
<<<<<<< HEAD
timeout = 120
=======
timeout = 60
>>>>>>> feature
mode = normalЦі маркери показують межі двох варіантів:
<<<<<<< HEAD — версія з поточної гілки;
======= — роздільник між версіями;
>>>>>>> feature — версія з гілки feature.
На цьому етапі merge ще не завершено.
git statusНайперше після повідомлення про конфлікт потрібно виконати:
git statusGit покаже, що злиття не завершене, і перелічить файли, які потребують ручного вирішення.
Скорочений варіант:
git status --shortДля конфліктного файлу можна побачити статус на кшталт:
UU config.txtПозначення UU означає, що файл має невирішені зміни з обох боків злиття:
перша літера стосується поточної гілки;
друга — гілки, яку зливають;
U означає unmerged, тобто «не об’єднано».
Інші корисні позначення:
AA — файл додано в обох гілках;
DD — файл видалено в обох гілках;
AU — файл додано в поточній гілці, але має невирішені зміни з іншої;
UA — файл має невирішені зміни в поточній гілці, але додано в іншій;
DU — файл видалено в поточній гілці, але змінено в іншій;
UD — файл змінено в поточній гілці, але видалено в іншій.
git diff --name-only --diff-filter=UЩоб отримати лише список файлів із невирішеними конфліктами, використовуйте:
git diff --name-only --diff-filter=UРезультат:
config.txtЦя команда особливо зручна в репозиторіях із великою кількістю файлів, оскільки не виводить сам вміст змін.
Пояснення параметрів:
--name-only виводить лише імена файлів;
--diff-filter=U залишає файли зі статусом U, тобто невирішені.
Альтернативний запис:
git diff --diff-filter=U --name-onlygit ls-files -uGit зберігає інформацію про всі конфліктні варіанти у своєму індексі. Переглянути її можна так:
git ls-files -uПриклад результату:
100644 9f1a... 1 config.txt
100644 3b72... 2 config.txt
100644 8c44... 3 config.txtНомери стадій означають:
1 — версія зі спільного предка;
2 — версія з поточної гілки;
3 — версія з гілки, яку зливають.
Ця команда корисна для діагностики, коли звичайного git status недостатньо. Поки файл має записи на стадіях 1, 2 або 3, конфлікт для нього ще не позначено як вирішений.
Для перегляду комбінованого diff можна використати:
git diffПід час merge Git покаже конфліктні ділянки та зміни, які ще не додано до індексу.
Для перегляду саме невирішених частин у комбінованому форматі:
git diff --ccЯкщо конфліктів багато, список файлів спочатку отримують так:
git diff --name-only --diff-filter=UА потім переглядають кожен файл окремо. Це допомагає відрізнити:
конфлікт, який справді потребує вибору між двома варіантами;
простий конфлікт видалення або перейменування;
конфлікт у файлі, де потрібно перевірити весь контекст, а не лише окремий рядок.
Після виникнення конфлікту репозиторій перебуває в особливому стані:
попередні коміти залишаються незмінними;
новий merge-коміт ще не створено;
конфліктні файли мають статус unmerged;
наступний merge виконати не можна, доки поточну операцію не завершено або не скасовано.
Перевірити, чи триває merge, можна за повідомленням у виводі:
git statusТипове повідомлення виглядає приблизно так:
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)Це означає, що Git очікує одного з двох рішень:
виправити конфлікти та завершити merge;
скасувати поточну операцію злиття.
Не кожна проблема після git merge є конфліктом.
Якщо git status показує:
M config.txtце означає, що файл змінено, але він не має невирішеного merge-конфлікту.
Статус:
?? debug.logозначає, що Git бачить новий невідстежуваний файл. Він не є результатом конфлікту, якщо поруч немає повідомлення про незавершене злиття.
Якщо Git повідомляє:
Already up to date.це означає, що додаткових комітів для злиття немає. Конфлікт у такій операції не виникає.
Це найвідоміший випадок. Обидві гілки змінили один і той самий фрагмент по-різному.
<<<<<<< HEAD
timeout = 120
=======
timeout = 60
>>>>>>> featureЯкщо обидві гілки створили settings.json, але з різним вмістом, Git не може автоматично вибрати один файл.
Такий конфлікт може мати статус:
AA settings.jsonОдна гілка видалила файл, а друга змінила його. Git потребує рішення: залишити змінений файл чи видалити його.
Можливий статус:
UD settings.jsonОдна гілка перейменувала файл, а інша змінила його в старому місці. Git може розпізнати перейменування автоматично, але інколи для цього потрібне ручне рішення.
Маркерів <<<<<<<, ======= і >>>>>>> може бути недостатньо для діагностики. Не всі конфлікти подаються як звичайні текстові блоки:
конфлікт перейменування;
конфлікт видалення;
конфлікт бінарних файлів.
Тому спочатку перевіряйте:
git statusgit diff без перевірки статусуgit diff показує багато різних змін і може ускладнити пошук конфліктних файлів. Для точного списку використовуйте:
git diff --name-only --diff-filter=UU та MСтатус M означає змінений файл. Статус U означає невирішений конфлікт. Файл із M не обов’язково потребує ручного вибору між версіями.
git commitПоки конфліктні файли залишаються у стані unmerged, merge не можна коректно завершити. Спочатку потрібно переконатися, що список команди:
git diff --name-only --diff-filter=Uне містить файлів.
git status після mergeНавіть якщо Git не повідомив про конфлікт, варто перевірити результат:
git statusЦе показує, чи merge завершився, чи залишилися файли, які потребують уваги.
Після отримання повідомлення про конфлікт виконайте:
git status
git diff --name-only --diff-filter=U
git diff --cc
git ls-files -uЦі команди допомагають послідовно:
визначити, чи триває merge;
отримати список конфліктних файлів;
переглянути контекст конфлікту;
перевірити записи невирішених версій у індексі.
Merge-конфлікт виникає, коли Git не може однозначно об’єднати зміни двох гілок.
Найчастіша причина — різні зміни одного рядка або однієї ділянки файлу.
Основна команда для перевірки стану — git status.
Щоб отримати лише конфліктні файли, використовуйте git diff --name-only --diff-filter=U.
Статус U означає unmerged, тобто файл ще не об’єднано.
git ls-files -u показує версії конфліктного файлу, збережені в індексі.
Поки є невирішені файли, merge залишається незавершеним.