Пошук уроків, статей та іншого контенту
З’єднаєте історії гілок, розглянете fast-forward і навчитеся розпізнавати конфлікти під час злиття.
git mergegit merge об’єднує зміни з однієї гілки в іншу. Команда не «перемикається» на гілку автоматично: злиття відбувається в поточну гілку.
Наприклад, щоб додати зміни з feature до main:
git switch main
git merge featureПісля цього поточна гілка main міститиме зміни з feature.
поточну гілку — гілка, у яку додаються зміни;
вказану гілку — джерело змін, наприклад feature;
результат злиття — новий стан історії, іноді з окремим merge-комітом.
Перевірити поточну гілку можна командою:
git branch --show-currentУявімо, що є основна гілка main і гілка feature/login:
A---B main
\
C---D feature/loginЩоб додати функціональність із feature/login до main, потрібно:
git switch main
git merge feature/loginЯкщо злиття виконано успішно, Git повідомить про результат. Після цього історія може виглядати так:
A---B---C---D main, feature/loginАбо так:
A---B---E main
\ /
C-D feature/loginПерший варіант називається fast-forward, другий — злиттям із merge-комітом.
Fast-forward можливий, коли поточна гілка не має власних нових комітів після створення іншої гілки.
Початкова історія:
A---B main
\
C---D featureГілка main просто вказує на коміт B, який є предком коміту D. Щоб виконати злиття, Git може пересунути вказівник main уперед:
A---B---C---D main, featureНовий merge-коміт не створюється, адже Git не поєднує дві різні лінії історії. Він лише пересуває вказівник гілки.
git switch main
git merge featureТипове повідомлення Git:
Updating 1a2b3c4..5d6e7f8
Fast-forwardУ такій історії немає окремого коміту, який позначає саме факт злиття.
Іноді потрібно зберегти у журналі факт об’єднання гілок, навіть якщо можливий fast-forward. Для цього використовують --no-ff:
git switch main
git merge --no-ff featureТоді Git створить окремий merge-коміт:
A---B-------M main
\ /
C---D featureОпція --no-ff не змінює самі зміни у файлах. Вона впливає на форму історії.
Fast-forward неможливий, якщо поточна гілка теж має нові коміти.
Початкова історія:
C---D feature
/
A---B
\
E---F mainУ цьому випадку main і feature розвиваються окремо. Git створює merge-коміт:
C---D feature
/ \
A---B---E---F---M main
\ /
...У спрощеному вигляді:
C---D
/ \
A---B---E---M mainMerge-коміт має двох батьків: один із main, інший із feature.
Команда залишається тією самою:
git switch main
git merge featureЯкщо конфліктів немає, Git може автоматично створити merge-коміт. Повідомлення коміту часто має вигляд:
Merge branch 'feature' into mainНижче наведено сценарій, який можна виконати в новій папці:
mkdir merge-demo
cd merge-demo
git init
git config user.name "Developer"
git config user.email "developer@example.com"
printf "Проєкт\n" > README.md
git add README.md
git commit -m "Створити README"
git switch -c feature
printf "Форма входу\n" > login.txt
git add login.txt
git commit -m "Додати форму входу"
git switch main
git merge feature
git log --oneline --graph --allУ цьому прикладі гілка main не змінилася після створення feature, тому Git, найімовірніше, виконає fast-forward.
Після злиття файл login.txt буде доступний і в main.
Для перегляду історії з гілками зручно використовувати:
git log --oneline --graph --decorate --allПояснення опцій:
--oneline скорочує інформацію про коміти;
--graph показує лінії гілок;
--decorate показує назви гілок і тегів;
--all показує історію всіх локальних гілок.
Поточний стан робочого дерева можна перевірити так:
git statusПісля успішного злиття Git зазвичай повідомляє:
чи був використаний fast-forward;
які файли змінено;
чи створено merge-коміт;
чи виникли конфлікти.
Конфлікт виникає, коли Git не може автоматично визначити, яку версію змін залишити.
Найтиповіший випадок — однаковий фрагмент одного файлу було змінено в обох гілках:
A---B---C main
\
D---E featureЯкщо C і E змінюють ті самі рядки, Git зупиняє злиття й залишає файл у конфліктному стані.
Конфлікт не означає, що репозиторій пошкоджено. Це запит до розробника: потрібно вибрати або об’єднати правильні варіанти змін.
Створимо репозиторій із конфліктними змінами:
mkdir conflict-demo
cd conflict-demo
git init
git config user.name "Developer"
git config user.email "developer@example.com"
printf "Заголовок сторінки\n" > page.txt
git add page.txt
git commit -m "Додати заголовок"
git switch -c feature
printf "Заголовок із форми\n" > page.txt
git add page.txt
git commit -m "Оновити заголовок у feature"
git switch main
printf "Заголовок із меню\n" > page.txt
git add page.txt
git commit -m "Оновити заголовок у main"
git merge featureGit повідомить про конфлікт у page.txt. Його вміст буде схожим на такий:
<<<<<<< HEAD
Заголовок із меню
=======
Заголовок із форми
>>>>>>> featureМаркери означають:
<<<<<<< HEAD — початок версії з поточної гілки main;
======= — роздільник між двома версіями;
>>>>>>> feature — кінець версії з гілки feature.
Ці маркери не є частиною потрібного тексту. Їх потрібно видалити та залишити правильний результат.
Наприклад, якщо заголовок має бути об’єднаним:
Заголовок із меню та формиПісля редагування файл треба додати до індексу:
git add page.txtПотім завершити злиття комітом:
git commitGit відкриє редактор із підготовленим повідомленням merge-коміту. Його можна залишити без змін або відредагувати.
Також можна одразу вказати повідомлення:
git commit -m "Об'єднати зміни main і feature"Загальний алгоритм:
Перевірити стан репозиторію:
git statusЗнайти файли з конфліктами.
Відкрити кожен такий файл.
Видалити маркери конфлікту й залишити правильний вміст.
Позначити виправлений файл:
git add path/to/fileПеревірити, що всі конфлікти розв’язані:
git statusЗавершити злиття:
git commitПісля git add Git вважає файл розв’язаним лише з технічного погляду. Він не перевіряє, чи справді ви обрали правильний текст. Тому результат потрібно перевірити вручну або тестами.
Якщо під час розв’язання конфліктів ви зрозуміли, що почали злиття помилково, його можна скасувати:
git merge --abortЦя команда повертає стан поточної гілки до стану перед початком незавершеного злиття.
Після скасування перевірте стан:
git statusgit merge --abort працює, коли Git має активний процес злиття. Якщо злиття вже завершено комітом, ця команда його не скасує.
Під час конфлікту корисно переглянути різницю:
git diffЦя команда покаже конфліктні зміни у робочих файлах.
Список невирішених файлів:
git diff --name-only --diff-filter=UПозначення U означає unmerged, тобто файл ще не оброблено до кінця.
Після виправлення файлу можна перевірити, що він доданий до індексу:
git statusЗлиття можна виконувати не лише з локальною гілкою, а й з іншою доступною назвою посилання, наприклад:
git merge origin/featureУ цьому випадку Git об’єднає поточну гілку з локальним станом віддаленої гілки origin/feature.
Перед таким злиттям важливо розуміти, що origin/feature — це локальне посилання на відомий Git стан віддаленої гілки. Воно оновлюється окремою операцією отримання змін. Саме злиття не завантажує нові коміти з сервера.
Для цього уроку достатньо пам’ятати головне правило: перед git merge перевірте, яку саме гілку ви зливаєте і яка гілка є поточною.
merge не в тій гілціКоманда:
git merge featureдодає feature у поточну гілку. Якщо поточною випадково є feature, команда не зробить очікуваного злиття з main.
Перед злиттям перевіряйте:
git branch --show-currentЯкщо у файлі залишити <<<<<<<, ======= або >>>>>>>, це може спричинити помилки під час запуску програми чи тестів.
Після розв’язання конфліктів можна пошукати маркери:
git grep -n -E '<<<<<<<|=======|>>>>>>>'Якщо команда нічого не вивела, такі маркери не знайдено у відстежуваних файлах.
git add лише повідомляє Git, що файл більше не має невирішеного конфлікту. Він не підтверджує логічну правильність коду.
Після злиття перевірте:
git status
git diff HEAD^ HEADТакож запустіть тести проєкту, якщо вони є.
Під час ручного розв’язання конфлікту легко залишити лише поточну версію та випадково втратити потрібну зміну з іншої гілки. Перед редагуванням прочитайте обидві версії й вирішіть, чи потрібно:
залишити зміни main;
залишити зміни feature;
об’єднати обидві версії;
переписати фрагмент заново.
git merge --abort після завершення merge-комітомПісля виконання git commit злиття вже завершене. Якщо результат неправильний, git merge --abort не скасує цей коміт. Тому перед завершенням злиття перевіряйте файли та стан репозиторію.
git merge branch-name додає вказану гілку до поточної.
Перед злиттям потрібно перейти в гілку, яка має отримати зміни.
Fast-forward пересуває вказівник поточної гілки вперед і не створює merge-коміт.
Якщо обидві гілки мають незалежні коміти, Git зазвичай створює merge-коміт.
git merge --no-ff примусово створює merge-коміт навіть за можливого fast-forward.
Конфлікт виникає, коли Git не може автоматично поєднати зміни.
Для розв’язання конфлікту потрібно відредагувати файли, виконати git add, а потім завершити злиття через git commit.
Незавершене злиття можна скасувати командою git merge --abort.
Після злиття слід перевірити git status, історію та результат роботи програми.