Ang paglabas ng mergiraf 0.4 na proyekto ay nai-publish, na bumubuo ng isang driver para sa Git na may pagpapatupad ng posibilidad ng three-way merging. Sinusuportahan ng Megiraf ang paglutas ng iba't ibang uri ng mga salungatan sa pagsasanib at maaaring magamit para sa iba't ibang wika ng programming at mga format ng file. Posibleng tawagan nang hiwalay ang mergiraf upang pangasiwaan ang mga salungatan na lumitaw kapag nagtatrabaho sa karaniwang Git, o palitan ang merge handler sa Git upang palawakin ang mga kakayahan ng mga command tulad ng merge, revert, rebase at cherry-pick. Ang code ay ipinamahagi sa ilalim ng lisensya ng GPLv3. Ang bagong bersyon ay nagdaragdag ng suporta para sa Python, TOML, Scala at Typescript na mga wika, pati na rin ang mga pag-optimize ng pagganap.
Nasa ibaba ang isang detalyadong paglalarawan ng mga problemang nalutas gamit ang mergiraf:
Ang software ay isang pangunahing halimbawa ng isang napakakomplikadong sistema. Ang mga kumplikadong sistema ay may isang bagay na karaniwan - ang mga ito ay KOMPLEX - at hindi mo maasahan ang kumplikadong pag-uugali na gusto mong mangyari kapag nagkataon. Sa halip, ang mga sistemang ito ay nagbabago sa paglipas ng panahon, hakbang-hakbang, at ang bawat mutation ay maingat na sinusuri sa bawat yugto. Upang makamit ito, kinakailangan ang isang mahusay na tinukoy na istraktura at naaangkop na mga tool. Ang ebolusyon ng anumang kumplikadong sistema ay maaaring mailarawan bilang isang nakadirekta na puno, kung saan ang ugat ay ang walang laman na hanay ng mga pag-andar at bawat node—maliban sa ugat—ay resulta ng paglalapat ng mutation sa magulang nito.
Sa konteksto ng mga produkto, ang bawat node ay tinatawag na "bersyon," na kumakatawan sa isang partikular na hanay ng mga function at anti-function. Anumang pagbabago sa set na ito ay itinuturing na isang mutation, na bumubuo ng isang gilid sa aming nakadirekta na acyclic graph. Ang mga function na ito ay likas na abstract; hindi nila direktang sinasalamin ang mga paraan kung saan gumagana ang mga pisikal na sistema, ngunit sa halip ay ipinapakita kung paano nakikita ng mga matatalinong ahente ang pagiging kapaki-pakinabang ng mga sistemang iyon. Upang isalin ang mga ideya sa mga pagpapatupad sa totoong mundo, kailangan mong i-roll up ang iyong mga manggas at sumisid sa *sapat* na mga detalyeng mababa ang antas na maaaring ipahayag at ipaliwanag nang eksakto kung paano gumagana ang mga bagay. Sa pagbuo ng software, ang mga mababang antas na ito ay karaniwang kinakatawan ng source code.
Upang unti-unting maipasok ang kanilang source code sa isang estado na nagpapakita ng nais na gawi, at upang idokumento kung paano nila ito nakuha doon, kinakatawan ng mga programmer ang kanilang trabaho sa mga tuntunin ng mga snapshot at pagbabago. Ang isang snapshot ay kumakatawan sa isang partikular na estado ng isang produkto kasama ang lahat ng mababang antas ng mga detalye, habang ang isang changeset ay kumakatawan sa isang paglipat sa pagitan ng mga snapshot. Kadalasan, ang mga snapshot ay inilalabas ng mga solong pagbabago sa kanilang mga magulang, kaya ang mga snapshot na ito ay halos palaging may label na ginagawa ang mga pagbabago na lumikha sa kanila, kaya ang mga termino ay kadalasang ginagamit nang palitan.
Minsan may mga snapshot na nakuha bilang resulta ng ilang transition - merge commits. Mahirap silang katrabaho, kaya kadalasan ay iniiwasan sila. Ang mga modernong open source na bersyon ng control system gaya ng Git ay nagbibigay ng mga pangunahing kakayahan para sa pamamahala ng mga development workflow. Pinapayagan nila ang mga developer na ayusin ang mga snapshot bilang mga nakadirekta na acyclic graph, i-annotate ang mga ito ng mga komento, at muling ayusin ang mga ito kung kinakailangan.
Ang functionality na ito ay nagbibigay-daan sa mga developer na magsulat ng isang makabuluhang kasaysayan ng proyekto, na mahalaga para sa pag-debug at pagsagot sa mga tanong tulad ng "Bakit ipinakilala ang mababang antas na detalyeng ito (hal. variable)?", "Humigit-kumulang ilang porsyento ng aking kontribusyon sa proyektong ito ay ?”, “ Sino ang na-hack ng mga pagpapatupad ng bookmark at kailan?”, “Anong mababang antas na pagbabago ang sumira sa function na ito (bagaman parang hindi dapat, sinuri namin ang lahat!?)”
Ang mga version control system ay umaakma dito sa konsepto ng isang sangay, isang mababang antas na konsepto na nangangahulugan lamang ng magkadikit na bahagi ng mababang antas ng kasaysayan ng isang proyekto na may kahulugan sa semantiko para sa developer. Karaniwang ginagamit ang mga sangay para sa mga partikular na pagpapatupad ng mga tampok, kung minsan maraming sangay ang nilikha para sa iba't ibang mga pagpapatupad ng kandidato ng parehong tampok. Gamit ang mga sumasanga na daloy ng trabaho (na halos mainstream at isang pamantayan sa pag-unlad, na ginagamit saanman at saanman), ang bawat indibidwal na developer ay epektibong makakapangasiwaan ang maraming magkasalungat na sangay ng isang proyekto, na ang bawat isa ay naiiba sa maturity o kalidad. Nagbibigay-daan ito sa mga developer na pagsamahin ang mga resulta ng kanilang trabaho at ng ibang tao nang hindi muling tina-type nang manu-mano ang lahat sa bawat oras.
Kadalasan, ang isang master branch ay nilikha na kumakatawan sa "opisyal" na produkto, mula sa kung saan ang mga side branches branch para sa bawat feature, na regular (perpekto pagkatapos ng bawat commit) na naka-synchronize sa pangunahing sangay, na nagpapahintulot sa mga developer na magtrabaho kasama ang pinakabagong bersyon ng produkto at sabay-sabay na ipinapatupad ang mga tampok na gusto nila ay kasalukuyang binuo sa pamamagitan ng pagtuklas ng mga problema na nagreresulta mula sa mga aksyon ng iba pang mga developer sa lalong madaling panahon.
Kapag sinusubukang pagsamahin ang mga function ng iba't ibang mga snapshot (na simpleng paghahanap ng isang karaniwang ninuno, at paglalapat ng mga pagbabago na bumubuo sa kanila, nang sunud-sunod sa itaas ng isa pa, ang operasyong ito ay tinatawag na rebase, ang pagsasama ay halos tulad ng isang rebase, ito ay bumubuo lamang ng commit. magkaiba ang graph, bilang isang resulta na ginagawang hindi maginhawa para sa kanila na manipulahin, kaya sinubukan nilang abandunahin ang mga pagsasanib sa pabor ng mga rebase) na lumitaw ang mga problema. Gumagamit ang mga modernong version control system (VCS) ng mga internal change merging algorithm na hinahati lang ang mga file sa mga indibidwal na linya, tinatrato ang bawat linya bilang isang character at ang mga file bilang mga pagkakasunud-sunod ng mga ito, at pagkatapos ay ilapat ang mga algorithm upang pagsamahin ang mga ito na nagmumula sa bioinformatics.
Sa kasamaang palad, ang line-by-line na representasyon ng source code ay walang kinalaman sa nilalaman nito. Ang tanging bentahe nito ay simple at unibersal. Ang hindi pagkakapare-pareho ay humahantong sa mga salungatan, na isang palaging pinagmumulan ng sakit ng ulo para sa mga developer. Ang pagresolba ng mga salungatan ay nangangailangan ng developer na maingat na pag-aralan ang parehong bersyon ng code, hindi lamang ang mga seksyong itinalaga ng line-by-line na paghahambing na algorithm bilang "nabago" o "sumasalungat," ngunit posibleng ang buong proyekto.
Dapat na maunawaan ng developer ang mga pagbabago, manu-manong isulat ang pinagsamang code, at lutasin ang anumang hindi pagkakapare-pareho. Ang mga problema ay nagiging mas problema kapag ang line-by-line na tool ay hindi wastong natukoy ang mga pagbabago, na kadalasang nangyayari sa malalaking pagbabago, kabilang ang mga walang kabuluhan tulad ng code reformatting. Kung ang mga kasunod na pagbabago ay hindi mailapat sa manu-manong pinagsamang code, ang sitwasyon ay magiging isang kumpletong bangungot. Sa kabila ng nakakatakot na mga kaso, ang line-by-line na algorithm ay gumagana sa halos lahat ng oras, lalo na kung aktibong sinusubukan ng mga developer na huwag magdulot ng mga problema para dito. Ang isang paraan upang mabawasan ang mga ganitong problema ay ang pag-atas sa mga source code na iproseso gamit ang mga tool sa canonicalization tulad ng itim.
Siyempre, ang tamang solusyon sa mga kahila-hilakbot na kaso (at sa pangkalahatan, hindi lamang para sa kanila, ang line-by-line algorithm ay isang heuristic, maaari itong walang kabuluhang humantong sa sirang code, halimbawa, pinalitan ng isang developer ang pangalan ng variable, at isa pa sa sa parehong oras ay nagsulat ng isang piraso ng bagong code gamit ang variable na iyon, hindi magkakaroon ng pagsasanib/rebasing dito, ngunit ang resulta ay magiging hindi gumagana) - ito ay ang paggamit ng tamang panloob na modelo.
Sa kabila ng katotohanan na ang pananaliksik sa lugar na ito ay nagpapatuloy nang humigit-kumulang 30 taon, at nagresulta sa paglikha ng ilang pagmamay-ari na komersyal na produkto, ang pananaliksik na ito ay hindi naging praktikal na open source na mga produkto hanggang kamakailan lamang. Ang karamihan ng mga solusyon sa open source software ay nagsimulang bumuo noong unang bahagi ng 2010s, at pangunahing nakatuon sa wikang Java.
Ang pinakakilalang libreng pagpapatupad ng panahong iyon, ang GumTree, ay nilikha ng isang mananaliksik na may background na pang-akademiko, na nakasulat sa Java, ay may sariling abstract na panloob na representasyon na nauna sa treesitter, at may mga backend na parehong batay sa treesitter at batay sa iba pang mga tool para sa pag-parse ng pinagmulan code sa mga abstract na representasyon. Ang system na ito ay maaari lamang bumuo (sa anyo ng isang text log ng kaganapan; mayroon ding isang API na madaling tawagan mula sa anumang wika na may mga binding sa Java) at mailarawan ang mga pagbabago. Gayunpaman, para sa pagsasama-sama ng mga pagbabago, pati na rin para sa pagtingin sa mga diff file na nabuo nito, hindi ito naaangkop sa labas ng kahon (gayunpaman, malamang na ang pag-load ng mga pagkakaiba ay maaaring ipatupad sa pamamagitan ng API).
Ang isang mas bata at mas praktikal na pagpapatupad ng difftastic ay nakasulat sa Rust, batay sa treesitter, at nakatuon sa pagbuo ng mga naka-highlight na diff sa console. Nilalayon din ng system na ito na makita ang mga pagkakaiba at hindi karaniwang nilalayon na pagsamahin ang mga pagbabago o maglapat ng mga patch.
Ang proyekto ng mergiraf ay lumitaw kamakailan at aktibong umuunlad. Ang tool na ito, na nakasulat sa Rust (21 MiB!), ay batay din sa treesitter, na naging kasing dami ng pamantayan para sa mga parser ng grammar na walang konteksto sa mga tool sa pag-develop gaya ng naging LLVM para sa pag-optimize ng mga representasyon ng pagtuturo sa mababang antas. Hindi tulad ng mga kakumpitensya nito, ang mergiraf ay nagbibigay ng mga function hindi para sa pagbuo ng mga diff, ngunit para sa awtomatikong paglutas ng mga merge conflict. Sa ilalim ng hood, ang mergiraf ay gumagamit ng isang pagpapatupad ng algorithm na ginagamit sa GumTree upang bumuo ng mga patch, at para sa aplikasyon, isang pagpapatupad ng algorithm na ginagamit sa spork, na inangkop para sa mga istruktura ng treesitter.
Ang serialization ng mga patch sa mga file na maaaring ilapat sa ibang pagkakataon, sa kasamaang-palad, ay hindi ipinatupad (ngunit malamang na maipapatupad ito sa pamamagitan ng pag-parse ng mga log ng kaganapan na nabuo ng GumTree). Ang isa pang maaasahang paraan upang maglapat ng mga pagkakaiba ay ang paglalapat ng mga pagkakaiba hindi sa pamamagitan ng mga patch, ngunit sa pamamagitan ng refactoring functionality ng mga LSP server, na maaaring makatulong sa pagtukoy ng mga salungatan sa antas ng buong proyekto. Ang visualization ay sinusuportahan lamang para sa mga salungatan.
Halimbawa sa trabaho: karaniwang ninuno na "base.py" (mga naka-indent na tab, dagdag na linya sa simula) foo = 1 def main(): print(foo + 2 + 3) "a.py" (indented still tabs, 2 extra lines sa simula, sa halip na isa, ang icecream library ay ginagamit para sa debug printing, ang "baz" na klase ay idinagdag: mula sa icecream import ic foo = 1 def main(): ic(foo + 2 + 3) class baz: def __init__(self): """baz""" "b.py" (variable na "foo" na pinalitan ng pangalan sa "bar", na pinoproseso gamit ang "black" pagkatapos ng mga pagbabago, na nagreresulta sa indentation na may mga puwang at dagdag pinutol ang mga linya): bar = 1 def main(): print(bar + 2 + 3) Call ./mergiraf merge ./base.py ./a.py ./b.py -x a.py -y b.py -s base.py -o ./res.py ay nagbibigay ng sumusunod na resulta mula sa icecream import ic bar = 1 def main(): ic(bar + 2 + 3) class baz: def __init__ (sarili): """baz""" (para sa pag-print ng debug, ginagamit ang "icecream" library, ang "foo" variable ay pinalitan ng pangalan sa "bar", na pinoproseso gamit ang "black" pagkatapos ng mga pagbabago, na nagreresulta sa indentation — mga puwang at dagdag na linya na pinutol, pinaghalong mga tab at mga puwang para sa indentation, ngunit ang pinapayagang anyo).
Ang kakulangan ng isang tool ay makikita kaagad. Karaniwang naka-configure ang pag-istilo ng dokumento sa mga .editorconfig na file, at ang mga pagbabago sa pandaigdigang istilo, tulad ng pagpapalit ng mga tab sa mga puwang at paggamit ng itim na istilo tulad ng ginagawa sa b.py, ay karaniwang sinasamahan ng mga pagbabago sa .editorconfig. Samakatuwid, para sa isang mas tamang aplikasyon ng mga naturang pagbabago, ang tool ay dapat magkaroon ng isang konsepto para sa isang pandaigdigang "default" na istilo, at magagawang i-pull up ang mga setting mula sa ".editorconfig".
Pinagmulan: opennet.ru
