Создано: 2026-02-06
# Merge — виды слияния в Git
Когда ты работаешь в отдельной ветке и хочешь перенести свои изменения в основную — ты делаешь **merge** (слияние). Но Git может выполнить это слияние по-разному, в зависимости от истории коммитов.
Есть **три основных вида** merge:
| Вид | Суть одним предложением |
| ------------------------------------- | ----------------------------------------------------------------------- |
| **Fast-Forward** | Просто передвигает указатель ветки вперёд — без создания нового коммита |
| **Three-Way Merge** (No Fast-Forward) | Создаёт отдельный merge-коммит, объединяющий две ветки |
| **Squash Merge** | Собирает все коммиты ветки в один и кладёт его поверх основной ветки |
Ниже — каждый вид подробно.
---
## 1. Fast-Forward Merge
**Что это?** Самый простой вид слияния. Git просто «перематывает» указатель основной ветки вперёд, потому что никаких расхождений в истории нет.
**Когда происходит?** Когда основная ветка (`main`) не получала новых коммитов с момента создания побочной ветки (`feature`). То есть `feature` — линейное продолжение `main`.
### Было (до merge)
```
main
↓
A ── B ── C ── D ── E
↑
feature
```
Здесь `main` стоит на коммите `C`, а `feature` ушла вперёд до `E`. При этом в `main` после ответвления **не было новых коммитов**.
### Стало (после `git merge feature`)
```
main
↓
A ── B ── C ── D ── E
↑
feature
```
Git просто передвинул указатель `main` на `E`. Никакого нового коммита не создалось — история осталась линейной.
### Команда
```bash
git checkout main
git merge feature
```
### Fork App
В [[Fork app|Fork]] fast-forward происходит автоматически при выборе опции **Default** — если Git видит, что основная ветка не уходила вперёд, он просто перемотает указатель.
Как сделать:
1. Правый клик по ветке `feature` → **Merge into \<текущая ветка\>...**
2. В окне слияния выбрать **Default**
3. Fork сам определит, что можно сделать fast-forward, и выполнит его
Все коммиты `feature` окажутся в `main` в самом верху — без создания дополнительного merge-коммита.
> [!TIP] Как узнать, что произошёл fast-forward?
> Git напишет в консоли: `Fast-forward`. Также в логе не появится merge-коммит.
> [!IMPORTANT] Ключевое свойство
> Fast-forward возможен ==только когда основная ветка не ушла вперёд== после создания побочной. Если в `main` появился хотя бы один новый коммит — Git не сможет просто «перемотать» и будет использовать Three-Way Merge.
---
## 2. Three-Way Merge (он же No Fast-Forward)
**Что это?** Git создаёт специальный **merge-коммит** — коммит с двумя родителями, который объединяет историю двух веток.
**Когда происходит?** Когда обе ветки ушли вперёд — то есть после ответвления в каждой из них появились свои коммиты.
### Почему «Three-Way»?
Git смотрит на **три точки**:
1. Последний коммит `main`
2. Последний коммит `feature`
3. Их **общий предок** (коммит, где ветки разошлись)
Сравнивая эти три состояния, Git понимает, какие изменения пришли из `main`, а какие из `feature`.
### Было (до merge)
```
D ──── E ← feature
/
A ── B ── C ── F ── G ← main
```
`main` и `feature` разошлись после коммита `B`. В `main` появились коммиты `F` и `G`, в `feature` — `D` и `E`. Простая «перемотка» невозможна.
### Стало (после `git merge feature`)
```
D ────── E
/ \
A ── B ── C ── F ── G ── M ← main
↑
merge-коммит
```
Git создал коммит `M` — он имеет **двух родителей**: `G` (из `main`) и `E` (из `feature`). Этот коммит содержит объединённые изменения обеих веток.
### Команда
```bash
git checkout main
git merge feature
```
Если хочешь **принудительно** создать merge-коммит даже когда fast-forward возможен:
```bash
git merge --no-ff feature
```
### Fork App
В [[Fork app|Fork]] three-way merge выполняется через опцию **No Fast-Forward**.
Как сделать:
1. Правый клик по ветке `feature` → **Merge into \<текущая ветка\>...**
2. В окне слияния выбрать **No Fast-Forward**
3. Fork создаст merge-коммит, даже если fast-forward был бы возможен
> [!TIP] Опция «Don't Commit»
> Работает так же, как **No Fast-Forward**, но с одним отличием: Fork выполнит слияние, но **не создаст коммит автоматически**. Все изменения окажутся в staging area — ты сможешь их проверить и зафиксировать вручную с собственным сообщением коммита.
Если после слияния через No Fast-Forward хочешь изменить описание merge-коммита — используй **Amend**.
> [!WARNING] Конфликты
> Если `main` и `feature` изменили **одни и те же строки** в одном файле — Git не сможет автоматически решить, чью версию взять. Возникнет **merge-конфликт**, который нужно разрешить вручную.
## 3. Squash Merge
**Что это?** Git берёт **все коммиты** из побочной ветки, «сжимает» их в один набор изменений и позволяет тебе создать **один новый коммит** в основной ветке.
**Когда полезно?** Когда в feature-ветке накопилась куча промежуточных коммитов вроде «fix», «wip», «temp» — и ты хочешь, чтобы в `main` попал один чистый коммит с понятным описанием.
### Было (до merge)
```
D ── E ── F ← feature
/ (3 коммита: "wip", "fix", "done")
A ── B ── C ← main
```
### Стало (после `git merge --squash feature` + `git commit`)
```
A ── B ── C ── S ← main
↑
один коммит,
содержащий изменения
D + E + F
```
Обрати внимание: ветка `feature` **не связана** с коммитом `S` — у `S` только один родитель (`C`). В истории `main` не остаётся следов того, что была отдельная ветка.
### Команда
```bash
git checkout main
git merge --squash feature
git commit -m "Добавить новую функциональность"
```
> [!IMPORTANT] Два шага, не один
> `--squash` не создаёт коммит автоматически. Он только подготавливает изменения (staging area). Тебе нужно самому сделать `git commit`.
### Fork App
В [[Fork app|Fork]] squash merge выполняется через опцию **Squash**.
Как сделать:
1. Правый клик по ветке `feature` → **Merge into \<текущая ветка\>...**
2. В окне слияния выбрать **Squash**
3. Fork соберёт все коммиты ветки в один и перенесёт его в `main`
Результат: в `main` появится один коммит, в описании которого будут перечислены названия всех исходных коммитов из `feature`.
---