{"id":120981,"date":"2024-12-14T09:45:59","date_gmt":"2024-12-14T07:45:59","guid":{"rendered":"https:\/\/prohoster.info\/blog\/novosti-interneta\/mergiraf-ast-orientirovannyj-instrument-dlya-tryohstoronnego-sliyaniya-v-git"},"modified":"2024-12-14T09:45:59","modified_gmt":"2024-12-14T07:45:59","slug":"mergiraf-ast-orientirovannyj-instrument-dlya-tryohstoronnego-sliyaniya-v-git","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/news\/mergiraf-ast-orientirovannyj-instrument-dlya-tryohstoronnego-sliyaniya-v-git","title":{"rendered":"mergiraf \u2014 narz\u0119dzie oparte na AST do tr\u00f3jstronnego \u0142\u0105czenia w Git","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Opublikowano wydanie projektu mergiraf 0.4, kt\u00f3ry rozwija sterownik dla Git z mo\u017cliwo\u015bci\u0105 tr\u00f3jstronnego \u0142\u0105czenia. Mergiraf wspiera rozwi\u0105zywanie r\u00f3\u017cnych rodzaj\u00f3w konflikt\u00f3w przy \u0142\u0105czeniu i mo\u017ce by\u0107 u\u017cywany dla r\u00f3\u017cnych j\u0119zyk\u00f3w programowania oraz format\u00f3w plik\u00f3w. Mo\u017cliwe jest zar\u00f3wno osobne wywo\u0142anie mergiraf do obs\u0142ugi konflikt\u00f3w, kt\u00f3re pojawiaj\u0105 si\u0119 podczas pracy z standardowym Git, jak i zast\u0105pienie w Git przetwornika \u0142\u0105czenia, aby rozszerzy\u0107 mo\u017cliwo\u015bci takich komend jak merge, revert, rebase i cherry-pick. Kod jest rozpowszechniany na licencji GPLv3. W nowej wersji dodano wsparcie dla j\u0119zyk\u00f3w Python, TOML, Scala i Typescript, a tak\u017ce przeprowadzono optymalizacj\u0119 wydajno\u015bci.    <\/p>\n<p>Poni\u017cej znajduje si\u0119 szczeg\u00f3\u0142owy opis problem\u00f3w, kt\u00f3re mo\u017cna rozwi\u0105za\u0107 przy pomocy mergiraf:    <\/p>\n<p>Oprogramowanie jest doskona\u0142ym przyk\u0142adem niezwykle z\u0142o\u017conego systemu. Z\u0142o\u017cone systemy maj\u0105 jedn\u0105 wsp\u00f3ln\u0105 cech\u0119 \u2014 s\u0105 Z\u0141O\u017bONE \u2014 i nie mo\u017cna oczekiwa\u0107, \u017ce po\u017c\u0105dane z\u0142o\u017cone zachowanie pojawi si\u0119 przypadkowo. Zamiast tego te systemy ewoluuj\u0105 z czasem, krok po kroku, a ka\u017cda mutacja jest starannie weryfikowana na ka\u017cdym etapie. Aby to osi\u0105gn\u0105\u0107, potrzebna jest dobrze okre\u015blona struktura i odpowiednie narz\u0119dzia. Ewolucj\u0119 ka\u017cdego z\u0142o\u017conego systemu mo\u017cna wizualizowa\u0107 jako skierowane drzewo, w kt\u00f3rym korze\u0144 reprezentuje pusty zbi\u00f3r funkcji, a ka\u017cdy w\u0119ze\u0142 \u2014 z wyj\u0105tkiem korzenia \u2014 jest wynikiem zastosowania mutacji do swojego rodzica.     <\/p>\n<p>W kontek\u015bcie produkt\u00f3w ka\u017cdy w\u0119ze\u0142 nazywa si\u0119 \u201ewersj\u0105\u201d, kt\u00f3ra jest okre\u015blonym zestawem funkcji i antyfunkcji. Jakiekolwiek zmiany w tym zestawie s\u0105 uwa\u017cane za mutacj\u0119 tworz\u0105c\u0105 kraw\u0119d\u017a w naszym skierowanym acyklicznym grafie. Te funkcje s\u0105 z definicji abstrakcyjne; nie odzwierciedlaj\u0105 bezpo\u015brednio sposob\u00f3w funkcjonowania system\u00f3w fizycznych, lecz pokazuj\u0105, w jaki spos\u00f3b rozumne agenty postrzegaj\u0105 u\u017cyteczno\u015b\u0107 tych system\u00f3w. Aby przet\u0142umaczy\u0107 pomys\u0142y na istniej\u0105ce w rzeczywistym \u015bwiecie realizacje, nale\u017cy zakasa\u0107 r\u0119kawy i zanurzy\u0107 si\u0119 w *wystarczaj\u0105co* niskopoziomowe szczeg\u00f3\u0142y, w j\u0119zyku, kt\u00f3rym mo\u017cna wyrazi\u0107 i wyja\u015bni\u0107, jak dok\u0142adnie wszystko dzia\u0142a. W rozwoju oprogramowania te niskopoziomowe szczeg\u00f3\u0142y zazwyczaj s\u0105 reprezentowane przez kod \u017ar\u00f3d\u0142owy.    <\/p>\n<p>Aby stopniowo doprowadzi\u0107 kod \u017ar\u00f3d\u0142owy do stanu, kt\u00f3ry wykazuje po\u017c\u0105dane zachowanie, a tak\u017ce udokumentowa\u0107, w jaki spos\u00f3b to osi\u0105gni\u0119to, programi\u015bci przedstawiaj\u0105 swoj\u0105 prac\u0119 w kategoriach snapshot\u00f3w i changest\u00f3w. Snapshot to okre\u015blony stan produktu z wszystkimi niskopoziomowymi szczeg\u00f3\u0142ami, podczas gdy changeset oznacza przej\u015bcie mi\u0119dzy snapshotami. Zwykle snapshoty s\u0105 generowane z pojedynczych changeset\u00f3w ich rodzic\u00f3w, dlatego te snapshoty prawie zawsze oznaczane s\u0105 przez te changesety, kt\u00f3re je stworzy\u0142y, co sprawia, \u017ce te terminy s\u0105 cz\u0119sto u\u017cywane zamiennie.     <\/p>\n<p>Czasami istniej\u0105 zrzuty, uzyskane w wyniku kilku przej\u015b\u0107 \u2014 scalania commit\u00f3w. Trudno si\u0119 z nimi pracuje, dlatego zazwyczaj s\u0105 unikane. Nowoczesne systemy kontroli wersji z otwartym \u017ar\u00f3d\u0142em, takie jak Git, oferuj\u0105 do\u015b\u0107 podstawowe mo\u017cliwo\u015bci zarz\u0105dzania procesami rozwoju. Pozwalaj\u0105 programistom organizowa\u0107 zrzuty w postaci skierowanych acyklicznych graf\u00f3w, anotowa\u0107 je komentarzami i w razie potrzeby zmienia\u0107 ich kolejno\u015b\u0107.     <\/p>\n<p>Ta funkcjonalno\u015b\u0107 pozwala programistom pisa\u0107 semantycznie znacz\u0105c\u0105 histori\u0119 projektu, co ma kluczowe znaczenie dla debugowania oraz odpowiadania na pytania takie jak: \u201eDlaczego wprowadzono ten niski element (np. zmienn\u0105)?\u201d, \u201eJaki procent stanowi m\u00f3j wk\u0142ad w ten projekt?\u201d, \u201eKogo dotkn\u0119\u0142a infekcja backdoora i kiedy?\u201d, \u201eJakie niskopoziomowe zmiany zepsu\u0142y t\u0119 funkcj\u0119 (chocia\u017c nie powinny by\u0142y, wszystko sprawdzili\u015bmy!?)\u201d    <\/p>\n<p>Systemy kontroli wersji uzupe\u0142niaj\u0105 to poj\u0119ciem ga\u0142\u0119zi \u2014 niskopoziomowym poj\u0119ciem, kt\u00f3re oznacza po prostu ci\u0105g\u0142y fragment niskopoziomowej historii projektu, semantycznie znacz\u0105cy dla programisty. Ga\u0142\u0119zie s\u0105 zwykle u\u017cywane do konkretnej implementacji funkcji, czasami tworzy si\u0119 kilka ga\u0142\u0119zi dla r\u00f3\u017cnych kandydat\u00f3w do wdro\u017cenia tej samej funkcji. Korzystaj\u0105c z proces\u00f3w pracy z ga\u0142\u0119ziami (kt\u00f3re s\u0105 faktycznie mainstreamem i standardem rozwoju, stosowanym wsz\u0119dzie), ka\u017cdy pojedynczy programista mo\u017ce skutecznie zarz\u0105dza\u0107 wieloma konkuruj\u0105cymi ga\u0142\u0119ziami projektu, ka\u017cda z nich r\u00f3\u017cni si\u0119 stopniem gotowo\u015bci lub jako\u015bci. Umo\u017cliwia to programistom \u0142\u0105czenie wynik\u00f3w swojej pracy i pracy innych bez konieczno\u015bci r\u0119cznego ponownego wprowadzania wszystkiego za ka\u017cdym razem.     <\/p>\n<p>Zwykle tworzy si\u0119 g\u0142\u00f3wn\u0105 ga\u0142\u0105\u017a, reprezentuj\u0105c\u0105 \u201eoficjalny\u201d produkt, od kt\u00f3rej odga\u0142\u0119ziaj\u0105 si\u0119 boczne ga\u0142\u0119zie dla ka\u017cdej funkcji, kt\u00f3re regularnie (w idealnym przypadku \u2014 po ka\u017cdym commicie) synchronizuj\u0105 si\u0119 z g\u0142\u00f3wn\u0105 ga\u0142\u0119zi\u0105, co pozwala programistom pracowa\u0107 z najnowsz\u0105 wersj\u0105 produktu oraz jednocze\u015bnie wprowadza\u0107 funkcje, kt\u00f3re aktualnie rozwijaj\u0105, wykrywaj\u0105c problemy wynikaj\u0105ce z dzia\u0142a\u0144 innych programist\u00f3w jak najszybciej.    <\/p>\n<p>Podczas pr\u00f3by kombinowania funkcji r\u00f3\u017cnych snapshot\u00f3w (co polega na znalezieniu wsp\u00f3lnego przodka i zastosowaniu zmian, kt\u00f3re je generuj\u0105, jedna po drugiej, na innym, operacja ta nazywa si\u0119 rebase, natomiast merge - to prawie jak rebase, tylko \u017ce strukturyzuje graf commit\u00f3w w inny spos\u00f3b, co czyni manipulacj\u0119 nimi trudniejsz\u0105, dlatego unika si\u0119 \u0142\u0105czenia na rzecz rebase) pojawiaj\u0105 si\u0119 problemy. Nowoczesne systemy kontroli wersji (VCS) wykorzystuj\u0105 wewn\u0119trzne algorytmy \u0142\u0105czenia zmian, kt\u00f3re po prostu dziel\u0105 pliki na poszczeg\u00f3lne linie, traktuj\u0105c ka\u017cd\u0105 lini\u0119 jako symbol, a pliki jako ich sekwencje, a nast\u0119pnie stosuj\u0105 algorytmy do ich scalania, kt\u00f3re maj\u0105 swoje korzenie w bioinformatyce.     <\/p>\n<p>Niestety, takie liniowe przedstawienie kodu \u017ar\u00f3d\u0142owego nie ma wiele wsp\u00f3lnego z jego zawarto\u015bci\u0105. Jego jedyn\u0105 zalet\u0105 jest prostota i uniwersalno\u015b\u0107. Niezgodno\u015b\u0107 prowadzi do konflikt\u00f3w, b\u0119d\u0105c sta\u0142ym \u017ar\u00f3d\u0142em b\u00f3lu g\u0142owy dla programist\u00f3w. Rozwi\u0105zywanie konflikt\u00f3w wymaga od programisty dok\u0142adnego zapoznania si\u0119 z obiema wersjami kodu, i to nie tylko z sekcjami oznaczonymi przez liniowy algorytm por\u00f3wnywania jako \u201ezmienione\u201d lub \u201ekonfliktuj\u0105ce\u201d, ale mo\u017cliwe, \u017ce i ca\u0142ym projektem.     <\/p>\n<p>Programista musi zrozumie\u0107 zmiany, r\u0119cznie napisa\u0107 scalony kod i usun\u0105\u0107 wszelkie niezgodno\u015bci. Problem\u00f3w przybywa, gdy narz\u0119dzie liniowe b\u0142\u0119dnie identyfikuje zmiany, co cz\u0119sto zdarza si\u0119 przy du\u017cych modyfikacjach, w tym trywialnych, takich jak formatowanie kodu. Je\u015bli p\u00f3\u017aniejsze zmiany nie mog\u0105 by\u0107 zastosowane do r\u0119cznie scalonego kodu, sytuacja staje si\u0119 ca\u0142kowitym koszmarem. Mimo przera\u017caj\u0105cych przypadk\u00f3w, w wi\u0119kszo\u015bci sytuacji algorytm liniowy dzia\u0142a, szczeg\u00f3lnie je\u015bli programi\u015bci aktywnie staraj\u0105 si\u0119 nie stwarza\u0107 mu problem\u00f3w. Jednym ze sposob\u00f3w minimalizacji takich problem\u00f3w jest obowi\u0105zkowe przetwarzanie \u017ar\u00f3de\u0142 przez narz\u0119dzia kanonizacji, takie jak black.    <\/p>\n<p>Oczywi\u015bcie, odpowiednie rozwi\u0105zanie dla przera\u017caj\u0105cych sytuacji (i nie tylko w ich przypadku, linia po linii \u2014 to heurystyka, kt\u00f3ra w trywialny spos\u00f3b mo\u017ce prowadzi\u0107 do nie dzia\u0142aj\u0105cego kodu, na przyk\u0142ad jeden programista zmieni\u0142 nazw\u0119 zmiennej, a inny w tym czasie napisa\u0142 fragment nowego kodu, kt\u00f3ry u\u017cywa tej zmiennej, nie b\u0119dzie tu konfliktu scalania\/zmiany bazy, ale efekt stanie si\u0119 nie dzia\u0142aj\u0105cy) \u2014 to u\u017cycie odpowiedniego wewn\u0119trznego modelu.     <\/p>\n<p>Pomimo tego, \u017ce badania w tej dziedzinie prowadzone s\u0105 od oko\u0142o 30 lat i doprowadzi\u0142y do stworzenia kilku komercyjnych produkt\u00f3w w\u0142asno\u015bciowych, badania te do niedawna nie zosta\u0142y przekszta\u0142cone w praktycznie zastosowalne produkty z otwartym kodem \u017ar\u00f3d\u0142owym. G\u0142\u00f3wna masa rozwi\u0105za\u0144 open source zacz\u0119\u0142a si\u0119 rozwija\u0107 na pocz\u0105tku lat 2010-tych i by\u0142a g\u0142\u00f3wnie skoncentrowana na j\u0119zyku Java.    <\/p>\n<p>Najbardziej wyr\u00f3\u017cniaj\u0105ca si\u0119 darmowa implementacja tego okresu, GumTree, zosta\u0142a stworzona przez badacza z akademickim zapleczem. Napisa\u0142 j\u0105 w Javie, ma swoje abstrakcyjne wewn\u0119trzne przedstawienie, poprzedzaj\u0105ce treesitter, ma backendy oparte zar\u00f3wno na treesitter, jak i innych narz\u0119dziach do parsowania kodu \u017ar\u00f3d\u0142owego w abstrakcyjne przedstawienia. System ten potrafi jedynie generowa\u0107 (w formie logu tekstowego zdarze\u0144, istnieje r\u00f3wnie\u017c API, kt\u00f3re mo\u017cna \u0142atwo wywo\u0142a\u0107 z ka\u017cdego j\u0119zyka programowania maj\u0105cego powi\u0105zania z Jav\u0105) i wizualizowa\u0107 zmiany. Niestety, do \u0142\u0105czenia zmian, a tak\u017ce do przegl\u0105dania generowanych plik\u00f3w diff, nie jest on od razu zastosowalny (cho\u0107 prawdopodobnie \u0142adowanie diff\u00f3w mo\u017cna zrealizowa\u0107 poprzez API).    <\/p>\n<p>M\u0142odsza i bardziej praktyczna implementacja difftastic napisana w Rust ma oparcie w treesitter i koncentruje si\u0119 na generacji pod\u015bwietlonych diff\u00f3w w konsoli. System ten r\u00f3wnie\u017c wykierowany jest na wizualizacj\u0119 diff\u00f3w i w og\u00f3le nie ma na celu \u0142\u0105czenia zmian ani stosowania \u0142atek.    <\/p>\n<p>Niedawno pojawi\u0142 si\u0119 i aktywnie rozwija si\u0119 projekt mergiraf. To narz\u0119dzie napisane w Rust (zajmuj\u0105ce 21 MiB!) opiera si\u0119 r\u00f3wnie\u017c na treesitter, kt\u00f3re sta\u0142o si\u0119 takim samym standardem dla parser\u00f3w gramatyk kontekstowo-wolnych w narz\u0119dziach programistycznych, jak LLVM dla optymalizacji niskopoziomowych reprezentacji instrukcji. W przeciwie\u0144stwie do konkurencji, mergiraf oferuje funkcje nie do generowania r\u00f3\u017cnic (diff), a do automatycznego rozwi\u0105zywania konflikt\u00f3w scalania. Pod mask\u0105 mergiraf u\u017cywa do generacji poprawek implementacji algorytmu stosowanego w GumTree, a do zastosowania \u2014 implementacji algorytmu u\u017cywanego w spork, dostosowan\u0105 do struktur treesitter.     <\/p>\n<p>Serializacja poprawek do plik\u00f3w, kt\u00f3re mo\u017cna nast\u0119pnie zastosowa\u0107, niestety nie zosta\u0142a jeszcze zrealizowana (ale ca\u0142kiem prawdopodobne, \u017ce mo\u017ce by\u0107 wykonana poprzez parsowanie log\u00f3w zdarze\u0144 generowanych przez GumTree). Innym potencjalnym sposobem zastosowania r\u00f3\u017cnic mo\u017ce by\u0107 wykorzystanie ich nie poprzez \u0142aty, ale poprzez funkcjonalno\u015b\u0107 refaktoryzacji serwer\u00f3w LSP, co mo\u017ce pom\u00f3c w wykrywaniu konflikt\u00f3w na poziomie ca\u0142ego projektu. Wizualizacja obs\u0142ugiwana jest tylko dla konflikt\u00f3w.    <\/p>\n<p>Przyk\u0142ad dzia\u0142ania:    wsp\u00f3lny przodek \u201ebase.py\u201d (wci\u0119cia tabulatorami, zb\u0119dny wiersz na pocz\u0105tku)       foo = 1       def main():          print(foo + 2 + 3)       \u201ea.py\u201d (wci\u0119cia nadal tabulatorami, 2 zb\u0119dne wiersze na pocz\u0105tku zamiast jednego, dla wydruku debugowania wykorzystano bibliotek\u0119 icecream, dodano klas\u0119 \u201ebaz\u201d:       from icecream import ic       foo = 1       def main():          ic(foo + 2 + 3)            class baz:                  def __init__(self):                          \u201e\u201e\u201ebaz\u201e\u201e\u201e       \u201eb.py\u201d (zmienna \u201efoo\u201d zosta\u0142a przemianowana na \u201ebar\u201d, przetworzona za pomoc\u0105 \u201eblack\u201d po zmianach, w wyniku czego wci\u0119cia \u2014 zosta\u0142y przekszta\u0142cone w spacje i zb\u0119dne wiersze usuni\u0119te):       bar = 1         def main():         print(bar + 2 + 3)        Wywo\u0142anie       .\\\/mergiraf merge .\\\/base.py .\\\/a.py .\\\/b.py -x a.py -y b.py -s base.py -o .\\\/res.py      daje nast\u0119puj\u0105cy wynik       from icecream import ic       bar = 1         def main():         ic(bar + 2 + 3)           class baz:             def __init__(self):                  \u201e\u201e\u201ebaz\u201e\u201e\u201e      (dla wydruku debugowania wykorzystano bibliotek\u0119 \u201eicecream\u201d, zmienna \u201efoo\u201d zosta\u0142a przemianowana na \u201ebar\u201d, przetworzona za pomoc\u0105 \u201eblack\u201d po zmianach, w wyniku czego wci\u0119cia \u2014 zosta\u0142y przekszta\u0142cone w spacje i zb\u0119dne wiersze usuni\u0119te, mieszanka tabulator\u00f3w i spacji do wci\u0119cia, ale widok zosta\u0142 rozwi\u0105zany).    <\/p>\n<p>W tej sytuacji wida\u0107 niedoci\u0105gni\u0119cie narz\u0119dzia. Styl dokumentu zazwyczaj konfiguruje si\u0119 w plikach <em>.editorconfig<\/em>, a globalne zmiany stylu, takie jak zamiana tabulator\u00f3w na spacje i przyj\u0119cie stylu Black, jak to mia\u0142o miejsce w <em>b.py<\/em>, zazwyczaj wi\u0105\u017c\u0105 si\u0119 ze zmianami w <em>.editorconfig<\/em>. Dlatego, aby skutecznie stosowa\u0107 takie zmiany, narz\u0119dzie powinno mie\u0107 koncepcj\u0119 dla globalnego stylu <em>domy\u015blnego<\/em> i umie\u0107 pobiera\u0107 ustawienia z <em>.editorconfig<\/em>.<br \/>\n<br \/>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/www.opennet.ru\/opennews\/art.shtml?num=62402\">opennet.ru<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u0440\u0435\u043b\u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 mergiraf 0.4, \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0434\u043b\u044f Git \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0451\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u043b\u0438\u044f\u043d\u0438\u044f. Mergiraf \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0432\u0438\u0434\u043e\u0432 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u043f\u0440\u0438 \u0441\u043b\u0438\u044f\u043d\u0438\u0438 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u044f\u0437\u044b\u043a\u043e\u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432 \u0444\u0430\u0439\u043b\u043e\u0432. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 mergiraf \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0449\u0438\u0445 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441\u043e \u0448\u0442\u0430\u0442\u043d\u044b\u043c Git, \u0442\u0430\u043a \u0438 \u0437\u0430\u043c\u0435\u043d\u0430 \u0432 Git \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u043b\u0438\u044f\u043d\u0438\u0439 \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[702],"tags":[],"class_list":["post-120981","post","type-post","status-publish","format-standard","hentry","category-news"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u0440\u0435\u043b\u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 mergiraf 0.4, \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0434\u043b\u044f Git \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0451\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u043b\u0438\u044f\u043d\u0438\u044f.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/news\/mergiraf-ast-orientirovannyj-instrument-dlya-tryohstoronnego-sliyaniya-v-git\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47mergiraf \u2014 AST-\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0442\u0440\u0451\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u043b\u0438\u044f\u043d\u0438\u044f \u0432 Git | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u0440\u0435\u043b\u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 mergiraf 0.4, \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0434\u043b\u044f Git \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0451\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u043b\u0438\u044f\u043d\u0438\u044f.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/news\/mergiraf-ast-orientirovannyj-instrument-dlya-tryohstoronnego-sliyaniya-v-git\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2024-12-14T07:45:59+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2024-12-14T07:45:59+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47mergiraf \u2014 narz\u0119dzie zorientowane na AST do tr\u00f3jstronnego scalenia w Gicie | ProHoster","description":"Opublikowano wersj\u0119 projektu mergiraf 0.4, rozwijaj\u0105cego sterownik dla Gita z realizacj\u0105 mo\u017cliwo\u015bci tr\u00f3jstronnego scalenia.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/news\/mergiraf-ast-orientirovannyj-instrument-dlya-tryohstoronnego-sliyaniya-v-git","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47mergiraf \u2014 AST-\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0442\u0440\u0451\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u043b\u0438\u044f\u043d\u0438\u044f \u0432 Git | ProHoster","og:description":"\u041e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u0440\u0435\u043b\u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 mergiraf 0.4, \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0434\u043b\u044f Git \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0451\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u043b\u0438\u044f\u043d\u0438\u044f.","og:url":"https:\/\/prohoster.info\/pl\/blog\/news\/mergiraf-ast-orientirovannyj-instrument-dlya-tryohstoronnego-sliyaniya-v-git","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2024-12-14T07:45:59+00:00","article:modified_time":"2024-12-14T07:45:59+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"120981","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 08:54:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2026-01-23 08:54:19","updated":"2026-01-23 08:54:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/120981","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=120981"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/120981\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=120981"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=120981"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=120981"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}