mergiraf — AST-orientált eszköz a Git háromoldalú egyesítéséhez.

Megjelent a mergiraf 0.4 verzió, amely egy Git meghajtó fejlesztését célozza, és lehetővé teszi a háromszoros egyesítést. A mergiraf támogatja a különböző típusú konfliktusok feloldását az egyesítés során, és különböző programozási nyelvekhez és fájlformátumokhoz használható. Lehetőség van a mergiraf különálló meghívására a konfliktusok kezelésére, amelyek a normál Git használata során merülnek fel, valamint a Git egyesítési kezelőjének helyettesítésére, bővítve ezzel olyan parancsok funkcionalitását, mint a merge, revert, rebase és cherry-pick. A kód GPLv3 licenc alatt érhető el. Az új verzióban támogatást kapott a Python, TOML, Scala és Typescript nyelvekhez, valamint teljesítményoptimalizálások is történtek.

Az alábbiakban részletes leírást talál a mergiraf által megoldott problémákról:

A szoftver egy példa a rendkívül összetett rendszerekre. Az összetett rendszereknek van egy közös tulajdonságuk — ÖSSZETETTEK — és nem várhatjuk el, hogy a szükséges összetett viselkedés véletlenszerűen, magától kialakuljon. Ehelyett ezek a rendszerek idővel evolúcióra lépnek, lépésről lépésre, és minden egyes mutációt alaposan ellenőriznek minden egyes lépésnél. Ehhez jól meghatározott struktúrára és megfelelő eszközökre van szükség. Bármely összetett rendszer evolúcióját irányított fának lehet elképzelni, ahol a gyökér egy üres funkciókészletet képvisel, és minden csomópont — a gyökér kivételével — a szülőjére alkalmazott mutáció eredménye.

A termékek kontextusában minden csomópontot "verziónak" nevezünk, amely egy adott funkciókészletet és anti-funkciókat képvisel. Bármilyen változtatás ezen a készleten mutációnak számít, amely egy élt formál a mi irányított aciklikus grafikonunkban. Ezek a funkciók lényegileg absztraktak; nem tükrözik közvetlenül a fizikai rendszerek működési módját, hanem inkább azt mutatják be, hogy a racionális ügynökök hogyan érzékelik ezeknek a rendszereknek a hasznosságát. Az ötletek valóságos megvalósításokká történő átviteléhez meg kell hígítani a munkát, és alá kell merülni a *megfelelően* alacsony szintű részletekbe, azon a nyelven, amelyen kifejezhetjük és elmagyarázhatjuk, hogy pontosan hogyan működik minden. A szoftverfejlesztésben ezek az alacsony szintű bemutatások általában a forráskódot jelentik.

Ahhoz, hogy fokozatosan a kívánt viselkedést megvalósítsák a forráskódban, és dokumentálják, hogyan jutottak el oda, a programozók a munkájukat snapshotok és changesetek formájában mutatják be. A snapshot a termék egy adott állapota, minden alacsony szintű részlettel, míg a changeset az átmenetet jelöli a snapshotok között. A snapshotokat általában az egyes changesetek generálják a szülőikhez képest, ezért ezek a snapshotok szinte mindig azokat a changeseteket jelölik, amelyek létrehozták őket, így ezeket a kifejezéseket gyakran felcserélhetően használják.

Néha léteznek snapshots, amelyek több átmenetből származnak – commitok összevonásából. Ezekkel nehéz dolgozni, ezért általában kerülik őket. A modern, nyílt forráskódú verziókezelő rendszerek, mint például a Git, meglehetősen alapvető lehetőségeket kínálnak a fejlesztési folyamatok kezelésére. Lehetővé teszik a fejlesztők számára, hogy a snapshotokat irányított, ciklus nélküli gráfok formájában szervezzék meg, kommentárokkal annotálják őket, és szükség esetén megváltoztassák a sorrendjüket.

Ez a funkcionalitás lehetővé teszi a fejlesztők számára, hogy szemantikailag jelentős történetet írjanak a projektről, ami kulcsfontosságú a hibakereséshez és a kérdések megválaszolásához, mint például: „Miért vezettük be ezt az alacsony szintű részletet (pl. változó)?”, „Milyen arányban van körülbelül az én hozzájárulásom ehhez a projekthez?”, „Kit történt a backdoor beékelés és mikor?”, „Melyik alacsony szintű módosítás zúzta el ezt a funkciót (bár nem kellett volna, mindent ellenőriztünk!?)”

A verziókezelő rendszerek kiegészítik ezt a fogalmat a branch-nal — egy alacsony szintű fogalom, amely egyszerűen egy folyamatos alacsony szintű történelemdarabot jelent, amely szemantikailag fontos a fejlesztő számára. A branch-eket általában egy adott funkció megvalósítására használják, néha több branch jön létre ugyanazon funkció különböző megvalósítási jelöltjeihez. Az elágazási munkafolyamatok használatával (amelyek valójában mainstream és a fejlesztés szabványa, mindenhol és általánosan alkalmazzák) minden egyes fejlesztő hatékonyan kezelheti a projekt sok konfliktusban lévő branch-ét, mindegyik más készültségi fokkal vagy minőséggel rendelkezik. Ez lehetővé teszi a fejlesztők számára, hogy kombinálják saját és mások munkájának eredményeit anélkül, hogy minden alkalommal manuálisan újra kellene írniuk mindent.

Általában van egy fő branch, amely az «hivatalos» terméket képviseli, és ahonnan oldalsó branch-ek ágaznak el minden funkcióhoz, amelyeket rendszeresen (ideális esetben — minden egyes commit után) szinkronizálnak a fő branch-csel, lehetővé téve a fejlesztők számára, hogy a legfrissebb termékverzióval dolgozzanak, miközben egyidejűleg bevezetik az éppen fejlesztett funkciókat, és a lehető legkorábban érzékeljék a más fejlesztők tevékenységeiből származó problémákat.

Amikor megpróbálják kombinálni különböző snapshot-ok funkcióit (ami egyszerűen a közös ős megtalálását jelenti, és a generáló changesetek fokozatos alkalmazását egy másikon, ezt a műveletet rebase-nek hívják, míg a merge szinte olyan, mint a rebase, csak másképp strukturálja a commit-ok grafját, így nehezen manipulálhatóvá válik, ezért a merge-ket igyekeznek elkerülni a rebase-ek javára) problémák merülnek fel. A modern verziókezelő rendszerek (VCS) belső változásrekonstrukciós algoritmusokat használnak, amelyek egyszerűen fájlokat bontanak le különböző sorokra, minden egyes sort karakterként kezelnek, míg a fájlokat – azok sorozataként, majd alkalmazzák azokat az algoritmusokat, amelyek eredetileg bioinformatikából származnak.

Sajnálatos módon, az ilyen soronkénti forráskód-prezentáció nem áll összefüggésben a tartalmával. Egyetlen előnye, hogy egyszerű és univerzális. Az eltérések konfliktusokhoz vezetnek, állandó fejfájást okozva a fejlesztőknek. A konfliktusok feloldása megköveteli a fejlesztőtől, hogy alaposan tanulmányozza a kód mindkét verzióját, nemcsak a soronkénti összehasonlító algoritmus által "módosított" vagy "konfliktusos" címkével ellátott részeket, hanem lehet, hogy az egész projektet is.

A fejlesztőnek meg kell értenie a változásokat, manuálisan kell megírnia az összevont kódot, és ki kell küszöbölnie minden eltérést. Sokkal több probléma merül fel, amikor a soronkénti eszköz hibásan azonosítja a változásokat, ami gyakran előfordul nagyobb módosítások során, beleértve a triviálisakat is, mint a kód újraformázása. Ha a későbbi változtatásokat nem lehet alkalmazni a manuálisan összevont kódra, a helyzet teljes rémálommá válik. A borzalmas esetek ellenére a legtöbb esetben a soronkénti algoritmus működik, különösen, ha a fejlesztők aktívan próbálnak nem problémákat okozni neki. Az egyik módja az ilyen problémák minimalizálásának a forráskódok kanonizálásával való kötelező feldolgozásának követelménye, mint például a black.

Természetesen a borzalmas esetek (és egyáltalán, nemcsak ezekre) helyes megoldása a megfelelő belső modell használata. A soronkénti algoritmus heurisztika, amely egyszerűen működésképtelen kódhoz vezethet, például ha az egyik fejlesztő átnevezi a változót, míg a másik éppen egy új kódot ír, amely azt a változót használja, itt nem lesz összevonási/újraszervezési konfliktus, de az eredmény működésképtelenné válik.

Bár a kutatások ezen a területen már körülbelül 30 éve zajlanak, és több szabadalmaztatott kereskedelmi termék létrehozásához vezettek, ezek a kutatások a közelmúltig nem alakultak át gyakorlati alkalmazásra alkalmas nyílt forráskódú termékekké. A többség nyílt forráskódú megoldása az 2010-es évek elején kezdett el fejlődni, és főként a Java nyelvre összpontosított.

A legkiemelkedőbb szabad implementáció a GumTree, amelyet egy akadémiai háttérrel rendelkező kutató készített, Java nyelven íródott, saját absztrakt belső reprezentációval rendelkezik, amely megelőzte a treesitter-t, backendjei pedig mind a treesitter alapú, mind más eszközöket használnak a forráskód absztrakt reprezentációba való parse-olására. Ez a rendszer csak eseménynapló formájában képes generálni (továbbá van egy API, amelyet bármely Java-hoz kötött nyelvből könnyedén hívhatunk) és vizualizálni a változásokat. Azonban a változások összevonására, illetve az általa generált diff fájlok megtekintésére nem alkalmazható alapértelmezetten (bár valószínű, hogy a diff-ok betöltését az API-n keresztül meg lehet valósítani).

Egy fiatalabb és a gyakorlatban jobban alkalmazható difftastic megvalósítás Rust nyelven készült, treesitter alapú, és a kiemelt diff-ok generálására összpontosít a konzolon. Ez a rendszer is a diff-ok vizualizálására irányul, és nem tűzi ki célul a változások összevonását vagy a patch-ek alkalmazását.

Nemrégiben megjelent és aktívan fejlődő projekt a mergiraf. Ez a Rust nyelven készült eszköz (21 MiB-ot foglal el!) szintén treesitter alapú, amely már a kontextus-mentes grammatikák parse-olóinak szabványává vált, akárcsak az LLVM az alacsony szintű utasítások optimalizálásában. A konkurenciától eltérően a mergiraf nem a diff-ok generálására, hanem az összevonási konfliktusok automatikus feloldására kínál funkciókat. A mergiraf a patch-ek generálásához a GumTree-ben használt algoritmus megvalósítását használja, az alkalmazáshoz pedig a spork algoritmus megvalósítását, amelyet a treesitter struktúrákhoz igazítottak.

A patch-ek fájlokba való sorozatba állítása, amelyeket később lehetne alkalmazni, sajnos nincsen megvalósítva (de valószínű, hogy a GumTree által generált eseménynaplók parse-olásával meg lehet valósítani). Egy másik ígéretes módszer a különbségek alkalmazására nem patch-ek révén történhet, hanem az LSP szerverek refaktoráló képességén keresztül, ami segíthet a konfliktusok észlelésében az egész projekten szinten. A vizualizáció csak a konfliktusok számára támogatott.

Működési példa: az általános ős «base.py» (tabulátorral való behúzás, felesleges sor a kezdetén) foo = 1 def main(): print(foo + 2 + 3) «a.py» (a behúzások továbbra is tabulatorokkal, 2 felesleges sor a kezdetén egy helyett, a hibakereséshez használt könyvtár az icecream, hozzáadva a «baz» osztály: from icecream import ic foo = 1 def main(): ic(foo + 2 + 3) class baz: def __init__(self): «»»baz»»» «b.py» (a «foo» változó átnevezése «bar»-ra, a változtatások után a «black» segítségével feldolgozva, az eredmény a behúzások – szóközökkel és a felesleges sorok eltávolítása): bar = 1 def main(): print(bar + 2 + 3) A hívás .\/mergiraf merge .\/base.py .\/a.py .\/b.py -x a.py -y b.py -s base.py -o .\/res.py a következő eredményt adja from icecream import ic bar = 1 def main(): ic(bar + 2 + 3) class baz: def __init__(self): «»»baz»»» (a hibakereséshez használt könyvtár az «icecream», a «foo» változó átnevezve a «bar»-ra, a felesleges sorok eltávolítása és a behúzások – szóközökkel történtek a «black» segítségével, ami megengedett elegyítése tabulátorok és szóközök a behúzásoknál, de engedélyezett formátumban).

Itt látható az eszköz hiányossága. A dokumentum stílusát általában az «editorconfig» fájlokban konfigurálják, és a globális stílusváltozások, mint például a tabulátorok szóközökre való cserélése és a black stílusának alkalmazása, mint ami a «b.py»-ben történt, általában a «.editorconfig» fájlokban módosításokat vonnak maguk után. Ezért a hasonló változások pontosabb alkalmazása érdekében az eszköznek rendelkeznie kell egy alapértelmezett globális stílus koncepcióval, és tudnia kell húzni a beállításokat a «.editorconfig» fájlból.

Forrás: opennet.ru

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster