$ sudo teach IT
Модуль 4 · Ветки · Урок 4.2

Слияние веток

Вы сольёте ветку в main, отличите fast-forward от merge-коммита и удалите ветки, которые больше не нужны.

Теория + практика~20 минутНовичокgit mergefast-forward

git merge

Слияние веток в Git (merge) — это перенос работы из одной ветки в другую командой git merge: коммиты ветки становятся частью main. Вы сольёте в main ветки feature/projects и feature/footer, увидите два вида слияния — перемотку и merge-коммит — и удалите ветки, которые своё отработали.

Страница проектов уже есть в ветке feature/projects, но main о ней ничего не знает. Черновик готов — пора переносить его в чистовик.

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

Сливают в текущую ветку. Сначала встаньте на ветку, которая принимает изменения (main), затем назовите ветку, которую вливаете. Читается так: «в main влить feature/projects».

Но сначала доделаем ветку: на главной нужна ссылка на новую страницу. Перейдите на feature/projects и добавьте в index.html внутри раздела projects, сразу после </ul>, одну строку:

HTML · index.html (раздел «Мои проекты»)
  <section id="projects">
    <h2>Мои проекты</h2>
    <ul>
      <li>Сайт-визитка</li>
      <li>Список дел</li>
    </ul>
    <p><a href="projects.html">Все проекты</a></p>
  </section>
Терминал
$ git switch feature/projects
Switched to branch 'feature/projects'
$ git add index.html
$ git commit -m "Добавить ссылку на страницу проектов"
[feature/projects 5dd4568] Добавить ссылку на страницу проектов
 1 file changed, 1 insertion(+)

В ветке два коммита. Теперь сливаем:

Терминал
$ git switch main
Switched to branch 'main'
$ git merge feature/projects
Updating 5acc5ae..5dd4568
Fast-forward
 index.html    |  1 +
 projects.html | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 projects.html

Разбор по строкам

  • Updating 5acc5ae..5dd4568 — main передвигается с коммита 5acc5ae на 5dd4568.
  • Fast-forward — способ слияния: перемотка (о нём ниже).
  • index.html | 1 + и projects.html | 14 ++++ — что пришло в main по файлам: плюсы — добавленные строки.
  • 2 files changed, 15 insertions(+) — итог: два файла, 15 новых строк; create mode ... projects.html — в main появился новый файл.

Откройте index.html в браузере: внизу раздела «Мои проекты» есть ссылка «Все проекты», и она ведёт на новую страницу.

Fast-forward

Пока вы работали в ветке, в main не появилось ни одного нового коммита. Значит, feature/projects — это просто main плюс два коммита сверху. Соединять нечего: Git передвигает метку main вперёд, на последний коммит ветки, как перематывают видео. Отсюда название: fast-forward, «перемотка».

Схема
До:     5acc5ae --- 294653e --- 5dd4568
           ^                       ^
          main              feature/projects

После:  5acc5ae --- 294653e --- 5dd4568
                                   ^
                         main, feature/projects

Что на схеме: коммиты те же, сдвинулась только метка main. Нового коммита перемотка не создаёт — это видно и в истории:

Терминал
$ git log --oneline -4
5dd4568 (HEAD -> main, feature/projects) Добавить ссылку на страницу проектов
294653e Добавить страницу проектов
5acc5ae Revert "Поменять цвет фона"
875cb0a Поменять цвет фона

Разбор по строкам

  • (HEAD -> main, feature/projects) — обе ветки теперь стоят на одном коммите, вы на main.
  • Флаг -4 — показать только четыре последних коммита.

Merge-коммит

Чаще бывает иначе: пока вы делали ветку, в main тоже что-то закоммитили. Проверим на подвале. Создайте ветку feature/footer и добавьте в index.html перед </body> строку подвала (&copy; в HTML — знак копирайта):

HTML · index.html (перед </body>)
  <footer><p>&copy; 2026 Алекс Иванов</p></footer>
</body>
Терминал
$ git switch -c feature/footer
Switched to a new branch 'feature/footer'
$ git add index.html
$ git commit -m "Добавить подвал"
[feature/footer 578f2d5] Добавить подвал
 1 file changed, 1 insertion(+)

Теперь вернитесь на main и допишите в конец README.md пустую строку и строку про страницы сайта:

Markdown · README.md
# portfolio

Личный сайт-визитка Алекса Иванова. Учебный проект курса по Git.

Страница на чистом HTML и CSS, без фреймворков.

Страницы: главная (index.html) и проекты (projects.html).
Терминал
$ git switch main
Switched to branch 'main'
$ git add README.md
$ git commit -m "Описать страницы в README"
[main 89766ce] Описать страницы в README
 1 file changed, 2 insertions(+)
$ git log --oneline --graph --all
* 89766ce (HEAD -> main) Описать страницы в README
| * 578f2d5 (feature/footer) Добавить подвал
|/
* 5dd4568 (feature/projects) Добавить ссылку на страницу проектов
* 294653e Добавить страницу проектов
* 5acc5ae Revert "Поменять цвет фона"
...

Разбор по строкам

  • * 89766ce (HEAD -> main) — новый коммит в main.
  • | * 578f2d5 (feature/footer) — коммит ветки на боковой линии.
  • |/ — здесь линии сходятся: 5dd4568 — общий предок, с него обе ветки разошлись.

Перемотать main теперь нельзя: у каждой ветки свой новый коммит. Git создаёт merge-коммит (коммит слияния) — особый коммит с двумя родителями, который соединяет обе линии. Выполните git merge feature/footer. Сначала откроется редактор, который вы настроили для Git, с готовым сообщением:

Вывод · редактор
Merge branch 'feature/footer'
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

Разбор по строкам

  • Merge branch 'feature/footer' — готовое сообщение коммита. Обычно его оставляют как есть.
  • Строки с # — комментарии, в коммит не попадут. Пустое сообщение отменит коммит.
  • Сохраните и закройте: в VS Code — закройте вкладку с файлом, в nano — Ctrl+O, Enter, Ctrl+X.
Терминал
$ git merge feature/footer
Merge made by the 'ort' strategy.
 index.html | 1 +
 1 file changed, 1 insertion(+)

Разбор по строкам

  • Merge made by the 'ort' strategy. — слияние сделано, создан merge-коммит; ort — название алгоритма, которым Git сливает по умолчанию, выбирать ничего не нужно.
  • index.html | 1 + — что пришло из ветки: строка подвала.
Терминал
$ git log --oneline --graph -5
*   d3c03bd (HEAD -> main) Merge branch 'feature/footer'
|\
| * 578f2d5 (feature/footer) Добавить подвал
* | 89766ce Описать страницы в README
|/
* 5dd4568 (feature/projects) Добавить ссылку на страницу проектов
* 294653e Добавить страницу проектов

Разбор по строкам

  • * d3c03bd (HEAD -> main) Merge branch 'feature/footer' — merge-коммит, на нём теперь main.
  • |\ — в него входят две линии: у коммита два родителя, 89766ce из main и 578f2d5 из ветки. git log -1 покажет их в строке Merge: 89766ce 578f2d5.

Обратите внимание: Git сам собрал правку index.html из ветки и правку README.md из main. Даже если две ветки меняют один файл, но в разных местах, Git тоже соединит их сам. Сложность возникает, только когда обе ветки поменяли одни и те же строки, — это конфликт, и ему посвящён следующий урок.

Флаг --no-ff

Команда git merge --no-ff имя-ветки создаёт merge-коммит, даже когда перемотка возможна. Некоторые команды так делают, чтобы в истории было видно, где жила ветка. Нам пока хватит поведения по умолчанию.

Удаляем слитые ветки

После слияния ветка своё отработала: её коммиты уже в main. Ветка — лишь метка, поэтому удаление ветки не удаляет ни одного коммита. Сначала спросим Git, какие ветки уже слиты в текущую:

Терминал
$ git branch --merged
  feature/footer
  feature/projects
* main
$ git branch -d feature/projects
Deleted branch feature/projects (was 5dd4568).
$ git branch -d feature/footer
Deleted branch feature/footer (was 578f2d5).
$ git branch
* main

Разбор по строкам

  • git branch --merged — ветки, все коммиты которых уже есть в текущей ветке. Их можно удалять.
  • git branch -d (от delete) — удалить ветку. (was 578f2d5) — коммит, на который она указывала.
  • * main — осталась одна ветка, а вся работа в ней.

Флаг -d безопасный: неслитую ветку он удалить откажется.

Терминал
$ git branch -d feature/test
error: the branch 'feature/test' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/test'
hint: Disable this message with "git config advice.forceDeleteBranch false"

Разбор по строкам

  • error: the branch 'feature/test' is not fully merged — «ветка слита не полностью»: в ней есть коммиты, которых нет в текущей ветке.
  • hint: ... run 'git branch -D feature/test' — если уверены, удалите с большой -D.

Осторожно: git branch -D

Большая -D удаляет ветку без проверки. Её неслитые коммиты больше не будут видны ни в одной ветке, найти их можно будет только по reflog, и то какое-то время. Прежде чем нажать Enter, проверьте git log --oneline имя-ветки: точно ли эти коммиты не нужны?

Подробно — в документации git merge.

Практика у себя

Если вы выполняли команды по ходу урока, сверьтесь с шагами и итогом.

Шаг 1. git switch feature/projects, добавьте ссылку «Все проекты» в index.html, закоммитьте «Добавить ссылку на страницу проектов».

Шаг 2. git switch main, затем git merge feature/projects. Ожидаемо: Fast-forward и create mode 100644 projects.html.

Шаг 3. git switch -c feature/footer, добавьте подвал перед </body>, закоммитьте «Добавить подвал».

Шаг 4. git switch main, допишите строку в README.md, закоммитьте «Описать страницы в README». git log --oneline --graph --all покажет развилку.

Шаг 5. git merge feature/footer, в редакторе оставьте сообщение Merge branch 'feature/footer', сохраните и закройте. Ожидаемо: Merge made by the 'ort' strategy.

Шаг 6. Удалите обе ветки: git branch -d feature/projects и git branch -d feature/footer. Итоговая история:

Терминал
$ git log --oneline --graph
*   d3c03bd (HEAD -> main) Merge branch 'feature/footer'
|\
| * 578f2d5 Добавить подвал
* | 89766ce Описать страницы в README
|/
* 5dd4568 Добавить ссылку на страницу проектов
* 294653e Добавить страницу проектов
* 5acc5ae Revert "Поменять цвет фона"
...

Проверьте себя

  • git branch показывает только * main.
  • В git log --oneline --graph есть merge-коммит Merge branch 'feature/footer' с двумя линиями под ним.
  • В папке есть projects.html, а на главной в браузере — ссылка «Все проекты» и подвал.
  • git status: nothing to commit, working tree clean.

Частые ошибки

Слили «не в ту сторону». Вы стояли на feature/footer и выполнили git merge main. Тогда коммиты main пришли в ветку, а сам main не изменился. Признак — первая строка git status: On branch feature/footer. Ничего не сломано: перейдите на main (git switch main) и слейте как надо: git merge feature/footer. Перед каждым слиянием проверяйте, на какой вы ветке.

Не удаляли ветки. Через месяц в git branch десяток имён, и непонятно, какие ещё в работе. Удаляйте ветку сразу после слияния; git branch --merged подскажет, какие уже не нужны.

not fully merged при -d. Git защищает коммиты, которых нет в текущей ветке. Возможно, вы стоите не на main или забыли слить ветку. Проверьте git status и git log --oneline --graph --all, и только если коммиты точно не нужны — -D.

Already up to date. Git отвечает так, если все коммиты ветки уже есть в текущей: сливать нечего. Это не ошибка.

Что важно запомнить

  • Сливают в текущую ветку: git switch main, затем git merge имя-ветки.
  • Fast-forward («перемотка») — main не уходил вперёд, Git просто двигает метку; нового коммита нет.
  • Merge-коммит — обе ветки ушли вперёд; у такого коммита два родителя, сообщение по умолчанию Merge branch '…'.
  • Разные места в файлах Git соединяет сам.
  • Слитые ветки удаляют git branch -d; -D удаляет без проверки — осторожно.

Пока Git всё сливал сам: ветки меняли разные места. А что, если две ветки поменяют одну и ту же строку? Тогда Git остановится и попросит решить, какая версия верная. Это конфликт слияния — в следующем уроке разберём его спокойно и по шагам.

На следующий урок