Использование веток

Git merge

Слияние используется в Git, чтобы собрать воедино разветвленную историю. Команда git merge выполняет слияние отдельных направлений разработки, созданных с помощью команды git branch, в единую ветку.

Обратите внимание: все приведенные ниже команды выполняют слияние в текущую ветку, в то время как целевая ветка остается без изменений. Поэтому команда git merge часто используется в сочетании с командами git checkout (для выбора текущей ветки) и git branch -d (для удаления устаревшей целевой ветки).

Порядок действий

Команда git merge объединяет несколько последовательностей коммитов в общую историю. Чаще всего команду git merge используют для объединения двух веток. Этот вариант слияния рассматривается в следующих примерах. В таких случаях команда git merge принимает два указателя на коммиты (обычно последние в ветке) и находит общий для них родительский коммит. Затем Git создает коммит слияния, в котором объединяются изменения из обеих последовательностей, выбранных к слиянию.

Представим, что у нас есть новая функциональная ветка, которая отходит от главной ветки main. И мы хотим объединить эту функциональную ветку с main.

При вызове этой команды произойдет слияние указанной функциональной ветки с текущей — в данном случае с веткой main. Git автоматически определяет алгоритм слияния (подробнее см. ниже).

Новый узел коммита слияния

Коммиты слияния отличаются от других наличием двух родительских элементов. Создавая коммит слияния, Git попытается автоматически объединить две истории. Однако если Git обнаружит, что вы изменили одну и ту же часть данных в обеих историях, сделать это автоматически не удастся. Это называется конфликтом управления версиями, и для его разрешения Git потребуются действия пользователя.

Подготовка к слиянию

Перед слиянием следует предпринять несколько подготовительных действий, чтобы операция прошла без проблем.

Проверка выбора принимающей ветки

Выполните команду git status. Это позволит убедиться, что HEAD указывает на ветку, принимающую результаты слияния. При необходимости выполните команду git checkout <принимающая-ветка>, чтобы переключиться на принимающую ветку. Для примера выполним команду git checkout main.

Получение последних коммитов из удаленного репозитория

Убедитесь, что в принимающей ветке и ветке для слияния содержатся последние изменения из удаленного репозитория. Выполните команду git fetch, чтобы получить из него последние коммиты. Затем убедитесь, что в ветке main также содержатся последние изменения. Для этого выполните команду git pull.

Выполнение слияния

После указанных выше действий по подготовке можете приступать к слиянию. Для этого выполните команду git merge <название ветки>, где <название ветки> — название ветки, которая будет объединена с принимающей.

Ускоренное слияние

Ускоренное слияние происходит, когда последний коммит текущей ветки является прямым продолжением целевой ветки. В этом случае для объединения истории Git не выполняет полноценное слияние, а просто переносит указатель текущей ветки в конец целевой ветки. Объединение историй проходит успешно, поскольку все коммиты целевой ветки теперь доступны из текущей ветки. Так, ускоренное слияние одной из функциональных веток с веткой main будет выглядеть следующим образом:

После ускоренного слияния главный узел и находящийся после него функциональный узел сошлись в один узел

Однако выполнить ускоренное слияние не получится, если ветки после разделения развивались независимо друг от друга. Если до целевой ветки нет прямого пути, Git будет вынужден объединить их методом трехстороннего слияния. Такое слияние выполняется с помощью специального коммита, который служит для объединения двух историй. Метод называется трехсторонним, поскольку Git использует три коммита для создания коммита слияния (последние коммиты двух веток и общий родительский элемент).


Хотя можно применять любую из этих стратегий слияния, многие разработчики предпочитают использовать метод ускоренного слияния (с помощью перебазирования) для небольших функций или исправлений багов, а трехстороннее слияние — для интеграции функций с более продолжительным временем разработки. В последнем случае местом соединения двух веток служит коммит слияния.

В первом примере демонстрируется ускоренное слияние. С помощью кода создается новая ветка, после чего в нее добавляется два коммита. Затем она включается в основную ветку посредством ускоренного слияния.

# Start a new feature
git checkout -b new-feature main
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Merge in the new-feature branch
git checkout main
git merge new-feature
git branch -d new-feature

Это распространенная модель работы с ветками, отведенными под решение краткосрочных задач. Чаще они нужны, чтобы создать изолированную среду для разработчика, нежели для продолжительной работы над объемными функциями.

Обратите внимание, что теперь Git сможет без проблем выполнить команду git branch -d, поскольку ветка new-feature теперь доступна из главной ветки.

Если при ускоренном слиянии вам понадобится коммит слияния для учета изменений, вы можете выполнить команду git merge с параметром --no-ff.

git merge --no-ff <branch>

Эта команда выполнит объединение указанной ветки с текущей с обязательным созданием коммита слияния (даже если слияние будет ускоренным). Это полезно для учета всех слияний в репозитории.

Трехстороннее слияние

Следующий пример похож на предыдущий, но в нем слияние должно быть трехсторонним, поскольку работа над веткой main ведется одновременно с работой над функцией. Так часто происходит в случае объемных функций или когда над одним проектом одновременно работают несколько разработчиков.

Start a new feature
git checkout -b new-feature main
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Develop the main branch
git checkout main
# Edit some files
git add <file>
git commit -m "Make some super-stable changes to main"
# Merge in the new-feature branch
git merge new-feature
git branch -d new-feature

Обратите внимание, что Git не может выполнить ускоренное слияние, потому что невозможно перенести указатель main на ветку new-feature без использования предыдущих коммитов.

В большинстве случаев ветка new-feature отводится под более объемные функции с продолжительным временем разработки, за которое в ветке main появляются новые коммиты. Если бы реальный размер вашей функциональной ветки был так же мал, как в приведенном выше примере, было бы проще перебазировать ее на ветку main и выполнить ускоренное слияние. В этом случае не потребовалось бы засорять историю проектов лишними коммитами слияния.

Разрешение конфликтов

При попытке объединить ветки, в которых изменена одна и та же часть того же файла, Git не сможет сделать выбор между версиями. В таком случае операция останавливается прямо перед созданием коммита слияния, чтобы пользователь вручную разрешил конфликты.

Преимущество слияния в Git заключается в том, что разрешение конфликтов при слиянии проходит по привычной схеме «редактирование — индексирование — коммит». При обнаружении конфликта выполните команду git status, чтобы увидеть, какие файлы необходимо исправить. Так, если в обеих ветках изменена одна и та же часть файла hello.py, вы увидите следующее:

On branch main
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
both modified: hello.py

Представление конфликтов

Когда Git обнаруживает конфликт в ходе слияния, к затронутым файлам добавляются визуальные индикаторы по обе стороны проблемного содержимого: <<<<<<<, ======= и >>>>>>>. Чтобы обнаружить конфликты, попробуйте поискать в проекте эти индикаторы.

here is some content not affected by the conflict
<<<<<<< main
this is conflicted text from main
=======
this is conflicted text from feature branch
>>>>>>> feature branch;

Обычно содержимое перед отметкой ======= относится к принимающей ветке, а все, что указано после нее, — к ветке, для которой выполняется слияние.

После обнаружения конфликтующих участков кода вы можете исправить их по своему усмотрению. Когда вы будете готовы завершить слияние, выполните команду git add для конфликтующего файла или файлов — так вы сообщите Git, что конфликт разрешен. Затем выполните обычную команду git commit, чтобы создать коммит слияния. Поскольку процесс ничем не отличается от фиксирования обычного снимка состояния, рядовому разработчику не составит труда разрешить конфликты при слиянии.

Обратите внимание, что конфликты возможны только в процессе трехстороннего слияния и не могут возникать при ускоренном слиянии.

Резюме

В этом документе содержатся общие сведения о команде git merge. Слияние — необходимый инструмент для работы в Git. Мы познакомились с принципами его работы, а также обсудили различия между ускоренным и полноценным трехсторонним слиянием. Ниже перечислены основные моменты.

  1. При слиянии в Git последовательности коммитов объединяются в общую историю.
  2. В Git существуют два основных способа объединения изменений: ускоренное и трехстороннее слияние.
  3. Если в обеих последовательностях коммитов нет конфликтующих изменений, Git объединит их автоматически.

В документе также упоминаются другие команды Git: git branch, git pull и git fetch. Подробные сведения о них см. на соответствующих страницах.

Готовы попробовать ветвление?

Ознакомьтесь с этим интерактивным обучающим руководством.

Начните прямо сейчас