Git – это распределенная система контроля версий с открытым исходным кодом. С ее помощью вы можете легко управлять файлами проекта, локально создавая и обслуживая ветки, подготавливая изменения к фиксации и управляя рабочими процессами.
На сегодняшний день многие разработчики используют Git, и они обычно знакомы с такими базовыми концепциями Git, как:
- создание репозитория
- создание ветки
- подготовка изменения к фиксации или его отмена
- фиксация изменения
- отправка коммита в удаленный репозиторий
Однако многие разработчики не знают, что такое «слияние» и «разрешение конфликтов слияния». В этой статье мы рассмотрим на практике, как разрешать конфликты слияния. Это значит, что, прочитав эту статью, вы поймете, что означают эти понятия, и сможете применять их на практике.
Что разработчики говорят о «конфликтах слияния»?
Недавно я провел опрос в Twitter, LinkedIn и YouTube. Я хотел узнать, удобно ли разработчикам разрешать конфликты слияния в Git. И угадайте, что я обнаружил?
Примерно 70-80% разработчиков сказали, что им не так-то просто разрешать конфликты слияния в Git. Это значит, что «разрешение конфликтов слияния» является довольно важной темой, которую нужно обсудить.
Результаты опросы – Удобно ли вам разрешать конфликты в Git?
Что такое Git merge и конфликты слияния?
Git – это система контроля версий, которая хранит историю версий всех ваших файлов. Вы можете вернуться к любой из версий в любое время и извлечь, таким образом, более старую версию.
Предположим, вы создали файл abc.txt и поместили его в репозиторий Git. На этом этапе файл имеет свою текущую версию, привязанную к нему. Теперь, если ваш коллега изменит этот файл и отправит его обратно в репозиторий, то к этому файлу будет привязана новая версия.
Git merge – это функция, которая позволяет синхронизировать текущее содержимое файла с предыдущими версиями. Это крайне важная функция, так как любой человек в любой момент времени должен работать с самой последней версией содержимого файла, не переписывая при этом другие изменения из предыдущих версий.
Git merge помогает вам объединить изменения, которые были внесены другими разработчиками, прежде чем вы начнете вносить новые изменения в тот же файл.
Если мы говорим о Git merge, нам следует помнить о двух вещах:
- Изменения: какие действия были выполнены между двумя версиями файла? Добавляется или удаляется новое содержимое, или обновляется текущее содержимое.
- Ситуации: есть два варианта. Изменения могли произойти в одной и той же или разных областях файла. Одна и та же область говорит о том, что разработчики внесли изменения в одном и том же месте (например, в одном абзаце, строке и т.д.) файла.
К счастью, Git позаботился о большинстве таких случаев. Он использует автоматическое слияние. Однако, если изменения произошли в одной и той же области файла, Git не будет его выполнять. Вместо этого вам придется разрешать конфликт слияния.
Конфликты слияния в Git: страшилка
Давайте попробуем разобраться в том, что написано выше, на примере историй двух разработчиков, Алекса и Тины.
В один прекрасный день
- Алекс извлек изменения из удаленного репозитория в свой локальный.
- Он изменил файл abc.txt, проиндексировал его, зафиксировал изменения, и, наконец, отправил обратно в удаленный репозиторий.
- Тем временем Тина, не зная о том, что Алекс внес изменения в файл abc.txt, внесла некоторые свои изменения в ту же область файла и попыталась отправить его в удаленный репозиторий.
- Git – это система контроля версий, и она предупредила Тину, что она внесла изменения в версию, более старую, нежели та, что хранилась в удаленном репозитории (так как изменения Алекса уже были там).
- Теперь Тина должна сначала извлечь изменения с удаленного репозитория, обновить файл, а затем попытаться снова отправить изменения.
- Тина сделала это. Однако, как в ее самом диком кошмаре, она получила предупреждение о том, что автоматическое слияние выполнить не удалось, и ей нужно разрешить конфликт слияния.
Эта ситуация вам что-то напоминает? Сталкивались ли вы с подобными ситуациями? Были ли вы когда-то на месте Тины? Если нет, то рано или поздно это все равно случиться! Итак, давайте разберемся, как Тине справиться с этой ситуацией максимально эффективно.
Как разрешать конфликты слияния в Git
Процесс разрешения конфликтов слияния не так сложен, как может показаться. В 90% случаев он будет еще проще, если вы будете максимально спокойны, и у вас будет четкое понимание того, как происходят изменения.
Решение
Как только Тина получит изменения, в ее локальном файле будут как ее изменения, так и изменения, внесенные Алексом. И теперь Тина может сделать что-то одно из следующего списка:
- Она может сохранить изменения Алекса и удалить свои
- Она может удалить изменения Алекса и сохранить свои
- Она может сохранить как изменения Алекса, так и свои
- Она может удалить как изменения Алекса, так и свои
Допустим, но что ей все-таки нужно сделать? Это всецело зависит от потребностей проекта и сценариев использования. Тина должна оценить входящие изменения и сделать все, что необходимо в данной ситуации.
Так, а что такое входящие изменения? Как Тина может их определить? И как Тина будет вносить изменения? Я прекрасно понимаю, что у вас таких вопросов навалом. Давайте попробуем на них ответить, рассмотрев несколько примеров из реальной жизни в следующем разделе.
Процесс разрешения конфликтов слияния в Git
Давайте рассмотрим пару реальных примеров конфликтов слияния и посмотрим, как их можно разрешить.
Пример 1. Изменения в одной области файла
Когда Git не может выполнить автоматическое слияние по причине того, что изменения находятся в одной и той же области, он обозначает «конфликтующие» области специальными символами.
<<<<<<< ======= >>>>>>>
Все, что находится между <<<<<<< и =======, является вашими локальными изменениями. Этих изменений еще нет в удаленном репозитории. Все строки, которые находятся между ======= и >>>>>>>, - это изменения из удаленного репозитория или какой-то другой ветки. Теперь вам нужно изучить эти два пункта и принять решение.
На изображении ниже продемонстрировано содержимое файла, которое дает нам понять, что автоматического слияния не произошло, и существует конфликт. Конфликт расположен в строке, где мы локально изменили файл, добавив строку - Sleep. Только одновременно с этим кто-то еще внес свои изменения в ту же область файла, добавив строку - Gym.
Выходит, что строка - Sleep помечается как локальное изменение, а - Gym - как входящее изменение из удаленного репозитория или другой ветки.
Конфликт, возникший вследствие внесения изменений в одной и той же области файла
В зависимости от вашего сценария использования и потребностей проекта, вы должны принять соответствующее решение для того, чтобы разрешить конфликт. Если вам нужно сохранить только строку - Sleep, то вы сохраняете ее, а остальные «конфликтующие» строки удаляете. В таком случае содержимое файла станет таким:
- Eat
- Read
- Sleep
Или наоборот, вы можете сохранить строку - Gym и удалить строку - Sleep:
- Eat
- Read
- Gym
Если вам нужно сохранить обе строки, удалите строки, которые вызывают конфликт:
- Eat
- Read
- Sleep
- Gym
Если вы считаете, что ни одно из изменений вам не нужно, удалите их все:
- Eat
- Read
Это только ваше дело, какие изменения оставить, а какие удалить. После того, как вы внесете все изменения, необходимо убедиться, что в файле нет ни одного символа, который указывает на конфликт (<<<<<<<, =======, >>>>>>>). Как только вы определитесь со всеми изменениями, сделайте следующее:
Подготовьте изменения к фиксации:
git add <files>
Зафиксируйте изменения с комментарием «Message»:
git commit -m "Message"
И наконец, отправьте изменения в удаленный репозиторий:
git push
В данной ситуации это все, что вам нужно сделать для того, чтобы разрешить конфликт.
Пример 2. Файл был удален из удаленного репозитория/другой ветки
Конфликт слияния удаленных файлов – это когда разработчик удаляет файл в одной ветке, тогда как другой разработчик редактирует тот же файл в другой ветке. В таком случае вам нужно решить, хотите ли вы сохранить файл или вы вполне можете его удалить.
Для того, чтобы снова добавить удаленный файл в вашу ветку, запустите следующую команду:
git add <file-name>
А для того, чтобы окончательно его удалить, запустите вот эту команду:
git rm <file-name>
После чего зафиксируйте изменения с комментарием «Message»:
git commit -m "Message"
И наконец, отправьте изменения:
git push
Что дальше?
Если вы усвоите два приведенных выше примера и попробуете применить их, то сможете справиться с большинством ситуаций и разрешить конфликты слияния. Именно поэтому я рекомендую отработать их пару раз на практике.
Прежде чем мы подведем итоги, хочу дать вам несколько советов:
- Во всех примерах, продемонстрированных в этой статье, предполагается, что для разрешения конфликтов вы используете GitBash или любой другой интерфейс командной строки для Git. В целом, вы можете использовать любой другой инструмент с графическим интерфейсом.
- Прежде чем приступать к работе над кодом, всегда извлекайте данные из удаленных репозиториев/других веток. Это позволит поддерживать вашу ветку в актуальном состоянии и уменьшит вероятность возникновения конфликтов.
- Всегда синхронизируйте данные перед их отправкой, чтобы убедиться, что Git не отклонит ваш запрос.
- Если вы не можете решить, какие изменения сохранить, а какие удалить, посоветуйтесь со своими коллегами/соразработчиками. Разбейтесь на пары, чтобы вам было проще разрешать любые сложные конфликты слияния.
Пока что это все. Я надеюсь, что вы узнали из этой статьи много нового, и она оказалась для вас полезной, а также помогла вам справиться с разрешением конфликтов слияния в Git.