в каком случае возникает конфликт слияния

Хватит копировать, пора сливаться. Часть 2. Конфликт слияний, который так и не произошёл (а должен был)

В последний раз мы видели как изменение кода в скопированном коммите создаёт опасность конфликта, который сидит себе тихо пока обе копии где-нибудь не сольются, что может произойти и в очень отдалённом будущем. Но знаете что хуже, чем конфликт слияний?

Отсутствие любого конфликта.

Давайте вернёмся к прежней ситуации:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Представим что ветка «feature» существует уже давно и периодически сливается в «master». Диаграмма показывает срез дерева сразу после последнего слияния А. Мы работаем над спринтом в «feature», полным нового функционала.

Предположим, что строка «apple» находится в файле настроек, управляющим какой-то фичей. Обе ветки фиксируют коммиты M1 и F1, которые не трогают этот файл.

Теперь представим что мы нашли серьёзную ошибку в старой фиче, которая полностью рушит поведение всей программы. Резко пресекая проблему, мы выключаем фичу, меняя строку на «berry» и фиксируя изменения как F2.

В жизни произойдёт что-то похожее на изменение строки c

Но раз уж я начал использовать «apple» и «berry», пусть так они и останутся.

Хорошо, мы отключили фичу, проверили что теперь никаких проблем нет, и скопировали отключение в «master». Фух, теперь можно выдохнуть, кровотечение остановлено, у нас есть время подумать что пошло не так и как это чинить.

Можно также отключить фичу напрямую в «master», а затем скопировать коммит в «feature», картина будет той же.

Пока мы исследуем проблему, работа в «master» ветке продолжается. Позже мы чиним баг, включаем обратно фичу и фиксируем изменения как F3 в «feature» ветке. Дерево коммитов теперь выглядит так:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Новый несвязанный с «berry» коммит M3 был добавлен в ветку «master». В «feature» мы вернули обратно «apple» коммитом F3.

Теперь мы хотим слить ветку «feature» в «master» чтобы временное отключение фичи было заменено нормальным фиксом. Но вместо этого после слияния мы получаем такую картину:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Слияние породило коммит M4, но вопреки ожиданиям строка всё ещё содержит «berry». В «master» перекочевало временное отключение фичи! Даже хуже, в «master» теперь находятся оба фикса: как основной, так и временный. Вполне возможно они плохо друг с другом совмещаются, и появятся только новые баги. Получается, фича работает только в ветке «feature», а в «master» вообще непонятно что.

Разберёмся почему это произошло, в следующей статье я опишу как этого избежать.

Вернёмся к дереву коммитов до слияния:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

При слиянии Git смотрит на базу слияния (merge base): ближайшего родственника между двумя ветками, в нашем случае это коммит А. Затем Git выполняет трёх-стороннее слияние (three-way merge), используя А как базу, M3 как HEAD, а F3 как вливающееся изменение. Теперь нас интересует только дельта между базой и двумя последними коммитами, так что уберём вся лишнее из диаграммы:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Сравнивая базу с верхушкой «master» ветки мы видим что «apple» поменялась на «berry». Сравнение с «feature» не даёт никаких изменений. Так как строка «apple» осталась прежней в «feature», слияние её в «master» также оставит всё как есть. В результате «master» будет содержать «berry» после слияния.

Но это ещё не всё, если затем слить «master» в «feature», то неверная строка «berry» распространится и в «feature»:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Это происходит потому, что теперь меняются местами принимающая и отдающая сторона. В основании (коммит А) строка «apple», в ветке «feature» она остаётся неизменной, но в неё вливают «berry» из «master». И здесь даже не важно, используется быстрая перемотка (fast-forward) или нет.

Многие думают о копировании коммитов как о «частичных слияниях, предвосхищающих будущее полное слияние», когда вам надо слить часть наработок в главную ветвь. Вы ожидаете, что позже, когда вы полностью сольёте исходную ветку в главную, сольются только новые наработки.

Если же вы настолько аккуратны, что не трогаете строк, задействованных при копировании, ожидания оправдаются. Иначе, вместо слияния вы получите наложение изменений друг на друга. Хуже того, если ваши изменения отменяют скопированные, то вы даже не получите конфликт при слиянии, который показал бы что что-то идёт не так. Внутри нашей команды мы называем это АБА проблемой, т.к. сначала строка содержала А, затем её поменяли на Б, Б скопировали, а затем вернули А прямо перед слиянием в «master».

Хорошо, вкратце получается мы хотели сделать частичное слияние из «feature» в «master», но проблема в том, что никакого «частичного» слияния нет.

В следующей статье я покажу как его осуществить.

Источник

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

Azure Repos | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018-TFS 2015

Visual Studio 2019 | Visual Studio 2017 | Visual Studio 2015

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

Из этого руководства вы узнаете, как выполнить следующие задачи:

Общие сведения о конфликтах слияния

На следующем рисунке показан самый простой пример конфликта изменений в Git. Обе ветви Main и бугфикс обновляют одни и те же строки исходного кода.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

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

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Наиболее распространенной ситуацией конфликта слияния является случай, когда вы извлекаете обновления из удаленной ветви в локальную ветвь, например из origin/bugfix в локальную bugfix ветвь. Разрешите эти конфликты аналогичным образом — создайте фиксацию слияния в локальной ветви, чтобы согласовать изменения и завершить слияние.

Что делает Git для предотвращения конфликтов слияния?

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

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

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

Если вы используете Visual Studio 2019 версии 16.8 или выше, опробуйте систему управления версиями Git. Узнайте, чем интерфейс Git отличается от Team Explorer, на странице наглядного сравнения.

Вы будете уведомлены о конфликтах слияния при вытягивание изменений или попытке объединить две ветви.

Появится уведомление о конфликте. Щелкните ссылку » конфликты «, чтобы начать устранение конфликтов файлов.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

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

Используйте флажки рядом со строками, измененными для выбора между удаленными и локальными изменениями, или измените результаты непосредственно в редакторе результатов в исходном и целевом редакторе в представлении diff.

Откройте представление изменения в Team Explorer и зафиксируйте изменения, чтобы создать фиксацию слияния и разрешить конфликт.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

сравните конфликтующие фиксации, а также различия между общим журналом с параметрами в средстве слияния Visual Studio.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Разрешение конфликтов слияния в командной строке:

Раздел является изменениями из одной фиксации, ======= отделяет изменения и >>>>>>> для другой конфликтующей фиксации.

Измените файлы так, чтобы они точно проглядели, как они должны удалять маркеры. Используйте git add для размещения разрешенных изменений.

Разрешение конфликтов при удалении файлов с помощью git add (сохранения файла) или git rm (удаление файла).

Источник

Лучший Pull Request

Относительно недавно мне посчастливилось присоединиться к команде разработки Bitbucket Server в Atlassian (до сентября он был известен как Stash). В какой-то момент мне стало любопытно, как этот продукт освещён на Хабре, и к моему удивлению, нашлось лишь несколько заметок о нём, подавляющее большинство которых на сегодняшний день уже устарело.

В связи с этим я решил опробовать себя в роли рассказчика и затронуть техническую сторону Bitbucket. Прошу не рассматривать моё намерение как попытку рекламы, ибо я совершенно не преследую эту цель. Если эта статья обнаружит интерес со стороны читателей, я буду рад развивать тему и постараюсь ответить на возникающие вопросы.

Позвольте начать с перевода статьи Тима Петтерсена «A better pull request» о том, как должен выглядеть pull request, чтобы наиболее эффективно решать возложенную на него задачу.

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

Мне хотелось бы пользоваться привычными названиями и в письменной речи, однако некоторые термины я не буду даже транслитерировать, поскольку, на мой взгляд, они становятся слишком корявыми, и потому оставлю их в английском написании, — прошу понять и простить. С другой стороны, я открыт критике и предложениям, поэтому если вы считаете, что есть лучший способ выразить тот или иной термин, прошу делиться этими мыслями. Спасибо.

Если вы используете Git, то наверняка пользуетесь и pull request-ами. Они в той или иной форме существуют с момента появления распределённых систем управления версиями. До того, как Bitbucket и GitHub предложили удобный веб-интерфейс, pull request мог представлять собой простое письмо от Алисы с просьбой забрать какие-то изменения из её репозитория. Если они были стóящими, вы могли выполнить несколько команд, чтобы влить эти изменения в вашу основную ветку master:

Разумеется, включать изменения от Алисы в master не глядя — это далеко не лучшая идея: ведь master содержит код, который вы собираетесь поставлять клиентам, а потому наверняка хотите внимательно следить за тем, что в него попадает. Более правильный путь, чем простое включение изменений в master, — это сначала влить их в отдельную ветку и проанализировать перед тем, как cливать в master:

Приведённая команда git diff с синтаксисом трёх точек (в дальнейшем «triple dot» git diff) покажет изменения между вершиной ветки alice/branch и её merge base — общим предком с локальной веткой master, иначе говоря, с точкой расхождения истории коммитов этих веток. В сущности, это будут ровно те изменения, которые Алиса просит нас включить в основную ветку.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния
git diff master. alice/master эквивалентен git diff A B

На первый взгляд, это кажется разумным способом проверки изменений pull request-а. Действительно, на момент написания статьи, именно такой алгоритм сравнения применяется в реализации pull request-ов в большинстве инструментов, предоставляющих хостинг git-репозиториев.

Несмотря на это, есть несколько проблем в использовании «triple dot» git diff для анализа изменений pull request-а. В реальном проекте основная ветка, скорее всего, будет сильно отличаться от любой ветки функциональности (в дальнейшем feature-ветка). Работа над задачами ведётся в отдельных ветках, которые по окончании вливаются в master. Когда master продвигается вперёд, простой git diff от вершины feature-ветки до её merge base уже недостаточен для отображения настоящего различия между этими ветками: он покажет разницу вершины feature-ветки лишь с одним из предыдущих состояний master.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния
Ветка master продвигается за счёт вливания новых изменений. Результат git diff master. alice/master не отражает этих изменений master.

Почему же невозможность увидеть эти изменения в ходе анализа pull request-а является проблемой? Тому есть две причины.

Конфликты слияния (merge conflicts)

С первой проблемой вы наверняка регулярно сталкиваетесь — конфликты слияния. Если в вашей feature-ветке вы измените файл, который в то же время был изменён в master, git diff по-прежнему будет отображать только изменения, сделанные вами в feature-ветке. Однако при попытке выполнить git merge вы столкнётесь с ошибкой: git расставит маркеры конфликтов в файлах вашей рабочей копии, поскольку сливаемые ветки имеют противоречивые изменения, — такие, которые git не в состоянии разрешить даже с помощью продвинутых стратегий слияния.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния
Конфликт слияния

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

Однако конфликты слияния — это меньшая неприятность, с которой вы можете столкнуться при использовании «triple dot» git diff для pull request-ов, по сравнению с другой проблемой: особый тип логического конфликта будет успешно слит, но сможет внести коварную ошибку в кодовую базу.

Логические конфликты, остающиеся незамеченными во время слияния

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

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

Здесь содержится очевидная ошибка: автор забыл включить в расчёт таможенный сбор!

Теперь представьте двух разработчиков, Алису и Боба, каждый из которых заметил эту ошибку и исправил её независимо от другого в своей ветке.

Алиса добавила строку для учёта customsFee перед immigrationFee:

Боб сделал аналогичную правку, однако поместил её после immigrationFee:

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

(Это, разумеется, надуманный пример, однако дублированный код или логика могут вызвать весьма серьёзные проблемы: к примеру, дыру в реализации SSL/TLS в iOS.)

Предположим, что вы сначала слили в master изменения pull request-а Алисы. Вот что показал бы pull request Боба, если бы вы использовали «triple dot» git diff:

Поскольку вы анализируете изменения по сравнению с общим предком, нет никакого предупреждения об угрозе ошибки, которая случится, когда вы нажмёте на кнопку слияния.

На самом же деле, при анализе pull request-а вы хотели бы видеть, как master изменится после слияния изменений из ветки Боба:

Здесь явно обозначена проблема. Рецензент pull request-а, будем надеяться, заметит дублированную строчку и уведомит Боба о том, что код нужно доработать, и тем самым предотвратит попадание серьёзной ошибки в master и, в конечном счёте, в готовый продукт.

Таким образом мы решили реализовать показ изменений в pull request-ах в Bitbucket. При просмотре pull request-а вы видите, как на самом деле будет выглядеть результат слияния (т.е. фактически, результирующий коммит). Чтобы осуществить это, мы производим настоящее слияние веток и показываем разницу между получившимся коммитом и верхушкой целевой ветки pull request-а:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния
git diff C D, где D — это коммит, получившийся в результате слияния, показывает все различия между двумя ветками

Если вам интересно, я разместил одинаковый репозиторий на нескольких хостингах, чтобы вы сами смогли увидеть описанную разницу между алгоритмами сравнения:

Продвижение веток

Во-первых, коммит слияния D на самом деле ещё не существует, а его создание — относительно дорогой процесс. Во-вторых, недостаточно просто создать коммит D и на этом закончить: B и C, родительские коммиты для нашего коммита слияния, могут поменяться в любое время. Мы называем изменение любого из родительских коммитов пересмотром (rescope) pull request-а, поскольку оно, по сути, модифицирует тот набор изменений, который будет применён в результате слияния pull request-а. Если ваш pull request нацелен на нагруженную ветку вроде master, он наверняка пересматривается очень часто.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния
Коммиты слияния создаются каждый раз, когда любая из веток pull request-а изменяется

Фактически, каждый раз когда кто-то коммитит или сливает pull request в master или feature-ветку, Bitbucket должен создать новый коммит слияния, чтобы показать актуальную разницу между ветками в pull request-е.

Обработка конфликтов слияния

Другая проблема при выполнении слияния для отображения разницы меджу ветками в pull request-е заключается в том, что время от времени вам придётся иметь дело с конфликтами слияния. Поскольку git сервер работает в неинтерактивном режиме, разрешать такие конфликты будет некому. Это ещё больше усложняет задачу, но на деле оказывается преимуществом. В Bitbucket мы действительно включаем маркеры конфликтов в коммит слияния D, а затем помечаем их при отображении разницы между ветками, чтобы явно указать вам на то, что pull request содержит конфликты:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния
Зелёные строки добавлены, красные — удалены, а жёлтые означают конфликт

Таким образом, мы не только заранее выявляем, что pull request содержит конфликт, но и позволяем рецензентам обсудить, как именно он должен быть разрешён. Поскольку конфликт всегда затрагивает, как минимум, две стороны, мы считаем, что pull request — это лучшее место для нахождения подходящего способа его разрешения.

Несмотря на дополнительную сложность реализации и ресурсоёмкость используемого подхода, я считаю, что выбранный нами в Bitbucket подход предоставляет наиболее точную и практичную разницу между ветками pull request-а.

Автор оригинальной статьи — Тим Петтерсен, участвовал в разработке JIRA, FishEye/Crucible и Stash. С начала 2013 года он рассказывает о процессах разработки, git, непрерывной интеграции и поставке (continuous integration/deployment) и инструментах Atlassian для разработчиков, особенно о Bitbucket. Тим регулярно публикует заметки об этих и других вещах в Twitter под псевдонимом @kannonboy.

Надеюсь, что эта статья оказалась интересной. Буду рад ответить на вопросы и комментарии.

Источник

Безболезненное разрешение Merge конфликтов в Git

Предлагаю читателям «Хабрахабра» перевод публикации «Painless Merge Conflict Resolution in Git»
из блога blog.wuwon.id.au.

В моей повседневной работе, часто приходится иметь дело со множеством git ветвей (branch). Это могут быть ветви промежуточных релизов, ветви с устаревшим API находящиеся на поддержке для некоторых клиентов, или ветви с экспериментальными свойствами. Лёгкость создания ветвей в модели Git так и соблазняет разработчиков создавать все больше и больше ветвей, и как правило бремя от большого количества ветвей становится очень ощутимым, когда приходится все эти ветви поддерживать и периодически делать слияния (merge) с другими ветвями.

Слияния очень важны для поддержания кода в актуальном состоянии, и как правило ошибка сделанная при слиянии может привести к большей головной боли, нежели ошибка сделанная при простом коммите. К сожалению ошибки слияния далеко не редкость, потому что во-первых слияния имеют несколько родительских ветвей. Даже при анализе истории слияния ветвей, бывает очень трудно понять, какие же изменения были сделаны для разрешения конфликта. Во-вторых, отмена неудачного слияния может превратиться в большую головную боль. В-третьих, большая часть конфликтов слияния происходит при работе с чужим кодом, потому что само понятие ветвей подразумевает множество пользователей, т.е. далеко не всегда слияние производит тот же человек который работал с той или иной веткой. В сухом остатке, сделать ошибку при слиянии очень легко, её трудно исправить и трудно найти. Таким образом время потраченное на изучение и понимание процесса слияния ветвей, окупится с лихвой.

Удивительно, но я обнаружил, что многие доступные инструменты и интерфейсы предназначенные для выполнения слияний, не достаточно хорошо оснащены для эффективного выполнения этого процесса. Часто программист просто надеется что команда git merge сделает за него всю работу. Но когда все-таки происходит конфликт, то обычно стратегия слияния заключается в беглом просмотре кода вокруг строки конфликта, и интуитивном угадывании что именно данный кусок кода предпочтительней другого.

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

Голубые Розы (Roses are Blue)

Давайте предположим что вашей команде поручили писать поэмы в отведённом для этих целей репозитории. (Какой кошмар!) А вам доверили самое главное — делать слияния последних фиксов из ветки master в ветку beta. Итак, вы переключаетесь в ветку beta и выполняете следующую команду:

Ого, это конфликт. Вы решаете просмотреть файл на который ссылается git:

Замечательно! Весь файл, как показывает Listing 1, находится в конфликтном состоянии. Какой же вариант файла является более корректным? Оба варианта выглядят корректно. Верхний вариант написан в хакер-стиле с элементами цветовой кодировки в стиле HTML и с использованием только строчных букв. Нижний вариант выглядит более натурально, с использованием пунктуации и заглавных букв.

Если бы это был ваш проект, вы бы могли просто выбрать один вариант и покончить с этим слиянием. Но проблема в том, что это не ваша поэма, вы никогда не читали эту поэму раньше, не были ответственны за написание или редактирование, и вы отлично понимаете что в случае не верного решения чья-то тяжёлая работа может кануть в небытие. Однако вас всё же назначили ответственным по слиянию этих веток. Что же вам делать?

Назад к Базе (Back to Base)

Хитрость заключается в том, что Listing 1 не даёт вам полную информацию, необходимую для совершения корректного слияния. На самом деле, в процессе слияния участвуют четыре важных части информации (состояния), три из которых просто необходимы для успешного разрешения конфликта. В случае Listing 1, Git предоставил вам только два состояния.

Следующая диаграмма иллюстрирует эти четыре состояния:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Состояния (B) и © относятся к текущим положениям (head) веток master и beta соответственно, эти два состояния как раз таки и отражены в Listing 1. Состояние (D) это результат слияния, то что вы хотите получить/сгенерировать в конечном итоге (в большинстве случаев Git автоматически генерирует состояние (D)). Состояние (А) на самом верху, представляет собой базу (основу) слияния веток master и beta. База слияния (A) это последний общий предок веток master и beta, и пока предположим что это база слияния уникальна. Как мы увидим позже состояние (A) играет ключевую роль в разрешении конфликтов. На диаграмме я также отразил дельты 1 и 2, которые представляют изменения между состояниями (A)-(B), и (A)-© соответственно. Зная состояния (A), (B) и © дельты 1 и 2 могут быть легко получены (вычислены). Обратите внимание, что дельты 1 и 2 могут состоять из более чем одного коммита. Но для наших целей будем считать что все дельты монолитны.

Чтобы понять, как получить состояние (D), вы должны понимать что же операция слияния пытается сделать. Состояние (D) должно представлять собой сочетание изменений, внесённых в ветку master и beta соответственно. Т.е. другими словами сочетание дельт 1 и 2. Идея проста на поверхности и большую часть времени не требует вмешательства со стороны человека, за исключением особых случаев когда дельты затрагивают наслаиваемые (пересекающиеся) части файла. В такой ситуации вам требуется помочь машине сгенерировать результат (D), путём сравнения дельт 1 и 2.

Определение Отличий (Identifying the Differences)

Для того чтобы найти изменения внесённые в каждую ветку, необходимо знать как выглядит база слияния, состояние (A). Самый простой механизм получения информации о базе слияния, это установка опции merge.conflictstyle в значение diff3

Теперь мы видим третий фрагмент посередине, который и является базой слияния или состояние (A). Изменения видны как на ладони: в ветке beta (HEAD) человеческие названия цветов были заменены на HTML коды, а в ветку master добавили капитализацию и пунктуацию. Основываясь на этих знаниях, мы теперь знаем что результат должен включать в себя капитализацию, пунктуацию и HTML коды цветов.

В принципе на этом можно было бы и закончить, потому что результат достигнут. Но есть решение и получше.

Графическое Слияние (GUI Merging)

Хотя и простое текстовое представление конфликта слияния делает свою работу в простых случаях, на практике конфликты могут быть более радикальными и сложными. В таких случаях могут помочь графические инструменты. Мой выбор пал на простой инструмент написанный на Python под названием meld, но может подойти любой другой графический инструмент, способный представить слияние в трёх-колоночном виде.

Для использования графического инструмента (он должен быть установлен), после того как git пожаловался что есть конфликт, введите следующую команду:

Последует вопрос какой программой для слияния вы хотели бы воспользоваться, просто введите meld и нажмите Enter. Вот как окно программы может выглядеть (подразумевается опция merge.conflictstyle не была включена):

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Несмотря на то что информация представлена бок о бок, она не отображает нужные фрагменты которые были в Listing 2. Мы не видим здесь фрагмента базы слияния (состояния (A)), что мы видим это файл roses.txt.LOCAL.2760.txt в левой колонке и файл roses.txt.REMOTE.2760.txt в правой колонке и файл посередине это неудачное слияние. Т.е. по сути нам представили состояния (B), © и несостоявшееся состояние (D), но состояние (A) отсутствует.

Правда отсутствует? Давайте проверим, в старом добром терминале:

Видим интересующий нас файл: roses.txt.BASE.2760.txt. Это и есть файл базы слияния. Теперь нам осталось всего лишь найти изменения внесённые в ветки master и beta, по отношению к базе. Мы можем сделать это двумя отдельными вызовами meld:

(Кто-то может подметить что было бы более разумно, поменять порядок аргументов в первом вызове, для того чтобы файл базы находился в левой колонке в обоих случаях, но именно такой порядок сохраняет подобие трёх-колоночного вида, при котором база остаётся по середине.) Результат выполнения — два окна как показано ниже:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слиянияв каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

При чтении первого окна справа налево и второго окна слева направо, становится ясно как день, какие изменения произошли в каждой ветке. Так как meld любезно подсветил все изменения, теперь практически не возможно пропустить даже мелко заметные правки (Кто-нибудь заметил добавление предлога «of» при просмотре текстового представления разрешения конфликта Listing 1 или даже Listing 2?)

Вооружившись этими знаниями, мы теперь можем вернуться к трёх-колоночному представлению и сделать изменения. Моя стратегия ручного слияния это взять весь текст из ветки с более весомыми изменениями (в данном случае master/REMOTE т.е. beta), и поверх него производить пошаговые правки, т.е. вносить изменения сделанные в другой ветке (master). Вот что получилось:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

А теперь всё вместе (All Together Now)

И добавьте следующее в ваш

Теперь, когда вы в следующий раз будете запускать команду git mergetool для разрешения конфликта, откроются все три окна:

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

Бонус от переводчика

Для тех кто пользуется tmux и n?vim, предлагаю следующий скрипт gitmerge:

Примечание: если вы не используете эту опцию в своем

/.tmux.conf, то вам надо поменять в двух последних строках «$sn:1» на «$sn:0»

Соответственно добавьте следующее в ваш

Воркфлоу разрешения конфликта будет выглядеть так:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Пока игнорируем вопрос (Was the merge successful [y/n]?) и переключаемся в сессию под названием gitmerge (сочетание TMUXPREFIX + s):

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Видим наше трёх-оконное представление на одном экране. Цифрами обозначены сплиты (panes) tmux’a, буквами соответствующие состояния. Делаем правки для разрешения конфликта, т.е. редактируем состояние (D) и сохраняем. После этого возвращаемся обратно в исходную сессию tmux’a и подтверждаем что слияние произошло успешно.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

git rebase master

Лично я предпочитаю и считаю более правильным делать сначала rebase master в ветке beta, и только после этого переключаться в master и делать git merge beta. В принципе воркфлоу не сильно отличается, за исключением трёх-оконного вида.

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Переключаемся в сессию gitmerge

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Обратите внимание, что состояния (B) и © поменялись местами:

в каком случае возникает конфликт слияния. Смотреть фото в каком случае возникает конфликт слияния. Смотреть картинку в каком случае возникает конфликт слияния. Картинка про в каком случае возникает конфликт слияния. Фото в каком случае возникает конфликт слияния

Рекомендую всем поиграться с примером репозитария хотя бы один раз, сделать разрешение конфликта по вышеописанной схеме. Лично я больше не гадаю а что же выбрать «Accept theirs» или «Accept yours».

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *