git merge
На этой странице вы найдёте информацию о команде git merge, принципах её работы и различии между fast-forward и трёхсторонним слиянием.
Команда git merge объединяет две или более линий разработки. Когда вы заканчиваете работу над функциональностью в отдельной ветке, git merge берёт изменения из этой ветки и вносит их в текущую ветку, создавая единую объединённую историю.
На этой странице рассматривается, что происходит во время слияния, в чём разница между fast-forward и 3-сторонним слиянием, как принудительно создать коммит слияния и как распознать и разрешить конфликт слияния.
Команда тесно связана с git branch (для создания и удаления веток), git checkout (для переключения на принимающую ветку) и git pull (для обновления перед слиянием). Если вы предпочитаете линейную историю без коммитов слияния, рассмотрите git rebase как альтернативу.
Принцип работы
Основное применение git merge — объединение двух веток. Команда также используется для интеграции нескольких коммитов из одной ветки в другую. На следующей иллюстрации git merge берёт вершины двух веток и находит общий предковый коммит между ними. Этот общий базовый коммит используется для создания нового коммита слияния, объединяющего изменения из обеих веток. Здесь у нас две ветки: master и stage. Нам нужно слить ветку stage в ветку master.

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

Процесс слияния
Перед началом слияния выполните следующие шаги:
- Убедитесь, что вы находитесь на правильной принимающей ветке. Выполните
git checkout <receiving branch>для переключения на неё. - Обновите целевую ветку последними удалёнными изменениями. Выполните
git pull, чтобы загрузить и интегрировать последние удалённые коммиты. - Последний шаг — выполнить
git merge <branch name>, указав ветку, которую нужно слить в принимающую ветку.
Fast-forward слияние
Fast-forward слияние происходит, когда путь от текущей ветки до целевой является линейным. При fast-forward слиянии истории объединяются, поскольку все коммиты, достижимые из целевой ветки, доступны через текущую ветку. Вот пример fast-forward слияния:

Когда две истории расходятся, Git использует трёхстороннее слияние в качестве альтернативы. 3-стороннее слияние использует специальный коммит для объединения двух историй.

Fast-forward слияния, как правило, используются для небольших функций или исправлений ошибок, тогда как 3-сторонние слияния применяются для интеграции долгосрочных функций. В следующих примерах используется fast-forward слияние:
git merge
# Start the stage
git checkout -b stage master
# Edit some files
git add <file>
git commit -m "Start with the stage"
# Edit some files
git add <file>
git commit -m "Finish with the stage"
# Merge in the stage branch
git checkout master
git merge stage
git branch -d stageМы выполняем git branch -d, чтобы удалить ветку stage, поскольку теперь она доступна из ветки master.
Команда git merge с опцией --no-ff используется, если при fast-forward слиянии требуется создать коммит слияния: она сливает указанную ветку в текущую, всегда создавая коммит слияния (даже при возможности fast-forward):
git merge --no-ff
git merge --no-ff <branch>3-стороннее слияние
Этот сценарий требует трёхстороннего слияния, когда ветка master продвигается вперёд, пока ветка stage ещё в разработке. Это используется, когда члены команды одновременно работают над крупной функциональностью:
the git merge command
# Start the stage
git checkout -b stage master
# Edit some files
git add <file>
git commit -m "Start with the stage"
# Edit some files
git add <file>
git commit -m "Finish with the stage"
# Develop the master branch
git checkout master
# Edit some files
git add <file>
git commit -m "Make some super-stable changes to master"
# Merge in the stage branch
git merge stage
git branch -d stageВ приведённом примере stage — крупная функциональность, на разработку которой уходит много времени, поэтому мы используем трёхстороннее слияние. Если ваша функциональность небольшая, лучше воспользоваться fast-forward слиянием, чтобы не засорять историю проекта лишними коммитами.
Полезные опции слияния
Эти флаги изменяют поведение git merge:
| Опция | Что делает |
|---|---|
--no-ff | Всегда создаёт коммит слияния, даже если возможен fast-forward. Сохраняет видимость ветки функциональности в истории. |
--ff-only | Выполняет слияние только если возможен fast-forward; иначе прерывает. Полезно в скриптах для отказа от коммита слияния. |
--squash | Объединяет все коммиты из ветки в единый набор подготовленных изменений (затем вы делаете коммит вручную). Коммит слияния и второй родитель не создаются. |
--abort | Останавливает конфликтное слияние и восстанавливает ветку в состояние до слияния. |
-m "<msg>" | Задаёт сообщение коммита слияния напрямую, не открывая редактор. |
Squash-слияние удобно, когда ветка функциональности содержит много небольших коммитов «в процессе работы», которые не нужны в основной истории:
git merge --squash
git checkout master
git merge --squash stage
git commit -m "Add stage feature"Разрешение конфликтов
При слиянии двух веток, если одна и та же часть одного и того же файла изменена в обеих ветках, возникают конфликты слияния, поскольку Git не может определить, какую версию использовать. В этом случае Git останавливается перед созданием коммита слияния, чтобы вы могли разрешить конфликт. Процесс слияния в Git использует рабочий процесс редактирование/добавление в индекс/коммит для разрешения конфликтов. При возникновении конфликта команда git status покажет файлы, требующие разрешения. Следующая картина отобразится, если одни и те же части файла example.txt были изменены:
git status
On branch master
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
both modified: example.txtЕсли вы решили не продолжать слияние, вы можете отменить его в любой момент, выполнив git merge --abort.
Как представлены конфликты
В случае конфликтов Git редактирует содержимое затронутых файлов, добавляя визуальные маркеры с обеих сторон конфликтующего содержимого. Конфликты могут возникать только при трёхстороннем слиянии — fast-forward слияние никогда не порождает конфликтов, так как принимающая ветка не имеет собственных новых коммитов, способных столкнуться с входящими изменениями.
Основные маркеры — <<<<<<<, ======= и >>>>>>>. Они помогают найти конфликтующие секции в файлах.
git conflicts
here is some content not affected by the conflict
<<<<<<< master
this is conflicted text from master
=======
this is conflicted text from stage branch
>>>>>>> stageЧтобы разрешить конфликт, откройте файл, удалите три строки с маркерами (<<<<<<<, =======, >>>>>>>), и отредактируйте оставшийся текст до нужной вам версии. Затем выполните git add <file> для конфликтующего файла, чтобы отметить его как разрешённый, и запустите git commit для создания коммита слияния.
Полный рабочий пример
Приведённая ниже сессия создаёт две ветки, изменяющие одну строку, сливает их, сталкивается с конфликтом, разрешает его и завершает слияние. Вы можете вставить эти команды в любой пустой каталог и воспроизвести всё точь-в-точь.
reproduce a conflict and resolve it
git init demo && cd demo
echo "line one" > file.txt
git add file.txt
git commit -m "Initial commit"
# Create a branch and change the line there
git checkout -b stage
echo "change from stage" > file.txt
git commit -am "Edit on stage"
# Change the same line on master
git checkout master
echo "change from master" > file.txt
git commit -am "Edit on master"
# Merge stage into master -> conflict
git merge stage
# Auto-merging file.txt
# CONFLICT (content): Merge conflict in file.txt
# Automatic merge failed; fix conflicts and then commit the result.После редактирования file.txt и сохранения нужной версии завершите слияние:
git add file.txt
git commit -m "Merge stage, keep resolved line"
git branch -d stageПроверка слияния
После слияния убедитесь в корректности результата с помощью git log. Флаги --graph и --oneline отображают топологию веток, что позволяет легко найти коммит слияния (с двумя родителями):
git log --oneline --graphЕсли вы решите, что завершённое слияние было ошибкой, git reset может переместить ветку обратно к коммиту, предшествующему слиянию.