mergiraf — nástroj orientovaný na AST pre trojstranné zlúčenie v Gite

Bol release projektu mergiraf 0.4, ktorý vyvíja ovládač pre Git s implementovanou možnosťou trojstranného zlúčenia. Mergiraf podporuje riešenie rôznych typov konfliktov pri zlúčení a môže sa použiť pre rôzne programovacie jazyky a formáty súborov. Je možné využiť samostatný príkaz mergiraf na spracovanie konfliktov, ktoré vznikajú pri práci so štandardným Gitom, alebo nahradiť v Gite spracovateľ zlúčení, aby sa rozšírila funkcionalita takých príkazov ako merge, revert, rebase a cherry-pick. Kód je distribuovaný pod licenciou GPLv3. V novej verzii bola pridaná podpora jazykov Python, TOML, Scala a Typescript a bola optimalizovaná výkonnosť.

Nižšie je uvedený podrobný popis problémov, ktoré sa dajú riešiť pomocou mergiraf:

Softvér je jasným príkladom mimoriadne zložitého systému. Zložité systémy majú jednu spoločnú vlastnosť — sú ZLOŽITÉ — a nemôžete očakávať, že požadované zložité správanie vznikne samo od seba, náhodne. Namiesto toho sa tieto systémy vyvíjajú časom, krok za krokom, a každá mutácia je starostlivo testovaná v každom kroku. Na dosiahnutie tohto je potrebná dobre definovaná štruktúra a vhodné nástroje. Evolúcia akéhokoľvek zloženého systému sa dá vizualizovať ako smerovaný strom, kde koreň predstavuje prázdny súbor funkcionalít, a každý uzol — s výnimkou koreňa — je výsledkom aplikácie mutácie na svojho rodiča.

V kontexte produktov sa každý uzol nazýva „verziou“, ktorá predstavuje určitý súbor funkcií a antifunkcií. Akákoľvek zmena tohto súboru sa považuje za mutáciu, ktorá vytvára hranu v našej smerovanej acyklickej grafe. Tieto funkcie sú svojim spôsobom abstraktné; priamo neodrážajú spôsoby, akými fyzické systémy fungujú, ale skôr ukazujú, ako rozumné agenti vnímajú užitočnosť týchto systémov. Na to, aby sme preložili myšlienky do existujúcich reálnych implementácií, je potrebné zasúkať si rukávy a ponoriť sa do *dosť* nízkoúrovňových detailov, v jazyku, ktorým možno vyjadriť a vysvetliť, ako konkrétne všetko funguje. V softvérovej vývoji sú tieto nízkoúrovňové detaily zvyčajne reprezentované zdrojovým kódom.

Aby postupne dostali pôvodný kód do stavu, ktorý vykazuje požadované správanie, a zdokumentovali, ako sa tam dostali, programátori prezentujú svoju prácu v pojmoch snapshotov a changesetov. Snapshot predstavuje konkrétny stav produktu so všetkými nízkoúrovňovými detailmi, zatiaľ čo changeset označuje prechod medzi snapshotami. Zvyčajne sú snapshoty generované z jednotlivých changesetov svojich rodičov, takže tieto snapshoty takmer vždy označujú to, čo robia changesety, ktoré ich vytvorili, preto sa tieto pojmy často používajú zameniteľne.

Niekedy existujú snímky, ktoré vznikli v dôsledku niekoľkých prechodov – zlúčenia commitov. Je s nimi ťažké pracovať, preto sa im zvyčajne vyhýbajú. Moderné systémy správy verzií s otvoreným zdrojovým kódom, ako Git, poskytujú veľmi základné možnosti na riadenie pracovných tokov vývoja. Umožňujú vývojárom organizovať snímky vo forme orientovaných acyklických grafov, anotovať ich komentármi a v prípade potreby meniť ich poradie.

Táto funkcionalita umožňuje vývojárom písať semanticky významný príbeh projektu, čo je rozhodujúce pre ladenie a odpovede na otázky ako „Prečo bola zavedená táto nízkoúrovňová súčasť (napr. premenná)?“, „Koľko percent je môj príspevok približne k tomuto projektu?“, „Kedy a kto bol napadnutý zavedením keyloggeru?“, „Ktorá nízkoúrovňová zmena rozbila túto funkciu (aj keď by nemala, všetko sme skontrolovali!?)"

Systémy sledovania verzií dopĺňajú túto koncepciu vetvy — nízkoúrovňové pojem, ktorý jednoducho znamená nepretržitú časť nízkoúrovňovej histórie projektu, semanticky významnú pre vývojára. Vetvy sa zvyčajne používajú na konkrétnu implementáciu funkcií, niekedy sa vytvára niekoľko vetiev pre rôzne kandidáty na implementáciu tej istej funkcie. Použitím pracovných postupov s vetvením (ktoré sú v skutočnosti mainstream a štandard vývoja, používajú sa všade a vo všetkých oblastiach) môže každý jednotlivý vývojár efektívne spravovať mnoho konfliktujúcich vetiev projektu, z ktorých každá sa líši v stupni pripravenosti alebo kvalite. To umožňuje vývojárom kombinovať výsledky svojich i cudzích prác bez toho, aby museli manuálne prepisovať všetko zakaždým.

Obvykle sa vytvorí hlavná vetva, reprezentujúca "oficiálny" produkt, od ktorej sa odvetvujú bočné vetvy pre každú funkciu, ktoré sa pravidelne (ideálne — po každom commite) synchronizujú s hlavnou vetvou, čo umožňuje vývojárom pracovať s najčerstvejšou verziou produktu a zároveň zavádzanie funkcií, ktoré práve vyvíjajú, detegujúc problémy, vyplývajúce z akcií iných vývojárov, čo najskôr.

Pri pokuse o kombinovanie funkcií rôznych snapshotov (čo je jednoducho nájdenie spoločného predka a aplikovanie changesetov, ktoré ich vytvárajú, postupne na iný, táto operácia sa nazýva rebase, zlučovanie je takmer ako rebase, jednoducho štruktúruje graf commitov inak, čo ich robí nepraktickými na manipuláciu, preto sa zlučovaniu snažia vyhnúť v prospech rebase) vznikajú problémy. Moderné systémy sledovania verzií (VCS) používajú vnútorné algoritmy na zlúčenie zmien, ktoré jednoducho rozbíjajú súbory na jednotlivé riadky, považujú každý riadok za znak a súbory za ich sekvencie, a potom aplikujú algoritmy na ich zlúčenie, ktoré pochádzajú z bioinformatiky.

Žiaľ, takéto riadkové zobrazenie zdrojového kódu nemá nič spoločné s jeho obsahom. Jeho jedinou výhodou je, že je jednoduché a univerzálne. Nesúlad vedie ku konfliktom, čo je stále zdrojom bolesti hlavy pre vývojárov. Riešenie konfliktov si vyžaduje, aby vedúci vývojár dôkladne preskúmal obidve verzie kódu, a to nielen sekcie označené riadkovým algoritmom porovnávania ako „zmenené“ alebo „konfliktné“, ale možno aj celý projekt.

Vývojár musí pochopiť zmeny, ručne napísať zlúčený kód a odstrániť akékoľvek nezrovnalosti. Počet problémov sa výrazne zvyšuje, keď riadkový nástroj nesprávne identifikuje zmeny, čo sa často stáva pri veľkých zmenách, vrátane triviálnych, ako je preformátovanie kódu. Ak sa nasledujúce zmeny nedajú aplikovať na ručne zlúčený kód, situácia sa mení na úplný chaos. Napriek hrozným prípadom v drvivej väčšine prípadov riadkový algoritmus funguje, najmä ak sa vývojári aktívne snažia nevyvolávať mu problémy. Jedným zo spôsobov minimalizácie takýchto problémov je povinné spracovanie zdrojov nástrojmi kanonizácie, ako je black.

Samozrejme, správne riešenie týchto hrozných situácií (a vôbec, nie len pre nich, riadkový algoritmus je heuristika, ktorá môže triviálne viesť k nefunkčnému kódu; napríklad jeden vývojár premenoval premennú, zatiaľ čo druhý napísal časť nového kódu, ktorý túto premennú používa, konflikt zlúčenia/prebázovania tu nebude, ale výsledok sa stane nefunkčným) je použitie správneho interného modelu.

Napriek tomu, že výskum v tejto oblasti prebieha už takmer 30 rokov a vyústil do vytvorenia niekoľkých proprietárnych komerčných produktov, tieto výskumy sa donedávna ešte nepremenili na prakticky použiteľné produkty s otvoreným zdrojovým kódom. Hlavná masa riešení OSS sa začala vyvíjať na začiatku 2010-tych rokov a bola zameraná najmä na jazyk Java.

Najvýznamnejšia voľná implementácia tej doby, GumTree, bola vytvorená výskumníkom s akademickým zázemím, napísaná v Jave, má svoje abstraktné vnútorné zastúpenie, ktoré predchádza treesitteru, má backendy poskytnuté ako založené na treesitter, tak aj založené na iných nástrojoch pre parsovanie zdrojového kódu do abstraktných reprezentácií. Tento systém dokáže iba generovať (vo forme textového logu udalostí, je tu aj API, ktoré sa dá triválne zavolať z akéhokoľvek programovacieho jazyka s viazaním na Javu) a vizualizovať zmeny. Avšak na zlúčenie zmien, ako aj na zobrazenie generovaných diff súborov, je nesprávne použiteľný (prihliadajúc na to, že načítavanie diff súborov môže byť implementované cez API).

Mladšia a praktickejšie aplikovateľná implementácia difftastic je napísaná v Rustu, založená na treesitter, zameraná na generovanie zvýraznených diffov v konzole. Tento systém je tiež orientovaný na vizualizáciu diffov a vo všeobecnosti nemá cieľ zlúčiť zmeny alebo aplikovať záplaty.

Prednedávnom sa objavil a aktívne sa vyvíja projekt mergiraf. Tento nástroj napísaný v Rustu (má 21 MiB!) je tiež založený na treesitter, ktorý sa stal už štandardom pre parséry kontextovo voľných gramatík v nástrojoch vývoja, akým sa stal LLVM pre optimalizáciu nízkoúrovňových reprezentácií inštrukcií. Na rozdiel od konkurentov mergiraf poskytuje funkcie nie na generovanie diffov, ale na automatické riešenie konfliktov zlúčení. Pod kapotou mergiraf využíva na generovanie záplat implementáciu algoritmu použitú v GumTree, a na aplikovanie — implementáciu algoritmu použitú v sporku, prispôsobenú pre štruktúry treesitter.

Serializácia záplat do súborov, ktoré je možné neskôr aplikovať, bohužiaľ, nie je implementovaná (ale je veľmi pravdepodobné, že môže byť implementovaná prostredníctvom parsovania logov udalostí generovaných GumTree). Iným perspektívnym spôsobom, ako aplikovať rozdiely, môže byť aplikácia rozdielov nie prostredníctvom záplat, ale prostredníctvom funkčnosti refaktorovania LSP serverov, čo môže pomôcť pri zistení konfliktov na úrovni celého projektu. Vizualizácia je podporovaná len pre konflikty.

Príklad fungovania: celkový predok „base.py“ (odsadenia pomocou tabulátorov, zbytočný riadok na začiatku) foo = 1 def main(): print(foo + 2 + 3) „a.py“ (odsadenia sú stále pomocou tabulátorov, namiesto jedného sú dve zbytočné prázdne riadky, na odladovanie sa používa knižnica icecream, pridaná trieda „baz“: from icecream import ic foo = 1 def main(): ic(foo + 2 + 3) class baz: def __init__(self): „““baz“““ „b.py“ (premenná „foo“ premenovaná na „bar“, spracované pomocou „black“ po zmenách, nakoniec odsadenia sú medzerami a zbytočné riadky odstránené): bar = 1 def main(): print(bar + 2 + 3) Volanie .\/mergiraf merge .\/base.py .\/a.py .\/b.py -x a.py -y b.py -s base.py -o .\/res.py vedie k nasledujúcemu výsledku from icecream import ic bar = 1 def main(): ic(bar + 2 + 3) class baz: def __init__(self): „““baz“““ (na odladovanie sa používa knižnica „icecream“, premenná „foo“ bola premenovaná na „bar“, spracované pomocou „black“ po zmenách, nakoniec odsadenia sú medzerami a zbytočné riadky odstránené, zmes tabulátorov a medzier pre odsadenie, ale povolený vzhľad).

Tu sa ukazuje nevýhoda nástroja. Štýl dokumentu sa zvyčajne konfiguruje v súboroch „.editorconfig“, a globálne zmeny štýlu, ako je prepínanie tabulátorov na medzery a prijatie štýlu black, ako to bolo vykonané v „b.py“, zvyčajne sprevádzajú zmeny v „.editorconfig“. Preto pre presnejšie použitie podobných zmien by mal mať nástroj koncepciu pre globálny štýl „predvolený“ a vedieť ťahať nastavenia z „.editorconfig“.

Zdroj: opennet.ru

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster