Создано: 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`. ---