# git merge _Ветки и слияния · GitLab Knowledge Base_ **TL;DR:** Сливает другую ветку в текущую. Делает либо fast-forward (двигает указатель), либо создаёт merge-коммит с двумя родителями. При пересекающихся изменениях возможны конфликты. `git merge` берёт коммиты из другой ветки и привешивает их к текущей. Базовая операция совместной работы. ## Два сценария ### Fast-forward Если у текущей ветки нет новых коммитов, которых нет в сливаемой, Git просто «перематывает» указатель текущей ветки на вершину сливаемой. Новый коммит **не создаётся**, история остаётся линейной. ``` до: main: A → B feat: A → B → C → D после: main: A → B → C → D (main догнал feat) feat: A → B → C → D ``` Происходит при `git merge feat` из `main`, если на main не было новых коммитов после ветвления. ### Three-way merge Если у обеих веток есть свои коммиты после развилки, Git создаёт *merge-коммит* - особый коммит с двумя родителями. ``` до: main: A → B → E feat: A → B → C → D после: main: A → B → E → M (M - merge-коммит) \ / C → D feat: A → B → C → D ``` Содержимое merge-коммита - слияние tree'ев E и D с учётом общего предка B. Если строки не пересекаются - Git справится автоматически. Если пересекаются - конфликт. ## Команды ```bash git switch main git merge feature # слить feature в main git merge --no-ff feature # создать merge-коммит даже если ff возможен git merge --ff-only feature # отказаться, если ff невозможен git merge --squash feature # сжать все коммиты feature в один stage git merge --abort # отменить начатый merge ``` Флаг `--no-ff` - частая стратегия команд, которые хотят сохранять топологию: «вот тут была feature-ветка». Без `--no-ff` фичевые коммиты сольются в основную линию и факт ветвления визуально пропадёт. ## Конфликты При конфликте Git помечает файл специальными маркерами: ``` <<<<<<< HEAD print("из main") ======= print("из feature") >>>>>>> feature ``` Что делать: 1. Открыть файл в редакторе или mergetool. 2. Удалить маркеры, оставить нужную версию (или их комбинацию). 3. `git add <файл>` - пометить как resolved. 4. `git commit` - завершить merge (Git предложит готовое сообщение). Полезное: ```bash git status # покажет, какие файлы в конфликте git diff # увидеть три версии в конфликтных hunk'ах git checkout --ours file # принять версию текущей ветки целиком git checkout --theirs file # принять версию сливаемой ветки целиком ``` ## Подводные камни - `--squash` создаёт коммит **без указания родителя из feature**. Это значит, что после squash-merge сливаемая ветка считается нссмерженной (нельзя удалить через `branch -d`). Это часто путает; нужно использовать `branch -D`. - Если в процессе merge передумал - `git merge --abort` вернёт всё в исходное состояние. - `git merge` с unstaged-изменениями в рабочей копии может отказаться, если эти изменения пересекаются с merge'ем. Решение - закоммитить или stash перед merge. ## Команды ```bash git merge feature ``` Слить ветку feature в текущую ```bash git merge --no-ff feature ``` Создать merge-коммит, даже если fast-forward возможен ```bash git merge --abort ``` Отменить идущий merge при конфликте ```bash git merge --squash feature ``` Сжать все коммиты feature в один stage в текущей ветке ## См. также - [Ветка (branch)](/courses/git/kb/branch.md) - [git commit](/courses/git/kb/commit-cmd.md) - [git log](/courses/git/kb/log.md)