mergiraf — një mjet i orientuar nga AST për bashkimin trepalësh në Git

Është publikuar lëshimi i projektit mergiraf 0.4, që zhvillon një driver për Git me implementimin e mundësisë së bashkëveprimit të trefishtë. Mergiraf mbështet zgjidhjen e llojeve të ndryshme të konflikteve gjatë bashkimit dhe mund të përdoret për gjuhë programimi të ndryshme dhe formate skedujsh. Është e mundur një thirrje e veçantë mergiraf për të trajtuar konfliktet që shkaktohen gjatë punës me Git-in standard, si dhe zëvendësimi në Git të处理 të bashkimeve për të zgjeruar mundësitë e komandave si merge, revert, rebase dhe cherry-pick. Kodi shpërndahet nën licencën GPLv3. Në versionin e ri është shtuar mbështetje për gjuhët Python, TOML, Scala dhe Typescript, si dhe është optimizuar performanca.

Më poshtë është një përshkrim i detajuar i problemeve që zgjidhen me anë të mergiraf:

Softueri është një shembull i shkëlqyer i një sistemi jashtëzakonisht kompleks. Sistemet e komplikuara kanë një veçori të përbashkët — ato janë TË KOMPLESIVE — dhe nuk mund të prisni që sjellja e komplikuar që ju nevojitet të shfaqet rastësisht. Në vend të kësaj, këto sisteme evoluojnë me kalimin e kohës, hap pas hapi, dhe çdo mutacion verifikohet me kujdes në çdo hap. Për të arritur këtë është e nevojshme një strukturë e mirëfilltë dhe mjete përkatëse. Evolucioni i çdo sistemi të komplikuar mund të vizualizohet si një pemë e orientuar, ku rrënja përfaqëson një grup të zbrazët funksionesh, dhe çdo nyjë — përveç rrënjës — është një rezultat i aplikimit të një mutacioni ndaj prindit të saj.

Në kontekstin e produkteve, çdo nyjë quhet "version", e cila përfaqëson një grup të caktuar funksionesh dhe anti-funksionesh. Çdo ndryshim i këtij grupi konsiderohet si një mutacion, që formon një skaj në grafikun tonë të orientuar aciklik. Këto funksione në thelb janë abstrakte; ato nuk pasqyrojnë drejtpërdrejt mënyrat se si funksionojnë sistemet fizike, por më tepër tregojnë se si agjentët racionalë perceptojnë dobishmërinë e këtyre sistemeve. Për të përkthyer idetë në realizime ekzistuese në botën reale, është e nevojshme të mbledhim mëngët dhe të zhytemi në detajet *mjaft* të nivelit të ulët, në një gjuhë me të cilin mund të shprehim dhe shpjegojmë se si funksionon konkretisht gjithçka. Në zhvillimin e softuerit, këto nivel të ulët zakonisht përfaqësohen nga kodi burimor.

Për të sjellë gradualisht kodin burimor në një gjendje që tregon sjelljen e kërkuar dhe për të dokumentuar se si arritën aty, programuesit e paraqesin punën e tyre në terma snapshot-esh dhe changeset-esh. Snapshot-i përfaqëson një gjendje të caktuar të produktit me të gjitha detajet e nivelit të ulët, ndërsa changeset-i shënon kalimin mes snapshot-esh. Në përgjithësi, snapshot-et krijohen nga changeset-et e vetme për prindërit e tyre, prandaj këto snapshot-e zakonisht shënohen me atë që bëjnë changeset-et që i krijuan, kështu që këto terma shpesh përdoren ndërlidhur.

Ndonjëherë ekzistojnë kopje që rezultojnë nga disa kalime — bashkimi i komiteteve. Ato janë të vështira për t'u punuar, prandaj zakonisht shmangen. Sistemete moderne të kontrollit të versioneve me burim të hapur, si Git, ofrojnë mundësi shumë themelore për menaxhimin e proceseve të zhvillimit. Ato lejojnë zhvilluesit të organizojnë kopje në formën e grafikëve të orientuar aciklik, t'i anotojnë ato me komente dhe, në rast nevoje, të ndryshojnë rendin e tyre.

Ky kjo funksionalitet u lejon zhvilluesve të shkruajnë një histori projektit që ka kuptim semantik, e cila është thelbësore për debugging dhe për t'u përgjigjur pyetjeve si "Pse u fut ky detaj i ulët (p.sh., variabla)?", "Sa përqind është kontribuimi im në këtë projekt?", "Kush e komprometoi implementimin e backdoor dhe kur?", "Cila ndryshim i ulët e prishi këtë funksion (ndonëse nuk duhej, ne e kontrolluam gjithçka!?)"

Sistemet e kontrollit të versioneve e plotësojnë këtë koncept me degën - një koncept të ulët që do të thotë thjesht një fragment të vazhdueshëm të historisë ulët të projektit, semantikisht të rëndësishëm për zhvilluesin. Degët zakonisht përdoren për implementimin e veçorive specifike, ndonjëherë krijohen disa degë për kandidatët e ndryshme në implementimin e të njëjtës veçori. Duke përdorur flukset e punës me degëzim (të cilat në fakt janë rrjedha kryesore dhe standardi i zhvillimit, përdoren kudo dhe gjithandej), çdo zhvillues i veçantë mund të menaxhojë në mënyrë efektive shumë degë konflikti të projektit, secila e ndryshme sipas nivelit të gatishmërisë ose cilësisë. Kjo u lejon zhvilluesve të bashkojnë rezultatet e punës së tyre dhe të tjerëve pa e riparë gjithçka manualisht çdo herë.

Zakonisht krijohet një degë kryesore që përfaqëson produktin "zyrtar", nga e cila dege të anësishme ndahen për çdo veçori, të cilat rregullisht (idealisht - pas çdo komitimi) sinkronizohen me degën kryesore, duke u lejuar zhvilluesve të punojnë me versionin më të fundit të produktit dhe njëkohësisht të implementojnë veçoritë që ata po zhvillojnë aktualisht, duke identifikuar problemet që rriten nga veprimet e zhvilluesve të tjerë sa më shpejt të jetë e mundur.

Kur përpiqesh të kombinosh funksionet e snapshot-eve të ndryshme (çka është thjesht gjetja e paraardhësit të përbashkët dhe aplikimi i seteve të ndryshimeve që i krijojnë ato, një pas një, mbi një tjetër, kjo operacion quhet rebase, ndërsa bashkimi — është pothuajse si rebase, thjesht strukturojnë grafikun e komiteteve ndryshe, si rezultat i të cilit bëhet e vështirë të manipulohet, prandaj nga bashkimet përpiqen të heqin dorë në favor të rebase-ve), gjithmonë hasen probleme. Sistemet moderne të kontrollit të versioneve (VCS) përdorin algorithemb të brendshme për bashkimin e ndryshimeve, që thjesht ndajnë skedarët në rreshta të veçantë, i konsiderojnë çdo rresht si një simbol, dhe skedarët si sekuenca të tyre, dhe më pas aplikojnë algorithembet për bashkimin e tyre, që janë nga bioinformatika.

Fatkeqësisht, kjo paraqitje rreshtore e kodit burimor nuk ka asnjë lidhje me përmbajtjen e tij. Cilësia e vetme e saj është se është e thjeshtë dhe universale. Paqartësia çon në konflikte, duke qenë një burim i vazhdueshëm i dhimbjeve të kokës për zhvilluesit. Zgjidhja e konflikteve kërkon nga zhvilluesi një shqyrtim të kujdesshëm të të dy versioneve të kodit, përfshirë jo vetëm seksionet e shënuara nga algoritmi rreshtor si 'të ndryshuara' ose 'në konflikt', por ndoshta edhe të gjithë projetin.

Zhvilluesi duhet të kuptojë ndryshimet, të shkruajë manualisht kodin e bashkuar dhe të eliminojë çdo paqartësi. Problemet shtohen kur instrumenti rreshtor identifikon gabimisht ndryshimet, gjë që ndodh shpesh gjatë ndryshimeve të mëdha, përfshirë ato triviale si ndryshimi i formatit të kodit. Nëse ndryshimet pasuese nuk mund të aplikohen mbi kodin e bashkuar manualisht, situata shndërrohet në një makth të plotë. Pavarësisht rasteve alarmuese, në shumicën e rasteve, algoritmi rreshtor funksionon, veçanërisht nëse zhvilluesit përpiqen aktivisht të mos i krijojnë probleme. Një nga mënyrat për të minimizuar këto probleme është kërkesa e detyrueshme për trajtimin e burimeve nga instrumente kanonizimi, si black.

Natyrisht, zgjidhja e duhur për të gjitha rastet e tmerrshme (dhe në përgjithësi, jo vetëm për to, algoritmi rresht-për-rresht — është një heuristikë, ai mund të çojë në kod jo funksional në mënyrë triviale, për shembull, një zhvillues e emëron një variabël ndryshe, ndërsa një tjetër shkruan një copë kod të ri që përdor atë variabël, nuk do të ketë një konflikt fusioni/promovimi këtu, por rezultati do të bëhet jo funksional) — është përdorimi i modelit të duhur të brendshëm.

Pavarësisht se kërkimet në këtë fushë janë zhvilluar për rreth 30 vjet, dhe kanë rezultuar në krijimin e disa produkteve komerciale pronësore, këto kërkime deri në kohët e fundit nuk janë shndërruar në produkte praktikisht të aplikueshme me burim të hapur. Masa kryesore e zgjidhjeve të softuerit të lirë filloi të zhvillohej në fillim të viteve 2010 dhe ishte e fokusuar kryesisht në gjuhën Java.

Implementimi më i dukshëm i lirë i atij periudhe, GumTree, u krijua nga një hulumtues me një sfond akademik, është shkruar në Java, ka një përfaqësim të brendshëm abstrakt, që i përkiste para treesitter, ka backend si ato që bazohen në treesitter ashtu edhe ato që bazohen në mjete të tjera për analizimin e kodit burimor në përfaqësime abstrakte. Ky sistem di vetëm të gjenerojë (në formën e një logu të ngjarjeve tekstuale, po ashtu ka një API që mund të thirret lehtësisht nga çdo gjuhë programimi që ka lidhje me Java) dhe të vizualizojë ndryshimet. Megjithatë, për përzierjen e ndryshimeve, po ashtu si për shikimin e skedarëve diff të gjeneruar prej saj, ai është i papërshtatshëm nga kutia (megjithatë, është e mundur që ngarkimi i diffeve mund të realizohet përmes API-së).

Një implementim më i ri dhe më praktik i difftastic, i shkruar në Rust, bazohet në treesitter, është fokusuar në gjenerimin e diffeve të ndriçuara në konsolë. Ky sistem gjithashtu është i orientuar drejt vizualizimit të diffeve dhe në përgjithësi nuk ka si qëllim përzierjen e ndryshimeve ose aplikimin e patch-eve.

Së fundmi ka dalë dhe po zhvillohet aktivisht projekti mergiraf. Ky mjet i shkruar në Rust (zë 21 MiB!) gjithashtu bazohet në treesitter, i cili është bërë standard për parserë të gramatikave kontekstualisht të lirë në mjetet e zhvillimit, ashtu siç është bërë LLVM për optimizimin e përfaqësimeve të ulëta të instruksioneve. Ndryshe nga konkurrentët, mergiraf ofron funksione jo për të gjeneruar diffs, por për të zgjidhur automatikisht konfliktet e bashkimeve. Nën kapak, mergiraf përdor për gjenerimin e patches një implementim të algoritmit të përdorur në GumTree, dhe për aplikimin — një implementim të algoritmit të përdorur në spork, të adaptuar për strukturat treesitter.

Serializimi i patch-eve në skedarë, të cilat mund të aplikohen më vonë, për fat të keq, nuk është realizuar (por është e mundur që të realizohet përmes analizimit të logeve të ngjarjeve që gjenerohen nga GumTree). Një mënyrë tjetër e premtuar për aplikimin e ndryshimeve mund të jetë përdorimi i ndryshimeve jo përmes patch-eve, por përmes funksionalitetit të rishikimit të serverëve LSP, që mund të ndihmojë në identifikimin e konflikteve në nivelin e tërë projektit. Vizualizimi mbështetet vetëm për konfliktet.

Shembulli i punës: përmendorja e përbashkët "base.py" (indentaime me tab, rreshti i tepërt në fillim) foo = 1 def main(): print(foo + 2 + 3) "a.py" (indentaime akoma me tab, 2 rreshta të tepërt në fillim në vend të njërit, për printim për debug përdoret biblioteka icecream, klasa "baz" është shtuar: from icecream import ic foo = 1 def main(): ic(foo + 2 + 3) class baz: def __init__(self): """baz""" "b.py" (variabla "foo" e riemëruar në "bar", e përpunuar me "black" pas ndryshimeve, si rezultat indentaimet — janë me hapësira dhe rreshtat e tepërt janë hequr): bar = 1 def main(): print(bar + 2 + 3) Thirrja .\/mergiraf merge .\/base.py .\/a.py .\/b.py -x a.py -y b.py -s base.py -o .\/res.py jep rezultatin e mëposhtëm from icecream import ic bar = 1 def main(): ic(bar + 2 + 3) class baz: def __init__(self): """baz""" (për printim për debug përdoret biblioteka "icecream", variabla "foo" e riemëruar në "bar", e përpunuar me "black" pas ndryshimeve, si rezultat indentaimet — janë me hapësira dhe rreshtat e tepërt janë hequr, përzierje tab dhe hapësirash për indentaim, por dukuria e lejuar).

Këtu shfaqet një mangësi e mjetit. Stili i dokumentit zakonisht konfigurohet në skedarët "editorconfig", dhe ndryshime globale të stilit, si kalimi nga tabullat në hapësira dhe pranimi i stilit black, siç u bë në "b.py", zakonisht shoqërohen me ndryshime në "editorconfig". Prandaj, për një zbatim më të saktë të ndryshimeve të tilla, mjeti duhet të ketë një koncept për stilin global "për të gjithë", dhe të jetë në gjendje të marrë cilësimet nga "editorconfig".

Burimi: opennet.ru

Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS 🔥 Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS | ProHoster