The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

mergiraf - AST-ориентированный инструмент для трёхстороннего слияния в Git

14.12.2024 09:34

Опубликован релиз проекта mergiraf 0.4, развивающего драйвер для Git с реализацией возможности трёхстороннего слияния. Mergiraf поддерживает разрешение различных видов конфликтов при слиянии и может использоваться для различных языков программирования и форматов файлов. Возможен как отдельный вызов mergiraf для обработки конфликтов, возникающих при работе со штатным Git, так и замена в Git обработчика слияний для расширения возможностей таких команд, как merge, revert, rebase и cherry-pick. Код распространяется под лицензией GPLv3. В новой версии добавлена поддержка языков Python, TOML, Scala и Typescript, а также проведена оптимизация производительности.

Ниже представлено подробное описание проблем, решаемых при помощи mergiraf:

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

В контексте продуктов каждый узел называется версией, представляющей собой определённый набор функций и антифункций. Любое изменение этого набора считается мутацией, формирующей ребро в нашем ориентированном ациклическом графе. Эти функции по своей сути абстрактны; они не отражают напрямую способы функционирования физических систем, а скорее демонстрируют, как разумные агенты воспринимают полезность этих систем. Чтобы перевести идеи в существующие в реальном мире реализации, необходимо засучить рукава и нырнуть в достаточно низкоуровневые детали, на языке которых можно выразить и объяснить, как конкретно всё работает. В разработке ПО эти низкоуровневые обычно представлены исходным кодом.

Чтобы постепенно привести исходный код в состояние, проявляющее требуемое поведение, и задокументировать, как они его туда привели, программисты представляют свою работу в терминах snapshot-ов и changeset-ов. Snapshot представляет собой определённое состояние продукта со всеми низкоуровневыми деталями, в то время как changeset обозначает переход между snapshot-ами. Обычно snapshot-ы порождаются одиночными changeset-ов к их родителям, поэтому эти snapshot-ы почти всегда маркируют тем, что делают changeset-ы, которые их создали, поэтому эти термины часто используются взаимозаменяемо.

Иногда существуют snapshot-ы, полученные в результате нескольких переходов — слияния коммитов. С ними сложно работать, поэтому их обычно избегают. Современные системы контроля версий с открытым исходным кодом, такие как Git, предоставляют весьма базовые возможности для управления рабочими процессами разработки. Они позволяют разработчикам организовывать snapshot-ы в виде ориентированных ациклических графов, аннотировать их комментариями и при необходимости менять их порядок.

Эта функциональность позволяет разработчикам писать семантически значимую историю проекта, что имеет решающее значение для отладки и ответов на вопросы вроде "Зачем была введена эта низкоуровневая деталь (например, переменная)?", "Сколько процентов приблизительно составляет мой вклад в этот проект?", "Кого взломали внедрения закладки и когда?", "Какое низкоуровневое изменение сломало эту функцию (хотя вроде бы не должно было, мы всё проверили!?)"

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

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

При попытке комбинирования функций различных snapshot-ов (что есть просто нахождения общего предка, и применения changeset-ов, их порождающих, последовательно поверх другого; эта операция называется rebase, слияние же - это почти как rebase, просто структурирует граф коммитов по-другому, в результате чего им становится неудобно манипулировать, поэтому от слияний стараются отказаться в пользу rebase-ов) возникают проблемы. Современные системы контроля версий (VCS) используют внутренние алгоритмы объединения изменений, которые просто разбивают файлы на отдельные строки, рассматривают каждую строку как символ, а файлы - как их последовательности, и затем применяют алгоритмы для их объединения, которые родом из биоинформатики.

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

Разработчик должен понять изменения, вручную написать объединённый код и устранить любые несоответствия. Проблем намного прибавляется, когда построчный инструмент неправильно идентифицирует изменения, что часто случается при крупных изменениях, включая тривиальные, такие как пеpефоpматирование кода. Если последующие изменения не удаётся применить к вручную объединённому коду, ситуация превращается в полный кошмар. Не смотря на ужасающие случаи, в большинстве случаев построчный алгоритм работает, особенно если разработчики активно стараются не создавать ему проблемы. Один из способов минимизаций подобных проблем - это обязательное требование обработки исходников инструментами каноникализации, такими как black.

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

Не смотря на то, что исследования в этой области ведутся вот уже около 30 лет, и вылились в создание нескольких проприетарных коммерческих продуктов, эти исследования до недавнего времени так и не были превращены в практически применимые продукты с открытым исходным кодом. Основная масса СПО-решений начала развиваться в начале 2010-х, и была сфокусирована в основном на языке Java.

Наиболее выдающаяся свободная реализация того периода, GumTree, создана исследователем с академическим бэкграундом, написана на Java, использует своё абстрактное внутреннее представление, имеет бэкэнды, как основанные на генераторе парсеров treesitter, так и базирующиеся на других инструментах для преобразования исходного кода в абстрактные представления. Данная система умеет только генерировать (в виде текстового лога событий, также имеется API, которое можно тривиально вызвать из любого ЯП, имеющего биндинги к Java) и визуализировать изменения. Однако для слияния изменений, а равно для просмотра сгенерированных ею diff-файлов, она из коробки неприменима (впрочем, вероятно, что загрузку diff-ов можно реализовать через API).

Более молодая и более применимая на практике реализация difftastic написана на Rust, основана на treesitter, сфокусирована на генерации подсвеченных diff-ов в консоли. Данная система тоже направлена на визуализацию diff-ов и вообще не ставит своей целью слияние изменений или применение патчей.

Совсем недавно появился и активно развивается проект mergiraf. Этот написанный на Rust инструмент (занимает 21 MiB!) также основан на treesitter, который уже стал таким же стандартом для парсеров контекстно-свободных грамматик в инструментах разработки, каким стал LLVM для оптимизации низкоуровневых представлений инструкций. В отличии от конкурентов mergiraf предоставляет функции не для генерации diff-ов, а для автоматического разрешения конфликтов слияний. Под капотом mergiraf использует для генерации патчей реализацию алгоритма, используемого в GumTree, а для применения - реализацию алгоритма, используемого в spork, адаптированные под структуры treesitter.

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

Пример работы:


общий предок "base.py" (отступы табами, лишняя строка в начале)


   foo = 1

   def main():
        print(foo + 2 + 3)

"a.py" (отступы по-прежнему табами, 2 лишних строки в начале вместо одной, для отладочной печати задействована библиотека icecream, добавлен класс "baz":

   from icecream import ic

   foo = 1

   def main():
        ic(foo + 2 + 3)

        class baz:
                def __init__(self):
                        """baz"""


"b.py" (переменная "foo" переименована в "bar", обработано с помощью "black" после изменений, в результате отступы - пробелами и лишние строки вырезаны):

   bar = 1


   def main():
       print(bar + 2 + 3)


Вызов

   ./mergiraf merge ./base.py ./a.py ./b.py -x a.py -y b.py -s base.py -o ./res.py

даёт следующий результат

   from icecream import ic

   bar = 1


   def main():
       ic(bar + 2 + 3)

       class baz:
           def __init__(self):
                """baz"""

(для отладочной печати задействована библиотека "icecream", переменная "foo" переименована в "bar", обработано с помощью "black" после изменений, в результате отступы - пробелами и лишние строки вырезаны, смесь табов и пробелов для отступа, но разрешённый вид).

Тут же виден недостаток инструмента. Стиль документа обычно конфигурируется в файлах ".editorconfig", и глобальные изменения стиля, вроде смены табов на пробелы и принятия стиля black-а, как было сделано в "b.py", обычно сопровождаются изменениями в ".editorconfig". Поэтому для более корректного применения подобных изменений инструмент должен иметь концепцию для глобального стиля "по умолчанию", и уметь подтягивать настройки из ".editorconfig".

  1. Главная ссылка к новости (https://codeberg.org/mergiraf/...)
  2. Pointers on abstract syntax tree differencing algorithms and tools
  3. Structural Diffs
  4. Список литературы
  5. Похожие проекты
  6. Другие merge-драйверы для Git
Автор новости: Аноним
Лицензия: CC BY 3.0
Короткая ссылка: https://opennet.ru/62402-mergiraf
Ключевые слова: mergiraf, git, diff, merge
При перепечатке указание ссылки на opennet.ru обязательно


Обсуждение (16) Ajax | 1 уровень | Линейный | +/- | Раскрыть всё | RSS
  • 1.2, pyphon (?), 10:05, 14/12/2024 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Как я увидел функционал:
    1. Обрабатываем код black'ом.
    2. Прогоняем линтеры.
    3. Задаём правила форматирования.
    4. Прогоняем код black'ом.
    5. Чтоб не жать одни и теже кнопки используем pre-commit.
    6. ...
    7. Profit.

    Никто в своём уме не будет тратить несколько дней на сведения одновременно нескольких веток с почти отрицательным результатом.

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

     
     
  • 2.5, Аноним (5), 10:25, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +1 +/
    Мне понравилась вся суть продукта в одной картинке. https://mergiraf.org/img/scene_3.png из двух разработчиков эта штука сделала сиамских близнецов.
     
  • 2.11, Аноним (11), 11:40, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +/
    Каноникализаторы и editorconfig - не панацея. Он помогает уменьшить число конфликтов от того, что один разраб предпочитает одно, другой - другое, и у каждого редактор настроен по-своему. Но конфликты слияния проистекают не только из этого. Это ОГРОМНАЯ ГОЛОВНАЯ БОЛЬ, когда ты не можешь заапстримить свои патчи, потому что апстрим - чудак. Самый ужас начинается, когда апстрим рефакторит структуру проекта одновременно с рефакторингом кода. В результате все файлы с твоими изменениями превращаются в почти сплошной конфликт слияния.


    ЗАПОМНИТЕ, ДЕТИ, ПЕРЕМЕЩЕНИЯ ФАЙЛОВ - СТРОГО В ОДНОМ КОММИТЕ, ЧИСТО ПОД ПЕРЕМЕЩЕНИЯ, А ИЗМЕНЕНИЯ ХОТЬ ОДНОГО СИМВОЛА КОДА - В ДРУГОМ.

     
     
  • 3.16, Аноним (5), 12:17, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +/
    Если проект нормально административно устроен все перемещения делает один человек он же по совместительству самый главный человек. Все остальное делают остальные люди и слушают что говорит главный человек. Все остальные перемещатели в очереди на прием к главному.
     

  • 1.3, Аноним (3), 10:20, 14/12/2024 [ответить] [﹢﹢﹢] [ · · · ]  
  • –1 +/
    Дочитал новость до "развивается проект mergiraf. Этот написанный на Rust инструмент (занимает 21 MiB!)" и прекратил чтение.
     
     
  • 2.6, Аноним (6), 10:27, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +3 +/
    Да, на дискету не поместится. Плак плак.
     
  • 2.12, Аноним (11), 11:48, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • –1 +/
    Вы уловили мой сарказм. В оригинале там было '(занимает целых 21 MiB!)'. На самом деле проект по исходникам всего-ничего занимает. С помощью нейросети вы сможете с весьма небольшими затратами переписать его на C++, на Си, на питон, на любой язык, какой вам нравится. Но зачем? Проект активно развивается.

    У меня план получше. Вместо переписывания проекта на Rust мы заменим Cargo на CMake. Если выкинуть всё дерево статически-линкуемых зависимостей, заменив его на сишные динамически-линкуемые библиотеки, сильно должен похудеть. И компилятор Rust можно вызывать из командной строки. Cargo - не особо нужен. Можно с небольшими затратами запилить полную поддержку Rust для CMake. Я имею в виду без участия Cargo вообще в каком-либо виде. И программить как на сишке. Программы должны от этого сильно похудеть.

     
     
  • 3.13, Аноним (11), 11:53, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +/
    >Проект активно развивается.

    Я имею в виду, что ну вот вы перепишете проект на си. А дальше что? Разрабы запилят ещё десяток функций и перелопатят архитектуру. И вам придётся всё это портировать. Причём уже не нейросетью, а почти вручную, чтобы не превратить историю коммитов уже ВАШЕГО проекта в говно.

     

  • 1.7, Аноним (7), 10:47, 14/12/2024 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Сильно не хватает такого инструмента. Постоянно конфликты в тех же resx файлах. Хотя там простейший xml.
     
  • 1.8, nilsys (?), 11:20, 14/12/2024 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    > ... примером чрезвычайно сложной системы. Сложные системы имеют одно общее свойство - они сложны - и вы не можете ожидать, что нужное сложное поведение возникнет само собой, случайно

    чего?

     
     
  • 2.10, Аноним (10), 11:33, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +1 +/
    Сайт OpenNET - не сайт сугубо для программистов, а об СПО в общем, для дилетанто... большой текст свёрнут, показать
     
     
  • 3.22, НейроАноним (?), 12:36, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +/
    Да вы что! У вас в голове полный бардак! Сайт OpenNET – это не просто сайт для каких-то бездельников или дилетантов! Он создан для людей, которые хотят знать больше, хотят развиваться, а не для тех, кто сидит с пивом на диване и не понимает, что происходит в мире технологий.

    Системы контроля версий - это не игрушки для программистов, это инструменты для всех, кто хочет организовывать свою работу! Вы думаете, что только программистам нужны такие решения? Да нет, любой нормальный человек, который хоть раз создавал документы, поймет, как это полезно! Забудьте о стихийном беспорядке на вашем компьютере, а используйте нормальные системы, которые упрощают жизнь!

    Ваши попытки сделать это "доступным" и "простым" - это просто отмазка! Настоящий специалист должен уметь разбираться в терминах и алгоритмах, а не прятаться за множеством неуместных шуток и сарказма. Если вы хотите, чтобы вас серьезно воспринимали, ведите себя соответственно!

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

    Так что, хватит уже оправдываться! Беритесь за ум и начинайте писать так, чтобы каждому было понятно – и программисту, и простому пользователю! Никаких отговорок!

     

  • 1.14, Аноним (11), 12:05, 14/12/2024 [ответить] [﹢﹢﹢] [ · · · ]  
  • +1 +/
    Ещё забыл написать в статью парочку своих мыслей (тоже уже написанных, но потерянных при краше).

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

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

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

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

    Как компромисс можно обрезаемые части векторизовать с помощью нейросети, и хранить именно векторы.

     
  • 1.18, Анониссимус (?), 12:27, 14/12/2024 [ответить] [﹢﹢﹢] [ · · · ]  
  • +1 +/
    Звучит интересно! Использовать это я, конечно, не буду...
     
     
  • 2.19, eugener (ok), 12:29, 14/12/2024 [^] [^^] [^^^] [ответить]  
  • +/
    вот-вот, я тоже с интересом прочитал и то же самое подумал.)
     

  • 1.21, НейроАноним (?), 12:32, 14/12/2024 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    - Опубликован релиз проекта Mergiraf 0.4 с поддержкой трёхстороннего слияния и разрешения конфликтов в Git.

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

    - В новой версии добавлена поддержка Python, TOML, Scala и TypeScript, а также оптимизация производительности.

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

    - Snapshot-ы, полученные в результате слияния коммитов, сложно обрабатывать, что приводит к необходимости избегать их.

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

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

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

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

     

     Добавить комментарий
    Имя:
    E-Mail:
    Текст:



    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру