Что произойдет при выполнении git merge
Введение в Git Merge и Git Rebase: зачем и когда их использовать
Часто у разработчиков возникает выбор между Merge (слияние) и Rebase (перемещение). В Гугле вы увидите разное мнение, многие советуют не использовать Rebase, так как это может вызвать серьезные проблемы. В статье я объясню, что такое слияние и перемещение, почему вы должны (или не должны) использовать их и как это сделать.
Git Merge и Git Rebase преследуют одну и ту же цель. Они предназначены для интеграции изменений из одной ветки в другую. Хотя конечная цель одинаковая, принципы работы разные.
Некоторые считают, что вы всегда должны использовать Rebase, другие предпочитают Merge. В этом есть свои плюсы и минусы.
Git Merge
Слияние — обычная практика для разработчиков, использующих системы контроля версий. Независимо от того, созданы ли ветки для тестирования, исправления ошибок или по другим причинам, слияние фиксирует изменения в другом месте. Слияние принимает содержимое ветки источника и объединяет их с целевой веткой. В этом процессе изменяется только целевая ветка. История исходных веток остается неизменной.
Плюсы:
Слейте ветку master в ветку feature, используя команды checkout и merge.
Это создаст новый «Merge commit» в ветке feature, который содержит историю обеих веток.
Git Rebase
Rebase — еще один способ перенести изменения из одной ветки в другую. Rebase сжимает все изменения в один «патч». Затем он интегрирует патч в целевую ветку.
В отличие от слияния, перемещение перезаписывает историю, потому что она передает завершенную работу из одной ветки в другую. В процессе устраняется нежелательная история.
Переместите ветку feature на главной ветке, используя следующие команды.
Это перемещает всю ветку функции в главную ветку. История проекта изменяется, создаются новые коммиты для каждого коммита в основной ветке.
Интерактивное перемещение
Это позволяет изменять коммиты при их перемещении в новую ветку. Это лучше, чем автоматическое перемещение, поскольку обеспечивает полный контроль над историей коммитов. Как правило, используется для очистки истории до слияния ветки feature в master.
Это откроет редактор, перечислив все коммиты, которые будут перемещены.
Это точно определяет, как будет выглядеть ветка после выполнения перемещения. Упорядочивая объекты, вы можете сделать историю такой, как захотите. Вы можете использовать команды fixup, squash, edit, и так далее.
Какой из них использовать?
Так что же лучше? Что рекомендуют эксперты?
Трудно принять единственно правильное решение о том, что лучше использовать, поскольку все команды разные. Всё зависит от потребностей и традиций внутри команды.
Принимайте решения на основании компетенции команды в Git. Для вас важна простота или перезаписывание истории, а может быть что-то другое?
По мере роста команды становится сложно управлять или отслеживать изменения в разработке, применяя слияние. Чтобы иметь чистую и понятную историю коммитов, разумно использовать Rebase.
Тонкости благополучного git-merge
Вступительное слово
Основными командами пользовательского уровня для ветвления в Git являются git-branch, git-checkout, git-rebase, git-log и, конечно же, git-merge. Для себя я считаю git-merge зоной наибольшей ответственности, точкой огромной магической энергии и больших возможностей. Но это достаточно сложная команда, и даже достаточно длительный опыт работы с Git порой бывает недостаточным для освоение всех ее тонкостей и умения применить ее наиболее эффективно в какой-либо нестандартной ситуации.
Попробуем же разобраться в тонкостях git-merge и приручить эту великую магию.
Здесь я хочу рассмотреть только случай благополучного слияния, под которым я понимаю слияние без конфликтов. Обработка и разрешение конфликтов — отдельная интересная тема, достойная отдельной статьи. Я очень рекомендую так же ознакомиться со статьей Внутреннее устройство Git: хранение данных и merge, содержащей много важной информации, на которую я опираюсь.
Анатомия команды
Если верить мануалу, команда имеет следующий синтаксис:
По большому счету, в Git есть два вида слияния: перемотка (fast-forward merge) и «истинное» слияние (true merge). Рассмотрим несколько примеров обоих случаев.
«Истинное» слияние (true merge)
Мы отклоняемся от ветки master, чтобы внести несколько багов улучшений. История коммитов у нас получилась следующая:
Выполним на ветке master git merge feature :
Посмотрим историю коммитов в тестовом репозитории, который я создал специально для этого случая:
А теперь посмотрим информацию о коммите (M):
Мы видим двух родителей, объект-дерево, соответствующее данному состоянию файлов репозитория, а так же информацию о том, кто виновен в коммите.
Посмотрим, куда ссылается указатель master:
Действительно, он теперь передвинут на коммит (M).
Squash и no-commit
В случае применения такого слияния коммиты ветки feature не будут включены в нашу историю, но коммит Sq будет содержать все их изменения:
Позже, в случае выполнения «классического» git merge feature можно исправить это. Тогда история примет следующий вид:
Перемотка (fast-forward merge)
Рассмотрим другой случай истории коммитов:
Все как и в прошлый раз, но теперь в ветке master нет коммитов после ответвления. В этом случае происходит слияние fast-forward (перемотка). В этом случае отсутствует коммит слияния, указатель (ветка) master просто устанавливается на коммит Y, туда же указывает и ветка feature:
Стратегии слияния
Стратегия resolve
Здесь C — общий коммит двух веток, дерево файлов, соответствующее этому коммиту, принимается за общего предка. Анализируются изменения, произведенные в ветках master и feature со времен этого коммита, после чего для коммита (M) создается новая версия дерева файлов в соответствии с пунктами 4 и 5 нашего условного алгоритма.
Стратегия recursive
Для иллюстрации этой стратегии позаимствуем пример из статьи Merge recursive strategy из блога «The plasticscm blog»:
Итак, у нас есть две ветки: main и task001. И так вышло, что наши разработчики знают толк в извращениях: они слили коммит 15 из ветки main с коммитом 12 из ветки task001, а так же коммит 16 с коммитом 11. Когда нам понадобилось слить ветки, оказалось, что поиск реального предка — дело неблагодарное, но стратегия recursive с ее конструированием «виртуального» предка нам поможет. В результате мы получим следующую картину:
]
Выполнение рекурсивного слияния с этой опцией будет более продвинутым вариантом стратегии subtree, где алгоритм основывается на предположении, как деревья должны совместиться при слиянии. Вместо этого в этом случае указывается конкретный вариант.
Стратегия octopus
Эта стратегия используется для слияние более чем двух веток. Получившийся в итоге коммит будет иметь, соответственно, больше двух родителей.
Данная стратегия предполагает большую осторожность относительно потенциальных конфликтов. В связи с этим порой можно получить отказ в слиянии при применении стратегии octopus.
Стратегия ours
Не следует путать стратегию ours и опцию ours стратегии recursive.
Стратегия ours — более радикальное средство.
Стратегия subtree
Для иллюстрации данной стратегии возьмем пример из главы Слияние поддеревьев книги «Pro Git».
Добавим в наш проект новые удаленный репозиторий, rack:
Ясно, что ветки master и rack_branch имеют абсолютно разные рабочие каталоги. Добавим файлы из rack_branch в master с использованием squash, чтобы избежать засорения истории ненужными нам фактами:
Теперь файлы проекта rack у нас в рабочем каталоге.
Введение в Git Merge и Git Rebase: зачем и когда их использовать
Git Merge и Git Rebase преследуют одну и ту же цель. Они предназначены для интеграции изменений из одной ветки в другую. Хотя конечная цель одинаковая, принципы работы разные.
Некоторые считают, что вы всегда должны использовать Rebase, другие предпочитают Merge. В этом есть свои плюсы и минусы.
Слияние — обычная практика для разработчиков, использующих системы контроля версий. Независимо от того, созданы ли ветки для тестирования, исправления ошибок или по другим причинам, слияние фиксирует изменения в другом месте. Слияние принимает содержимое ветки источника и объединяет их с целевой веткой. В этом процессе изменяется только целевая ветка. История исходных веток остается неизменной.
Как это сделать
Слейте ветку master в ветку feature, используя команды checkout и merge.
Это создаст новый «Merge commit» в ветке feature, который содержит историю обеих веток.
Rebase — еще один способ перенести изменения из одной ветки в другую. Rebase сжимает все изменения в один «патч». Затем он интегрирует патч в целевую ветку.
В отличие от слияния, перемещение перезаписывает историю, потому что она передает завершенную работу из одной ветки в другую. В процессе устраняется нежелательная история.
Если вы сделаете перемещение неправильно, история изменится, а это может привести к серьезным проблемам, поэтому убедитесь в том, что делаете!
Как это сделать
Переместите ветку feature на главной ветке, используя следующие команды.
Это перемещает всю ветку функции в главную ветку. История проекта изменяется, создаются новые коммиты для каждого коммита в основной ветке.
Это позволяет изменять коммиты при их перемещении в новую ветку. Это лучше, чем автоматическое перемещение, поскольку обеспечивает полный контроль над историей коммитов. Как правило, используется для очистки истории до слияния ветки feature в master.
Это откроет редактор, перечислив все коммиты, которые будут перемещены.
Это точно определяет, как будет выглядеть ветка после выполнения перемещения. Упорядочивая объекты, вы можете сделать историю такой, как захотите. Вы можете использовать команды fixup, squash, edit, и так далее.
Какой из них использовать?
Так что же лучше? Что рекомендуют эксперты?
Трудно принять единственно правильное решение о том, что лучше использовать, поскольку все команды разные. Всё зависит от потребностей и традиций внутри команды.
Принимайте решения на основании компетенции команды в Git. Для вас важна простота или перезаписывание истории, а может быть что-то другое?
Что рекомендую я?
По мере роста команды становится сложно управлять или отслеживать изменения в разработке, применяя слияние. Чтобы иметь чистую и понятную историю коммитов, разумно использовать Rebase.
Git merge
Порядок действий
Команда git merge объединяет несколько последовательностей коммитов в общую историю. Чаще всего команду git merge используют для объединения двух веток. Этот вариант слияния рассматривается в следующих примерах. В таких случаях команда git merge принимает два указателя на коммиты (обычно последние в ветке) и находит общий для них родительский коммит. Затем Git создает коммит слияния, в котором объединяются изменения из обеих последовательностей, выбранных к слиянию.
Коммиты слияния отличаются от других наличием двух родительских элементов. Создавая коммит слияния, Git попытается автоматически объединить две истории. Однако если Git обнаружит, что вы изменили одну и ту же часть данных в обеих историях, сделать это автоматически не удастся. Это называется конфликтом управления версиями, и для его разрешения Git потребуются действия пользователя.
Подготовка к слиянию
Перед слиянием следует предпринять несколько подготовительных действий, чтобы операция прошла без проблем.
Проверка выбора принимающей ветки
Получение последних коммитов из удаленного репозитория
Выполнение слияния
Ускоренное слияние
Ускоренное слияние происходит, когда последний коммит текущей ветки является прямым продолжением целевой ветки. В этом случае для объединения истории Git не выполняет полноценное слияние, а просто переносит указатель текущей ветки в конец целевой ветки. Объединение историй проходит успешно, поскольку все коммиты целевой ветки теперь доступны из текущей ветки. Так, ускоренное слияние одной из функциональных веток с веткой main будет выглядеть следующим образом:
Однако выполнить ускоренное слияние не получится, если ветки после разделения развивались независимо друг от друга. Если до целевой ветки нет прямого пути, Git будет вынужден объединить их методом трехстороннего слияния. Такое слияние выполняется с помощью специального коммита, который служит для объединения двух историй. Метод называется трехсторонним, поскольку Git использует три коммита для создания коммита слияния (последние коммиты двух веток и общий родительский элемент).
Хотя можно применять любую из этих стратегий слияния, многие разработчики предпочитают использовать метод ускоренного слияния (с помощью перебазирования) для небольших функций или исправлений багов, а трехстороннее слияние — для интеграции функций с более продолжительным временем разработки. В последнем случае местом соединения двух веток служит коммит слияния.
В первом примере демонстрируется ускоренное слияние. С помощью кода создается новая ветка, после чего в нее добавляется два коммита. Затем она включается в основную ветку посредством ускоренного слияния.
Это распространенная модель работы с ветками, отведенными под решение краткосрочных задач. Чаще они нужны, чтобы создать изолированную среду для разработчика, нежели для продолжительной работы над объемными функциями.
Эта команда выполнит объединение указанной ветки с текущей с обязательным созданием коммита слияния (даже если слияние будет ускоренным). Это полезно для учета всех слияний в репозитории.
Трехстороннее слияние
Следующий пример похож на предыдущий, но в нем слияние должно быть трехсторонним, поскольку работа над веткой main ведется одновременно с работой над функцией. Так часто происходит в случае объемных функций или когда над одним проектом одновременно работают несколько разработчиков.
Обратите внимание, что Git не может выполнить ускоренное слияние, потому что невозможно перенести указатель main на ветку new-feature без использования предыдущих коммитов.
В большинстве случаев ветка new-feature отводится под более объемные функции с продолжительным временем разработки, за которое в ветке main появляются новые коммиты. Если бы реальный размер вашей функциональной ветки был так же мал, как в приведенном выше примере, было бы проще перебазировать ее на ветку main и выполнить ускоренное слияние. В этом случае не потребовалось бы засорять историю проектов лишними коммитами слияния.
Разрешение конфликтов
При попытке объединить ветки, в которых изменена одна и та же часть того же файла, Git не сможет сделать выбор между версиями. В таком случае операция останавливается прямо перед созданием коммита слияния, чтобы пользователь вручную разрешил конфликты.
Представление конфликтов
Когда Git обнаруживает конфликт в ходе слияния, к затронутым файлам добавляются визуальные индикаторы по обе стороны проблемного содержимого: >>>>>>. Чтобы обнаружить конфликты, попробуйте поискать в проекте эти индикаторы.
Обычно содержимое перед отметкой ======= относится к принимающей ветке, а все, что указано после нее, — к ветке, для которой выполняется слияние.
Обратите внимание, что конфликты возможны только в процессе трехстороннего слияния и не могут возникать при ускоренном слиянии.
Резюме
Готовы попробовать ветвление?
Ознакомьтесь с этим интерактивным обучающим руководством.
Владеешь merge — освой и rebase
Независимо от используемых в проекте стратегий ветвления, приходится регулярно интегрировать изменения из одной ветки в другую. В git это можно сделать двумя основными способами: merge (слияние) и rebase (перебазирование).
В данной статье мы рассмотрим обе операции и отличия между ними, а также обозначим моменты, требующего особого внимания.
Сначала с помощью анимационных средств разберем каждую операцию по отдельности, а на заключительном этапе проведем параллельное сравнение. Если вам уже знакомы принципы работы этих действий, то сразу переходите к сравнительным характеристикам.
Согласно официальному р уководству Git rebase “повторно применяет коммиты поверх другой базовой ветки”, тогда как merge “объединяет две или более историй разработки”. Иначе говоря, основное отличие между ними в том, что слияние сохраняет историю в первозданном виде, а перебазирование ее перезаписывает. Прежде чем переходить к более подробному осмыслению принципов их внутренней работы, обратимся к примеру:
Слияние
Начнем с самого распространенного рабочего процесса интеграции изменений: слияния. Перед объединением изменений Ada с feature-2 Satoshi должен сначала обновить свой локальный указатель на master ветку, поскольку в данный момент она устарела. Как только master и o/master синхронизируются, Satoshi сможет включить все изменения в свою тематическую ветку.
Перебазирование
После повторной интеграции всех изменений Satoshi может продолжить работу над своей тематической веткой.
Понимая главное отличие между слиянием и перебазированием, проанализируем конечные результаты этих операций.
Сравнение слияния и перебазирования
Примечание: каждый раз перебазирование ветки будет сопровождаться созданием новых коммитов, даже если их содержимое останется одним и тем же. Таким образом, прежние коммиты будут в итоге полностью удалены из истории.
С большой силой приходит большая ответственность
Итак, теперь мы знаем, что перебазирование перезаписывает историю, а слияние ее сохраняет. Но что это значит в более широком смысле? Какие возможности и потенциальные недочеты таят в себе две эти операции?
Конфликты при интеграции изменений
Трудноразрешимые конфликты говорят о недостатке общения с коллегами ввиду очень длительной работы над одними и теми же файлами.
Опубликованные ветки
Кроме того, всякий раз при перебазировании уже опубликованной ветки, независимо от того, основывается ли на ней чья-либо работа, вам по прежнему необходимо принудительно опубликовать ее для внесения обновлений в удаленный сервер, тем самым полностью переписывая текущий указатель.
Потеря данных (как преимущество)
Главные правила перебазирования
Во избежание связанных с перебазированием проблем рекомендуется придерживаться следующих правил:
Усложненные случаи перебазирования
Существует множество усложненных операций перебазирования. К числу наиболее эффективных из них относится ранее упомянутый его интерактивный вариант, позволяющий определить способ повторного выполнения каждого коммита.
Данный режим применяется для разделения, сжатия, перегруппировки и даже полного удаления коммитов, и этим его возможности не ограничиваются.
Заключение
Такой подход равносилен высказыванию: “Хоть у меня и отличная машина, но лучше я ограничусь первой передачей, ведь скоростная езда смертельно опасна”. А почему бы не научиться переключать передачи и безопасно передвигаться на высоких скоростях?!
Мой собственный опыт показал, что знание принципов перебазирования углубляет понимание Git, а также развивает вас как разработчика, особенно если речь идет об управлении исходным кодом.
Как-то в самом начале моей карьеры разработчика я получил один из лучших советов от более опытного коллеги. Звучит он так: “Хватит стучать по клавишам в Source Tree, лучше научись использовать команды Git из терминала! Иначе ты не познаешь все возможности Git, и в перспективе не сможешь программировать автоматические конвейеры.”
С тех пор в качестве визуального средства для просмотра дерева истории я предпочитаю только GitK, а все команды набираю в терминале. И вам рекомендую!
Теперь, понимая разницу между слиянием и перебазированием, вы с большей уверенностью начнете применять обе операции.
Благодарю за внимание и желаю удачи в развитии навыков управления исходным кодом.