
2024-05-10
Как решать конфликты в мерж реквесте
2023-11-07
Всем привет, друзья.
Сегодня мы рассмотрим вопрос как решать конфликты git при мерже или слиянии веток. У многих новичков в git появляется страх перед этим событием и когда оно впервые наступает - случается паника, безысходность, тлен и небытие. Человек начинает судорожно пытаться что-то править, применять рандомные команды, что приводит к еще более плачевному результату и переделке всей ветке целиком. Первым что надо делать - это не паниковать и осуществлять действия максимально осознанно. Далее мы рассмотрим какие варианты действий у нас есть.
Для решения конфликтов есть несколько вариантов:
-- через консоль
-- через интерфейс PhpStorm
-- пересборка ветки с нуля
Давайте разберем, откуда вообще появляются конфликты в ветках. Конфликты возникают, когда в разных ветках редактируются одни и те же файлы в одних и теж же местах. Сейчас разберем на примере консольной команды симфони, создадим конфликт веток и постараемся его решить разными способами.
Создаем консольную команду TestCommand на ветке master, коммитим и пушим ее в мастер со следующим содержимым:
Теперь создаем ветку develop с текущего момента и уже будучи на ветке develop вносим изменения в консольную команду следующим образом, делаем коммит и пушим ветку:
Теперь переключаемся на ветку master и вносим изменения в эти же строки, но другой код например так:
Теперь делаем коммит на ветке мастер, пушим его и пытаемся сделать следующее:
git checkout develop
git merge master
После выполнения этих команд мы получим конфликт слияния веток и выглядеть это будет следующим образом:
На картинке в консоли мы видим надпись "КОНФЛИКТ (содержимое): Конфликт слияния в src/Console/TestCommand.php". А в самом файле мы видим, что у нас туча подчеркнутого кода и какие-то странные надписи "<<<<<<< HEAD", "=======" и ">>>>>>> master".
В таком виде конфликт отображается в коде. Раздел от HEAD до значков равно отображает код, который был реализован на вашей ветке ( develop ( та ветка куда происходит merge ) ). Раздел от значков равно и до именования второй ветки - это тот код, который был выполнен в другой ветке, но на тех же строчках, что и ваш код, в данном случае в мастер ветке.
По итогу git не может определить какой именно код ему нужно оставить и предлагает вам сделать этот выбор.
При этом, если сейчас в консоли выполнить git status - то нам будет отображена информация о том, что мы находимся в состоянии резолва конфликта веток. Выглядеть это будет так:
Из этого состояния у нас есть два пути - либо выполнить команду
git merge --abort
И в этом случае мы выйдем из режима резолва конфликтов, но при этом и команда git merge master не будет выполнена - это действие будет откачено назад и конфликты по веткам так и останутся не решенными. Либо нужно разрезолвить конфликты и сделать коммит.
Теперь перейдем к вариантам решения конфликтов...
PhpStorm имеет довольно неплохой интерфейс резолва конфликтов git. Чтобы его открыть - находясь в состоянии резолва конфликта нужно кликнуть правой кнопкой мыши в любой части кода, выбрать Git и выбрать Resolve Conflicts:
У вас откроется окно со списком всех файлов где есть конфликты:
В этом окне если кнопки быстрых действий "Accept Yours" и "Accept Theirs" - это кнопки, позволяющие быстро принять изменения с одной из сторон при этом стереть изменения другой стороны. Кнопка Accept Yours оставляет ваши изменения в ветке develop и удаляет изменения из мастера, а Accept Theirs соответственно делает наоборот. Я рекомендую обращаться с этими кнопками максимально аккуратно, потому что часто они приводят к неработоспособности кода, потому что эти кнопки не резолвят конфликт, а просто удаляют куски кода.
Чтобы реить конфликт - нажимаем на конкретный файл в списке двумя кликами и нам откроется такое окно:
В этом окне мы видим указание веток сверху в левом и правом столбцах. Посередине отображается столбец итогового файла как он будет выглядеть после всех изменений, а так же кнопки быстрых действий с крестиком и стрелочками. Крестик помечает изменения как ненужные, а стрелочки добавляют изменения в средний столбец.
Давайте например будем думать, что нам нужны и те и другие изменения и нажмем стрелочки в обоих столбцах и слева и справа. Сначала нажимаем слева и получаем такую картину:
Обратите внимание, что теперь подсветка diff у нас изменилась и в diff нам теперь подсвечаются только тексты, а не целые конструкции print_r(); Теперь нам необходимо как-то вклеить часть, которая находится справа ( так как этот код тоже кто-то писал и он нужен ). Если мы нажмем на стрелочки справа ( которые кстати стали наклонными, что значит, что код добавится ниже ) - то код из правой колонки добавится после только что добавленного кода из develop. По итогу у нас получится в средней колонке код из двух веток собранный один за другим и надпись сверху:
Надпись All changes have been processed. Save changes and finish merging появляется только в том случае, если все конфликты во всех файлах были зарезолвлены. Если в других файлах еще остались конфликты, то этой надписи не будет. Сейчас мы можем либо нажать на надпись Savechanges and finish merging либо нажать на кнопку Apply и выйти в окно редактирования кода.
Допустим мы нажали apply - теперь выходим в окно редактирования кода, в консоли запускаем команду git status и видим, что конфликт зарезолвлен, мы можем сделать коммит с исправленным кодом например так:
git commit -m 'fix merge conflicts'
И пушим ветку. На этом конфликт можно считать зарезолвленным.
Теперь попробуем решить конфликты без использования PhpStorm нтерфейсов и вернемся к тому месту, когда у нас был конфликт в коде:
Чтобы зарезолвить конфликт без использования интерфейсов - достаточно просто корректно отредактировать код следующим образом ( удаляем стрелочки HEAD, значки равно и все что не является php кодом ):
После редактирования кода выполняем команды:
git add .
git commit -m 'fix git conflicts'
git push
На этом всё - ручной резолв конфликтов завершён.
Такой способ хорош в том случае, когда конфликтов мало и они простые.
Данный метод обычно подходит в следующем сценарии. Например вы работаете в микросервисной архитектуре, выполнили какую-то задачу и у вас 4 мерж реквеста в разные компоненты по одной задаче. Вы задачу выполнили в августе месяце, а у QA руки до нее дошли только в конце ноября. В итоге все ваши ветки протухли настолько, что конфликты есть по ВСЕМ файлам в 75% строк кода, которые вы писали и изменений заехало столько, что резолвить их попросту не получается.
В этом случае более разумно просто отказаться от идеи резолва конфликтов и выполнить задачу заново. В этот раз вы смотрите в свой старый мерж реквест - стараетесь выполнить все те же самые действия, но уже в новой ветке сделанной от свежих основных веток (develop/master) и таким образом пересобираете новую ветку с нуля.
Это довольно крайний случай, но подобное тоже иногда встречается. Поэтому старайтесь не запускать свои ветки на несколько месяцев, так как в основные ветки других компонентов вливаются ветки других компонентов, а ваша ветка в это время протухает и теряет шансы нормально заехать без конфликтов. Особенно актуально это для тех веток, где много изменений по коду не в одну строчку кода.
С какого-то момента времени я начал практиковать разработку по всем задачам по правилу 1 ветка = 1 коммит. Для выполнения данного правила я использую команду git rebase -i HEAD~2 ( если надо схлопнуть 2 коммита ). Например задача пришла в работу, я делаю первый коммит с нормальным названием вида 'TTR-1025 some commit message' где TTR-1025 это номер задачи, а все последующие коммиты, которые мне требуется сделать - я сразу сквошу через интерактивный ребейз. Делается это примерно так:
//задача пришла в работу
//переключаюсь на develop
git checkout develop
//скачиваю обновления, чтобы develop был свежий
git pull -r
//создаю новую ветку по номеру задачи
git checkout -b TTR-1025
//добавляю все изменения сделанные в процессе работы
git add .
//создаю первый коммит
git commit -m 'TTR-1025 some feature description'
//пушу ветку впервые
git push -u origin TTR-1025
// еще какая-то разработка
//снова добавляю еще какие-то доработки
git add .
//делаю второй коммит с рандомным комментарием
git commit -m 'fix'
//делаю интерактивный ребейс во время которого для
//второго коммита вместо pick проставляю squash
git rebase -i HEAD~2
//делаю пуш с форсом
git push -f
При вызове интерактивного ребейса в консоли будет открываться редактор. Сначала одно окно, где необходимо заменить все pick на squash кроме первого коммита (самого верхнего) и сохранить - далее откроется второй редактор и предложит отредактировать комментарий и также нужно будет сохранить.
Таким образом у вас будет всегда сохраняться всего 1 коммит.
Но зачем это вообще надо и какие дает преимущества.
Во-первых, 1 коммит намного проще вымержить из любой другой ветки в случае необходимости ( например в случае, когда на релизной ветке нашли баг в какой-либо задаче ).
Во-вторых, когда вы делаете ребейз от основной ветки, в случае, если у вас много коммитов - то конфликты вам придется резолвить для каждого коммита, где они возникают, а не только для последней версии кода. Например если у вас в ветке 40 коммитов и ваша ветка конфликтует с какой-то другой - то вам придется решать конфликты 40 раз ( причем может быть даже одни и те же ) вместо того, чтобы решить все конфликты всего один раз.
Используйте интерактивный ребейс, друзья! И старайтесь держать ваши коммиты в чистоте с внятными комментариями - ваш тим-лид вам будет благодарен! ))) Надеюсь статья была вам полезна! )))
Ура! Я наконец-то дописал статью как собирать собственные бандлы на Symfony 6!!!
Статья про EasyAdmin всё ещё в процессе )))
Не, ну мне же надо на чем-то тестировать твиттер локальный...
Я тут еще много полезного буду выкладывать, так что заходите обязательно почитать.
Сайтик пока что в разработке - это далеко не окончательная версия - по сути это то что удалось слепить за 8 часов.
Комментарии