# Merge и rebase

> Два способа объединить изменения веток в Git — merge сохраняет историю как есть и создаёт коммит слияния, rebase переносит коммиты поверх другой ветки и делает историю линейной.

Источник: https://llmdocs.ru/wiki/merge-rebase/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Команды**: git merge, git rebase, git cherry-pick
- **Стратегия слияния по умолчанию**: ort (с Git 2.34, 2021), ранее recursive
- **Виды merge**: fast-forward, трёхсторонний (merge commit), squash
- **Интерактивный режим**: git rebase -i
- **Повторное использование решений конфликтов**: git rerere
- **Главное правило**: не переписывать опубликованную общую историю
**Merge** (слияние) и **rebase** (перебазирование) — два способа перенести изменения из одной ветки [Git](https://llmdocs.ru/wiki/git.md) в другую. Оба дают одинаковый итоговый код, но по-разному записывают историю. `git merge` соединяет две линии разработки, сохраняя их как есть, и при необходимости создаёт **коммит слияния** с двумя родителями.[1] `git rebase` берёт коммиты ветки и заново применяет их поверх другой, как будто работа начиналась с её последнего состояния, — история получается линейной.[2] Выбор между ними — один из главных вопросов командных договорённостей о работе с ветками (см. [Ветвление в Git](https://llmdocs.ru/wiki/git-branching.md)).

## Merge

Предположим, ветка `feature` ответвилась от `main`, и с тех пор в обеих появились новые коммиты.

```text
      A---B---C  feature
     /
D---E---F---G    main
```

`git switch main && git merge feature` находит общего предка (**merge base**, здесь `E`), сравнивает с ним обе вершины и создаёт коммит `M`, объединяющий изменения:

```text
      A---B---C
     /         \
D---E---F---G---M  main
```

Это **трёхстороннее слияние** (*three-way merge*). Если же в `main` новых коммитов не было, Git просто передвигает указатель ветки вперёд — это **fast-forward**, без коммита слияния. Флаг `--no-ff` заставляет создать коммит слияния и в этом случае, чтобы в истории было видно, что изменения пришли пачкой из отдельной ветки; `--ff-only` наоборот — разрешает только перемотку.[3]

С версии Git 2.34 (2021) по умолчанию используется стратегия слияния **ort** — переписанная и заметно более быстрая замена прежней `recursive`.[5]

**Squash-слияние** (`git merge --squash`) собирает все изменения ветки в один обычный коммит без ссылки на исходные — так часто вливают [pull request](https://llmdocs.ru/wiki/pull-request.md), чтобы в основной ветке был один коммит на задачу.

## Rebase

`git switch feature && git rebase main` вычисляет изменения коммитов `A`, `B`, `C` и по очереди применяет их поверх `G`:

```text
D---E---F---G  main
             \
              A'--B'--C'  feature
```

`A'`, `B'`, `C'` — **новые** коммиты с тем же содержанием изменений, но другими родителями и хешами. После этого `main` можно перемотать на `C'` через fast-forward, и история останется прямой линией.

### Интерактивный rebase

`git rebase -i HEAD~5` открывает список последних коммитов, в котором для каждого можно выбрать действие:[4][6]

| Команда | Действие |
|---|---|
| `pick` | оставить как есть |
| `reword` | изменить сообщение |
| `edit` | остановиться, чтобы поправить коммит |
| `squash` / `fixup` | слить с предыдущим (с сохранением сообщения или без) |
| `drop` | удалить коммит |

Строки можно переставлять. Так перед публикацией приводят в порядок историю: убирают коммиты «опечатка», «ещё фикс», объединяют связанные изменения. Связка `git commit --fixup <hash>` и `git rebase -i --autosquash` делает это почти автоматически.

## Главное правило rebase

**Не перебазируйте коммиты, которые уже опубликованы и на которых кто-то основывает свою работу.**[2] Rebase создаёт новые коммиты вместо старых; у коллег останутся старые версии, и при следующей синхронизации история раздвоится. Переписывать свою личную ветку можно, но отправлять её придётся с принудительной перезаписью — безопаснее вариантом `git push --force-with-lease`, который откажется затирать чужие коммиты, появившиеся на сервере.

## Конфликты

Если обе ветки изменили одни и те же строки, Git останавливается и размечает файл маркерами `<<<<<<<`, `=======`, `>>>>>>>`. Дальше нужно отредактировать файл, выполнить `git add` и продолжить: `git merge --continue` или `git rebase --continue`. Отменить всё — `git merge --abort` / `git rebase --abort`.

При merge конфликты решаются один раз для всего слияния, при rebase — отдельно для каждого переносимого коммита, что бывает утомительно. Помогает механизм **rerere** (*reuse recorded resolution*, `git config rerere.enabled true`): Git запоминает, как был решён конфликт, и повторяет решение автоматически.

## Что выбрать

| | Merge | Rebase |
|---|---|---|
| История | истинная, с ветвлениями | линейная, «как будто последовательно» |
| Хеши коммитов | не меняются | меняются |
| Безопасность для общих веток | безопасен | только для личных веток |
| Чтение `git log` | сложнее при множестве веток | проще |
| Поиск ошибки `git bisect` | работает | работает удобнее на линейной истории |

Распространённая практика: обновлять свою рабочую ветку из `main` через rebase (`git pull --rebase`), а в основную ветку вливать через merge или squash-merge при закрытии pull request. Командам стоит договориться о едином стиле и закрепить его настройками репозитория на [GitHub](https://llmdocs.ru/wiki/github.md) или в GitLab.

**Cherry-pick** (`git cherry-pick <hash>`) — родственная операция: переносит отдельный коммит в текущую ветку, например исправление ошибки из `main` в ветку релиза.

## См. также

- [Git](https://llmdocs.ru/wiki/git.md)
- [Ветвление в Git](https://llmdocs.ru/wiki/git-branching.md)
- [Git-хуки](https://llmdocs.ru/wiki/git-hooks.md)
- [Pull request / Merge request](https://llmdocs.ru/wiki/pull-request.md)

## Источники

1. [Pro Git — 3.2 Basic Branching and Merging](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging)
2. [Pro Git — 3.6 Rebasing](https://git-scm.com/book/en/v2/Git-Branching-Rebasing)
3. [git-merge documentation](https://git-scm.com/docs/git-merge)
4. [git-rebase documentation](https://git-scm.com/docs/git-rebase)
5. [Git 2.34 release notes (ort default)](https://github.com/git/git/blob/master/Documentation/RelNotes/2.34.0.txt)
6. [Pro Git — 7.6 Rewriting History](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History)
