# Злиття (git merge): мердж-коміт, squash і rebase на GitHub

> Що робить мердж (git merge), три варіанти за кнопкою мерджу на GitHub (merge commit, squash and merge, rebase and merge) і що кожен лишає в історії, fast-forward простими словами і як перевірити, чи зміна справді в main.

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

## Зелена кнопка

**Alex Tutor:** Новини для тебе й продуктової команди Котокорму щодо запиту фінвідділу. Pull request #133 від Daniel, «Raise free-delivery threshold to 1000 UAH», отримав схвалення на рев'ю. Лишилося одне: змерджити його.

**Daniel:** До речі, ця назва це твоє повідомлення коміту з першого модуля. Підсумковий рядок мені сподобався, тож я взяв його для pull request.

**Sofia:** Мої слова в історії сайту. До такого можна й звикнути.

**Alex Tutor:** **Мердж** (merge), або злиття, означає перенести зміни з однієї гілки в іншу. Тут: із `feature/free-delivery-1000` у `main`. Доки цього не сталося, на сайті й далі 800.

```text
Граф комітів, від найстарішого: Pull request #133 перед мерджем
1. коміт 8be2f70 у main "Add cat treats bundle"
2. гілка feature/free-delivery-1000 починається від коміту 8be2f70
3. коміт 5c1d7e2 у feature/free-delivery-1000 "Set freeDeliveryFrom to 1000"
4. коміт a8f40b9 у feature/free-delivery-1000 "Update delivery page text"
5. коміт d36b9c5 у feature/free-delivery-1000 "Update cart hint text"
```

*Три коміти в гілці, і жодного з них поки що немає в main.*

**Sofia:** Три коміти в гілці, і жодного з них немає в `main`. Три ahead, нуль behind: `main` не рухалася, відколи Daniel почав.

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

> **Вправа:** Daniel мерджить `feature/free-delivery-1000` у `main`. Що змінюється?

## Одна кнопка, три варіанти

**Alex Tutor:** Унизу pull request є зелена кнопка `Merge pull request`, а поруч із нею маленька стрілка. Стрілка відкриває три варіанти.

**Sofia:** `Create a merge commit`, `Squash and merge`, `Rebase and merge`. І котрий дає правильний результат?

**Alex Tutor:** Усі три. Після будь-якого з них файли в `main` однакові: поріг 1000 у кожному разі. Відрізняється історія, яку ти потім читатимеш.

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

*Файли в результаті однакові; різниться лише те, як читається історія.*

**Sofia:** То як тоді вибирати?

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

**Alex Tutor:** Розберемо їх по черзі, на тій самій гілці, і намалюємо, як після цього виглядатиме `main`.

> **Вправа:** Три варіанти в кнопці мерджу на GitHub залишають у файлах `main` різний текст.

## Create a merge commit

**Alex Tutor:** Варіант перший, `Create a merge commit`. GitHub додає коміти гілки в `main` такими, як вони є, і зв'язує дві лінії одним додатковим комітом: це **мердж-коміт** (merge commit).

```text
Граф комітів, від найстарішого: Варіант 1: мердж-коміт
1. коміт 8be2f70 у main "Add cat treats bundle"
2. гілка feature/free-delivery-1000 починається від коміту 8be2f70
3. коміт 5c1d7e2 у feature/free-delivery-1000 "Set freeDeliveryFrom to 1000"
4. коміт a8f40b9 у feature/free-delivery-1000 "Update delivery page text"
5. коміт d36b9c5 у feature/free-delivery-1000 "Update cart hint text"
6. мердж-коміт e4b7a90: feature/free-delivery-1000 змерджено в main "Merge pull request #133" (виділено)
```

*Дві доріжки сходяться в e4b7a90, у якого двоє батьків. Історія main отримує чотири записи.*

**Sofia:** Дві доріжки сходяться в `e4b7a90`. Тобто в цього коміту двоє батьків: попередній коміт у `main` і останній у гілці.

**Alex Tutor:** Так. Його повідомлення пише сам GitHub: «Merge pull request #133 from catchow/feature/free-delivery-1000». Історія `main` зростає на чотири записи: три коміти від Daniel і мердж-коміт.

**Daniel:** У терміналі розробники роблять те саме командою `git merge`. Нічого не переписується: мої коміти зберігають свої хеші, і завжди видно, де гілка почалася і де повернулася.

**Sofia:** А ціна цього: історія стає строкатішою, бо кожен дрібний крок кожної гілки потрапляє в `main`.

> **Вправа:** Pull request #140 змерджили через `Create a merge commit`. Які твердження правдиві? Познач усе, що підходить.

## Що таке squash and merge?

**Alex Tutor:** Варіант другий, `Squash and merge`. **Squash and merge** пакує всі коміти pull request в один новий коміт у `main`: GitHub бере всі зміни з гілки, і вони потрапляють в історію разом, одним кроком.

```text
Граф комітів, від найстарішого: Варіант 2: squash and merge
1. коміт 8be2f70 у main "Add cat treats bundle"
2. гілка feature/free-delivery-1000 починається від коміту 8be2f70
3. коміт 5c1d7e2 у feature/free-delivery-1000 "Set freeDeliveryFrom to 1000"
4. коміт a8f40b9 у feature/free-delivery-1000 "Update delivery page text"
5. коміт d36b9c5 у feature/free-delivery-1000 "Update cart hint text"
6. коміт 6f2c8a1: feature/free-delivery-1000 змерджено в main через squash "Raise free-delivery threshold to 1000 UAH (#133)" (виділено)
```

*Один новий коміт у main, 6f2c8a1, з пунктирним зв'язком із гілкою. Трьох початкових комітів в історії main немає.*

**Sofia:** Один коміт, `6f2c8a1`, і пунктирна лінія замість суцільної. А його назва закінчується на `(#133)`.

**Alex Tutor:** Пунктирна, бо три коміти від Daniel не стали частиною історії `main`. Частиною історії стали їхні зміни, запаковані в один коміт. Номер pull request до назви GitHub додає сам.

**Daniel:** Перед підтвердженням GitHub дає мені відредагувати повідомлення цього коміту. Типово воно бере назву pull request і перелічує під нею назви моїх трьох комітів, тож нічого насправді не губиться.

**Daniel:** Саме цим користується наша команда. Тому `main` у першому модулі й мала вигляд прямої лінії: один pull request, один коміт. Мої коміти на кшталт «oops, typo» туди не потрапляють.

![Три невеликі посилки пакують в одну більшу коробку з однією порожньою наліпкою, і вона їде конвеєром на склад.](https://productovi.com/courses/git-for-product/inline/merging-illustration-3.webp)

*Squash-мердж перетворює кроки гілки на один коміт в основній гілці.*

**Sofia:** А якщо я хочу побачити три початкові кроки?

**Alex Tutor:** Відкрий pull request. Його вкладка `Commits` зберігає їх навіть після видалення гілки.

> **Вправа:** Pull request #140 змерджили через `Squash and merge`. Скільки нових комітів отримала `main` і де досі можна побачити t1 і t2?

## Rebase and merge і fast-forward merge

**Alex Tutor:** Варіант третій, `Rebase and merge`. GitHub відтворює коміти гілки поверх `main`, один за одним. Без мердж-коміту і без склеювання.

```text
Граф комітів, від найстарішого: Варіант 3: rebase and merge
1. коміт 8be2f70 у main "Add cat treats bundle"
2. гілка feature/free-delivery-1000 починається від коміту 8be2f70
3. коміт 5c1d7e2 у feature/free-delivery-1000 "Set freeDeliveryFrom to 1000"
4. коміт a8f40b9 у feature/free-delivery-1000 "Update delivery page text"
5. коміт d36b9c5 у feature/free-delivery-1000 "Update cart hint text"
6. коміт 1f6a3d8 у main "Set freeDeliveryFrom to 1000"
7. коміт 2b9e5c4 у main "Update delivery page text"
8. коміт 7d0c8f1 у main "Update cart hint text" (виділено)
```

*Три копії комітів, з новими хешами, продовжують пряму лінію main. Оригінали лишаються в гілці, доки її не видалять.*

**Sofia:** У `main` три коміти з тими самими повідомленнями, що й у гілці, але з іншими хешами.

**Alex Tutor:** Бо це копії. **Rebase** створює кожен коміт заново на новому місці, а заново створений коміт отримує новий хеш. Історія лишається прямою лінією і зберігає кожен крок.

**Daniel:** Ще ти почуєш «fast-forward». Якщо `main` не змінювалася, відколи почалася гілка, об'єднувати нічого. Git може просто пересунути назву `main` уперед, на останній коміт гілки.

**Sofia:** Як у нашому випадку: `main` стояла на місці, поки ти працював. Жодного нового коміту, просто назва наздоганяє.

**Daniel:** Так, але це справа термінала. Станом на 2026 рік кнопка GitHub звичайного fast-forward не робить: перший варіант завжди додає мердж-коміт, а третій завжди створює коміти заново.

**Alex Tutor:** Ще одна перевірка — на випадок, про який Daniel сказав, що він буває лише в терміналі.

> **Вправа:** `main` не змінювалася, відколи Daniel почав гілку. У терміналі, перебуваючи в `main`, він запускає `git merge feature/free-delivery-1000`. Що станеться за замовчуванням?

> **Вправа:** Зістав кожен спосіб мерджу з тим, як після нього виглядає граф.

## Merge, squash чи rebase: чому команда вибирає один спосіб

**Sofia:** Якщо файли в підсумку однакові, чому команди про це сперечаються?

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

**Daniel:** Команди, які вибирають мердж-коміти, хочуть мати повний запис: кожен коміт з початковим хешем і точні місця, де гілки сходилися. Прихильники rebase теж хочуть кожен крок, але прямою лінією.

**Alex Tutor:** Переможця тут немає. Важливо, щоб команда домовилася про один спосіб, і тоді історія читається однаково з тижня в тиждень.

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

**Sofia:** Тобто «скільки комітів увійшло в цей реліз?» це питання з підступом, доки я не знаю, як команда мерджить.

> **Вправа:** Який варіант мерджу дав таку історію `main`?

## Чи моя зміна вже в main?

**Alex Tutor:** Daniel натиснув `Squash and merge`, а потім `Confirm squash and merge`. Тепер питання, яке ти як продакт чутимеш найчастіше: зміна вже там, де треба?

**Alex Tutor:** Подивись на позначку вгорі pull request. Зелена `Open` змінилася на фіолетову `Merged`, а рядок поруч каже, що коміт пішов у `main`.

```text
Pull requests у catchow/website
- [Merged] Raise free-delivery threshold to 1000 UAH #133: Змерджено в main сьогодні: зміни вже в main
- [Open] Rewrite delivery page copy #132: Відкритий: зміни Emma досі в її гілці
- [Draft] Fix checkout button after cart edits #134: Чернетка: Daniel ще працює над ним
```

**Sofia:** Отже, #133 уже в `main`. #132 від Emma ще відкритий, а виправлення кнопки, #134, поки що чернетка. Жоден із них до `main` не дійшов.

**Alex Tutor:** Ще дві перевірки. Перша: прочитай, у яку гілку змерджено, бо pull request може бути спрямований не в `main`. Друга: змерджено ще не означає, що зміна вийшла в реліз.

**Daniel:** У Котокормі мердж у `main` автоматично викладають на сайт за кілька хвилин. Інші команди накопичують мерджі й випускають їх релізом. До цього дійдемо в четвертому модулі.

**Daniel:** І згадай мою кнопку. На pull request #97 досі написано `Merged`, хоча #98 відкотив його за десять хвилин. Merged це факт про минуле. Якщо є сумніви, відкрий файл у `main`.

![Посилка з печаткою «доставлено» стоїть на порозі, а кур'єр несе таку саму посилку назад до фургона.](https://productovi.com/courses/git-for-product/inline/merging-illustration-4.webp)

*Змерджено куди, коли і чи воно досі там?*

**Sofia:** Отже, щоразу три питання: куди змерджено, коли, і чи воно досі там.

> **Вправа:** Зміни яких із цих pull requests зараз є в `main`? Познач усе, що підходить.

**Alex Tutor:** А застереження Daniel про #97 варте окремої перевірки.

> **Вправа:** Якщо pull request має фіолетову позначку `Merged`, його зміни гарантовано є на робочому сайті просто зараз.

## Справа про зниклі коміти

**Alex Tutor:** Останнє. Колега з аналітики відкриває pull request #133 і не розуміє: у pull request три коміти, а в історії `main` лише один.

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

**Alex Tutor:** Поясни двома-трьома реченнями, що сталося і де ці три коміти досі можна знайти.

**Daniel:** Додатковий бал, якщо згадаєш номер у назві коміту. Це найшвидший шлях назад до pull request.

> **Вправа:** Колега відкриває pull request #133 і бачить у вкладці `Commits` три коміти, а в історії `main` лише один: «Raise free-delivery threshold to 1000 UAH (#133)». Колега думає, що два коміти загубилися. Поясни двома-трьома реченнями, що сталося і де ці три коміти досі можна знайти.

**Alex Tutor:** Наступного уроку: pull request від Emma. Вони з Daniel змінили той самий рядок на сторінці доставки, і GitHub відмовляється зливати це самотужки.

**Sofia:** Конфлікт. Уже саме слово звучить як неприємності.

- Попередній урок: [Гілки в Git: паралельні версії проєкту](https://productovi.com/uk/courses/git-for-product/branches.md)
- Наступний урок: [Конфлікти злиття (merge conflict): чому виникають і що робити](https://productovi.com/uk/courses/git-for-product/merge-conflicts.md)

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