mergiraf — een AST-georiënteerde tool voor driedimensionale samenvoeging in Git.

De release van het mergiraf-project 0.4 is uitgebracht, dat een stuurprogramma voor Git ontwikkelt met de mogelijkheid voor driedimensionale samenvoeging. Mergiraf ondersteunt het oplossen van verschillende soorten conflicten bij samenvoegingen en kan worden gebruikt voor verschillende programmeertalen en bestandsformaten. Het is mogelijk om mergiraf afzonderlijk aan te roepen voor het verwerken van conflicten die ontstaan bij het gebruik van standaard Git, evenals om de merge-handler in Git te vervangen om de mogelijkheden van commando's zoals merge, revert, rebase en cherry-pick uit te breiden. De code wordt verspreid onder de GPLv3-licentie. In de nieuwe versie is ondersteuning toegevoegd voor de talen Python, TOML, Scala en Typescript, en is de prestatie geoptimaliseerd.

Hieronder volgt een gedetailleerde beschrijving van de problemen die met mergiraf worden opgelost:

Software is een duidelijk voorbeeld van een extreem complex systeem. Complexe systemen hebben één algemeen kenmerk: ze zijn COMPLEX — en je kunt niet verwachten dat het benodigde complexe gedrag vanzelf, toevallig, ontstaat. In plaats daarvan evolueren deze systemen in de loop van de tijd, stap voor stap, en elke mutatie wordt zorgvuldig getest op elke fase. Hiervoor zijn goed gedefinieerde structuren en de juiste tools nodig. De evolutie van elk complex systeem kan worden gevisualiseerd als een gerichte boom, waarbij de wortel een lege set functies vertegenwoordigt, en elke knoop — met uitzondering van de wortel — het resultaat is van het toepassen van een mutatie op zijn ouder.

In de context van producten wordt elke knoop een 'versie' genoemd, die een bepaalde set functies en anti-functies vertegenwoordigt. Elke wijziging in deze set wordt beschouwd als een mutatie die een rand in onze gerichte acyclische grafiek vormt. Deze functies zijn van abstracte aard; ze weerspiegelen niet direct hoe fysieke systemen functioneren, maar tonen eerder aan hoe redelijke agenten de bruikbaarheid van deze systemen waarnemen. Om ideeën om te zetten in bestaande, in de echte wereld, implementaties, moet men de handen uit de mouwen steken en zich onderdompelen in *voldoende* laag-niveau details, in de taal waarin kan worden uitgedrukt en uitgelegd hoe alles precies werkt. In softwareontwikkeling worden deze laag-niveau details meestal weergegeven door de broncode.

Om de broncode geleidelijk in de toestand te brengen die het vereiste gedrag vertoont en te documenteren hoe ze dat hebben bereikt, presenteren programmeurs hun werk in termen van snapshots en changesets. Een snapshot vertegenwoordigt een bepaalde staat van het product met alle gedetailleerde laag-niveau informatie, terwijl changeset de overgang tussen snapshots aanduidt. Gewoonlijk worden snapshots voortgebracht door enkele changesets naar hun ouders, dus deze snapshots worden bijna altijd gemarkeerd door wat changesets hebben gedaan die ze hebben gecreëerd; daarom worden deze termen vaak door elkaar gebruikt.

Soms zijn er snapshots die zijn verkregen door meerdere overgangen - samenvoegingen van commits. Deze zijn moeilijk te beheren, dus ze worden meestal vermeden. Moderne open-source versiesystemen, zoals Git, bieden vrij basale mogelijkheden voor het beheer van ontwikkelingsworkflows. Ze stellen ontwikkelaars in staat om snapshots te organiseren in de vorm van gerichte acyclische grafen, deze van opmerkingen te voorzien en zo nodig hun volgorde te wijzigen.

Deze functionaliteit stelt ontwikkelaars in staat om een semantisch betekenisvolle geschiedenis van het project te schrijven, wat cruciaal is voor het debuggen en het beantwoorden van vragen zoals "Waarom werd dit laag-niveau detail (bijv. een variabele) geïntroduceerd?", "Hoeveel procent is ongeveer mijn bijdrage aan dit project?", "Wie werden gebroken door de introductie van de backdoor en wanneer?", "Welke laag-niveau wijziging heeft deze functie kapotgemaakt (hoewel het volgens ons niet had moeten, we hebben alles gecontroleerd!?)"

Versiebeheersystemen vullen dit concept aan met takken - een laagdrempelig begrip dat simpelweg een doorlopende fragment van de laagdrempelige geschiedenis van een project betekent, semantisch belangrijk voor de ontwikkelaar. Takken worden meestal gebruikt voor specifieke implementaties van functies, en soms worden er meerdere takken gemaakt voor verschillende kandidaten voor de implementatie van dezelfde functie. Door werkprocessen met takken (die feitelijk mainstream en standaard zijn in de ontwikkeling, overal en te allen tijde worden gebruikt) kan elke afzonderlijke ontwikkelaar efficiënt omgaan met vele conflicterende takken van het project, die elk verschillen in gereedheid of kwaliteit. Dit stelt ontwikkelaars in staat om resultaten van hun en andermans werk te combineren zonder steeds alles handmatig opnieuw te typen.

Gewoonlijk wordt er een hoofdtak aangemaakt die het 'officiële' product vertegenwoordigt, waaruit neven-takken voor elke functie worden afgeleid, die regelmatig (bij voorkeur na elke commit) worden gesynchroniseerd met de hoofdtak, wat ontwikkelaars in staat stelt om met de meest actuele versie van het product te werken en tegelijkertijd functies te implementeren die ze op dat moment aan het ontwikkelen zijn, waardoor problemen die voortvloeien uit de acties van andere ontwikkelaars zo vroeg mogelijk worden gedetecteerd.

Bij het proberen te combineren van functies van verschillende snapshots (wat simpelweg het vinden van een gemeenschappelijke voorouder is en het toepasselijk sequentially de changesets die deze voortbrengen op de andere, deze bewerking wordt rebase genoemd; samenvoegen is bijna als rebase, maar het structureert de commit-grafiek op een andere manier, waardoor deze onhandig wordt om mee te werken; daarom proberen mensen samenvoegingen te vermijden ten gunste van rebases) ontstaan er problemen. Moderne versiebeheersystemen (VCS) gebruiken interne algoritmes voor het samenvoegen van wijzigingen, die eenvoudig bestanden opdelen in afzonderlijke regels, elke regel beschouwen als een symbool, en bestanden als hun reeksen, en vervolgens algoritmes toepassen voor hun samenvoegen, die afkomstig zijn uit de bio-informatica.

Helaas heeft deze regel-voor-regel weergave van de broncode niets te maken met de inhoud ervan. Het enige voordeel is dat het eenvoudig en universeel is. Deze inconsistente presentatie leidt tot conflicten, wat een constante bron van frustratie is voor ontwikkelaars. Het oplossen van conflicten vereist dat de ontwikkelaar beide versies van de code zorgvuldig bestudeert, niet alleen de secties die door het regel-voor-regel vergelijkingsalgoritme als 'gewijzigd' of 'conflictueus' zijn gemarkeerd, maar mogelijk ook het hele project.

De ontwikkelaar moet de wijzigingen begrijpen, handmatig de samengevoegde code schrijven en eventuele inconsistenties oplossen. Het aantal problemen neemt toe wanneer het regel-voor-regel hulpmiddel wijzigingen incorrect identificeert, wat vaak gebeurt bij grote aanpassingen, inclusief triviale, zoals herformattering van code. Als latere wijzigingen niet op de handmatig samengevoegde code kunnen worden toegepast, verandert de situatie in een complete nachtmerrie. Ondanks deze angstige gevallen werkt het regel-voor-regel algoritme in de meeste gevallen, vooral als ontwikkelaars actief proberen geen problemen voor het algoritme te creëren. Een manier om dergelijke problemen te minimaliseren is de vereiste verwerking van de bronbestanden met canonisatie-instrumenten zoals black.

Natuurlijk is de juiste oplossing voor deze angstige gevallen (en eigenlijk niet alleen voor hen, het regel-voor-regel algoritme is een heuristiek die triviaal kan leiden tot niet-werkende code, bijvoorbeeld als een ontwikkelaar een variabele heeft hernoemd terwijl een andere tegelijkertijd een stuk nieuwe code schreef dat die variabele gebruikt, er zal geen samenvoeg- of rebaseconflict zijn, maar het resultaat zal niet-werkend zijn) het gebruik van een correct intern model.

Hoewel het onderzoek op dit gebied al bijna 30 jaar gaande is en heeft geleid tot de ontwikkeling van verschillende propriëtaire commerciële producten, zijn deze onderzoeken tot onlangs niet omgezet in praktisch toepasbare producten met open source. De meeste open-source oplossingen begonnen zich in het begin van de jaren 2010 te ontwikkelen en waren vooral gericht op de programmeertaal Java.

De meest opvallende vrije implementatie uit die tijd, GumTree, is ontwikkeld door een onderzoeker met een academische achtergrond, geschreven in Java, heeft zijn eigen abstracte interne representatie, voorafgaand aan treesitter, en heeft back-ends die zowel gebaseerd zijn op treesitter als op andere tools voor het parseren van broncode naar abstracte representaties. Dit systeem kan alleen genereren (in de vorm van een tekstlog van gebeurtenissen, er is ook een API die triviaal kan worden aangeroepen vanuit elke programmeertaal met bindings naar Java) en visualiseren van wijzigingen. Echter, voor het samenvoegen van wijzigingen, alsook voor het bekijken van de door het systeem gegenereerde diff-bestanden, is het uit de doos niet toepasbaar (hoewel het waarschijnlijk is dat het uploaden van diffs via de API kan worden gerealiseerd).

Een jongere en meer praktisch toepasbare implementatie van difftastic is geschreven in Rust, gebaseerd op treesitter, en is gericht op het genereren van gemarkeerde diffs in de console. Dit systeem is ook gericht op het visualiseren van diffs en heeft absoluut niet als doel wijzigingen samen te voegen of patches toe te passen.

Onlangs is er een project genaamd mergiraf ontstaan en dit ontwikkelt zich actief. Deze tool, geschreven in Rust (het neemt 21 MiB in beslag!), is ook gebaseerd op treesitter, dat inmiddels een standaard is geworden voor parsers van contextvrije grammatica's in ontwikkeltools, net zoals LLVM dat is voor het optimaliseren van lage-niveau instructie-representaties. In tegenstelling tot zijn concurrenten biedt mergiraf functies voor het automatisch oplossen van samenvoegconflicten, in plaats van voor het genereren van diffs. Onder de motorkap gebruikt mergiraf voor het genereren van patches een implementatie van het algoritme dat in GumTree wordt gebruikt, en voor het toepassen – de implementatie van het algoritme dat in spork wordt gebruikt, aangepast voor treesitter-structuren.

Helaas is de serialisatie van patches naar bestanden die later kunnen worden toegepast niet gerealiseerd (maar het is heel goed mogelijk dat dit kan worden gerealiseerd door het parseren van de logboeken van gebeurtenissen die door GumTree worden gegenereerd). Een andere veelbelovende manier om verschillen toe te passen, kan zijn door deze niet via patches toe te passen, maar via de refactoring-functionaliteit van LSP-servers, wat kan helpen bij het detecteren van conflicten op projectniveau. Visualisatie wordt alleen ondersteund voor conflicten.

Voorbeeld van werking: de gemeenschappelijke voorouder «base.py» (inspringingen met tabs, overbodige regel aan het begin) foo = 1 def main(): print(foo + 2 + 3) «a.py» (inspringingen zijn nog steeds tabs, 2 overbodige regels aan het begin in plaats van één, voor debug-printen is de icecream-bibliotheek gebruikt, de klasse «baz» is toegevoegd: from icecream import ic foo = 1 def main(): ic(foo + 2 + 3) class baz: def __init__(self): «»»baz»»» «b.py» (de variabele «foo» is hernoemd naar «bar», verwerkt met «black» na de wijzigingen, waardoor de inspringingen nu met spaties zijn en overbodige regels zijn verwijderd): bar = 1 def main(): print(bar + 2 + 3) De aanroep .\/mergiraf merge .\/base.py .\/a.py .\/b.py -x a.py -y b.py -s base.py -o .\/res.py levert het volgende resultaat op from icecream import ic bar = 1 def main(): ic(bar + 2 + 3) class baz: def __init__(self): «»»baz»»» (voor debug-printen is de icecream-bibliotheek gebruikt, de variabele «foo» is hernoemd naar «bar», verwerkt met «black» na de wijzigingen, waardoor de inspringingen nu met spaties zijn en overbodige regels zijn verwijderd, een mix van tabs en spaties voor inspringing, maar in een toegestane vorm).

Hier is het nadeel van het instrument zichtbaar. De documentstijl wordt meestal geconfigureerd in «.editorconfig»-bestanden, en globale stijlwijzigingen, zoals het vervangen van tabs door spaties en het aannemen van de stijl van black, zoals gedaan in «b.py», worden meestal vergezeld door wijzigingen in «.editorconfig». Daarom moet het instrument voor een meer correcte toepassing van dergelijke wijzigingen een concept hebben voor een globale standaardstijl en in staat zijn om instellingen uit «.editorconfig» op te halen.

Bron: opennet.ru

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster