{"id":9950,"date":"2021-03-05T16:32:41","date_gmt":"2021-03-05T15:32:41","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=9950"},"modified":"2023-03-28T11:39:50","modified_gmt":"2023-03-28T09:39:50","slug":"jak-git-dziala-za-kulisami","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/jak-git-dziala-za-kulisami\/","title":{"rendered":"Jak Git dzia\u0142a za kulisami"},"content":{"rendered":"\n<p>Git jest narz\u0119dziem niezwykle pomocnym przy codziennej pracy w zespole. Chc\u0105c nauczy\u0107 si\u0119 jego obs\u0142ugi, si\u0119gamy po przer\u00f3\u017cne kursy. Cz\u0119sto przedstawiaj\u0105 one spos\u00f3b dzia\u0142ania Gita, stosuj\u0105c pewne uproszczenia i abstrakcje. Nie jest to z\u0142e, gdy\u017c znacznie u\u0142atwia nam przyswojenie podstawowej wiedzy. Ma jednak istotn\u0105 wad\u0119 \u2013 nieraz prowadzi do wyrobienia sobie b\u0142\u0119dnych za\u0142o\u017ce\u0144. Nie stanowi to problemu przy wykonywaniu prostych czynno\u015bci. Jednak podczas codziennej pracy w zespole, szybko okazuje si\u0119, \u017ce konieczne jest wyj\u015bcie poza ramy podstawowej wiedzy. Cz\u0119sto natrafiamy na problem, kt\u00f3ry wymaga od nas nieco wi\u0119kszego poj\u0119cia o regu\u0142ach rz\u0105dz\u0105cych Gitem. Czasami takie problemy zdarza si\u0119 nam spowodowa\u0107 samemu, wychodz\u0105c z b\u0142\u0119dnych za\u0142o\u017ce\u0144, kt\u00f3re wynikaj\u0105 z niepe\u0142nej wiedzy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Git \u2013 dlaczego warto spr\u00f3bowa\u0107?<\/h2>\n\n\n\n<p>Niezwykle przydatne jest zrozumienie, jak Git dzia\u0142a pod os\u0142on\u0105 tych wszystkich uproszcze\u0144. U\u0142atwia to zdecydowanie prac\u0119 w zespole i pozwala unikn\u0105\u0107 czyhaj\u0105cych na nas pu\u0142apek. Taka wiedza pozwala nam nabra\u0107 wi\u0119kszej pewno\u015bci siebie i swobody przy pos\u0142ugiwaniu si\u0119 Gitem. Umo\u017cliwia nam tworzenie przejrzystej historii podczas codziennej pracy przy projekcie.<\/p>\n\n\n\n<p>Git wybacza sporo b\u0142\u0119d\u00f3w, wi\u0119c \u017ceby bezpowrotnie utraci\u0107 jakie\u015b zmiany, trzeba si\u0119 naprawd\u0119 postara\u0107. Jednak po co przysparza\u0107 niepotrzebnie nerw\u00f3w sobie i zespo\u0142owi? Je\u015bli wiemy jakie mechanizmy zadzia\u0142aj\u0105, gdy wykonamy konkretne polecenie, to jeste\u015bmy w pe\u0142ni \u015bwiadomi efekt\u00f3w naszych dzia\u0142a\u0144. Mamy <strong>pewno\u015b\u0107, \u017ce nie wprowadzimy \u017cadnych komplikacji i osi\u0105gniemy zamierzony cel.<\/strong><\/p>\n\n\n\n<p>W tym artykule zajrzymy w zakamarki Gita, kt\u00f3re s\u0105 ukryte dla u\u017cytkownik\u00f3w, dop\u00f3ki ci nie wyka\u017c\u0105 zainteresowania nimi. Dzi\u0119ki temu dowiemy si\u0119, <strong>w jaki spos\u00f3b Git przechowuje informacje i jak nimi manipuluje<\/strong>.<\/p>\n\n\n\n<p>\u017beby zrozumie\u0107 tre\u015bci zawarte w dalszej cz\u0119\u015bci artyku\u0142u, potrzebna b\u0119dzie przynajmniej podstawowa znajomo\u015b\u0107 Gita, bo skupimy si\u0119 na bardziej zaawansowanych kwestiach.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Czym jest repozytorium?<\/h2>\n\n\n\n<p>Nie jest trudno domy\u015bli\u0107 si\u0119, sk\u0105d Git wie, \u017ce jaki\u015b folder na dysku jest repozytorium lokalnym. Znakiem rozpoznawczym jest podfolder o nazwie <strong>.git<\/strong>, kt\u00f3ry znajduje si\u0119 w ka\u017cdym repozytorium. Jego zadaniem jest przechowywanie wszystkich danych, kt\u00f3re powstaj\u0105 podczas pracy z Gitem. Znajduje si\u0119 tam m.&nbsp;in. historia projektu.<\/p>\n\n\n\n<p>Folder b\u0119d\u0105cy repozytorium lokalnym jest r\u00f3wnocze\u015bnie katalogiem roboczym. To znaczy, \u017ce zawiera pliki projektu, na kt\u00f3rych pracujemy i kt\u00f3re modyfikujemy. Po\u015br\u00f3d nich znajduje si\u0119 wcze\u015bniej wspomniany folder .git. Mo\u017cna bez przeszk\u00f3d zajrze\u0107 do jego wn\u0119trza (co zreszt\u0105 zrobimy w jednym z kolejnych akapit\u00f3w). Pozwoli nam to lepiej zrozumie\u0107 specyfik\u0119 dzia\u0142ania Gita. Nie ma jednak potrzeby wykonywa\u0107 manualnie jakichkolwiek operacji na tym specjalnym folderze. S\u0142u\u017cy on Gitowi i to Git powinien zajmowa\u0107 si\u0119 jego zarz\u0105dzaniem. Jako u\u017cytkownicy dostajemy od Gita <strong>szerok\u0105 gam\u0119 narz\u0119dzi<\/strong>, kt\u00f3re pokrywaj\u0105 wi\u0119kszo\u015b\u0107 naszych potrzeb. Dzi\u0119ki temu nie musimy ingerowa\u0107 w wewn\u0119trzne struktury Gita.<\/p>\n\n\n\n<p>Istotn\u0105 informacj\u0105 jest dla nas fakt, \u017ce Git przechowuje histori\u0119 zmian plik\u00f3w projektu za pomoc\u0105 ich kopii. Oznacza to, \u017ce kiedy dokonamy jakich\u015b zmian w pliku i utworzymy commit, Git automatycznie utworzy sobie kopi\u0119 tego pliku. B\u0119dzie ona dok\u0142adnym odwzorowaniem pliku z katalogu roboczego. Nast\u0119pnie zostanie umieszczona w folderze .git i pozostanie w nim ju\u017c niezmieniona.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wersje plik\u00f3w<\/h3>\n\n\n\n<p>W repozytorium istnieje wiele wersji jednego pliku, kt\u00f3re nieco si\u0119 od siebie r\u00f3\u017cni\u0105. S\u0105 to r\u00f3\u017cnice wynikaj\u0105ce z wykonywanych zmian podczas pracy nad projektem. Jedn\u0105 wersj\u0119 pliku znajdziemy przede wszystkim w katalogu roboczym. To j\u0105 modyfikujemy, pracuj\u0105c nad projektem. Je\u017celi dodamy plik do przechowalni (ang. <em>staging area<\/em>) za pomoc\u0105 <code>git&nbsp;add<\/code>, to Git utworzy jego kopi\u0119. Umie\u015bci j\u0105 w folderze .git, kt\u00f3ry b\u0119dzie zawiera\u0142 pozosta\u0142e wersje pliku. Innymi s\u0142owy, <strong>Git posiada osobn\u0105 wersj\u0119 pliku dla ka\u017cdego commita, w kt\u00f3rym ten plik zosta\u0142 zmodyfikowany.<\/strong> Dzi\u0119ki temu prze\u0142\u0105czanie si\u0119 mi\u0119dzy commitami jest bardzo wydajne.<\/p>\n\n\n\n<p>W innych systemach kontroli wersji, gdy chcemy cofn\u0105\u0107 si\u0119 do odleg\u0142ego commita z przesz\u0142o\u015bci, konieczne jest przej\u015bcie po wszystkich nowszych od niego commitach. Po drodze odczytywane s\u0105 kolejne zmiany, jakie by\u0142y wykonane na pliku, a\u017c do momentu, gdy zostanie przywr\u00f3cona po\u017c\u0105dana wersja. W Gicie ten proces jest znacznie prostszy, gdy\u017c posiada on zapisan\u0105 wersj\u0119 pliku dla interesuj\u0105cego nas commita. Prze\u0142\u0105czaj\u0105c si\u0119 na ten commit, wystarczy w folderze .git znale\u017a\u0107 plik z t\u0105 wersj\u0105. Jest to znacznie szybsze ni\u017c w innych systemach kontroli wersji.<\/p>\n\n\n\n<p>Do pracy na lokalnym repozytorium niepotrzebne jest aktywne po\u0142\u0105czenie internetowe z repozytorium zdalnym. Jest ono wymagane tylko podczas pobierania i wysy\u0142ania zmian. Ponadto, nie jest nawet konieczne, \u017ceby repozytorium zdalne istnia\u0142o, mo\u017cliwa jest praca jedynie na repozytorium lokalnym. Nie ma \u017cadnych przeciwwskaza\u0144, \u017ceby utworzy\u0107 repozytorium zdalne p\u00f3\u017aniej i wtedy po\u0142\u0105czy\u0107 je z lokalnym, a co wi\u0119cej, mo\u017cliwe jest te\u017c po\u0142\u0105czenie z wi\u0119cej ni\u017c jednym repozytorium zdalnym. Taki <a href=\"https:\/\/git-scm.com\/book\/pl\/v2\/Distributed-Git-Distributed-Workflows\" rel=\"nofollow\" >mechanizm<\/a> wykorzystuj\u0105 m.in. Github czy GitLab by udost\u0119pni\u0107 funkcj\u0119 fork.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Czym jest commit?<\/h2>\n\n\n\n<p>Git przechowuje commity w postaci plik\u00f3w o niewielkich rozmiarach. Ka\u017cdy commit zawiera ustalony z g\u00f3ry zestaw informacji. S\u0105 to:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>opis,<\/li><li>autor i tw\u00f3rca commita,<\/li><li>data utworzenia commita i data jego modyfikacji,<\/li><li>skr\u00f3t commita rodzica (albo rodzic\u00f3w),<\/li><li>skr\u00f3t pliku <em>tree.<\/em><\/li><\/ul>\n\n\n\n<p>Ka\u017cdy commit posiada r\u00f3wnie\u017c unikalny identyfikator, kt\u00f3ry nazywany jest te\u017c skr\u00f3tem. Jest to ci\u0105g 40 liczb szesnastkowych. Skr\u00f3t wyliczany jest za pomoc\u0105 algorytmu SHA-1 na podstawie zawarto\u015bci, jaka znajduje si\u0119 w pliku commita. Ka\u017cdy skr\u00f3t jest inny, poniewa\u017c ka\u017cdy commit b\u0119dzie zawiera\u0142 nieco inne dane. Skr\u00f3t przypisany commitowi nie b\u0119dzie si\u0119 te\u017c nigdy zmienia\u0142, gdy\u017c commity s\u0105 niemodyfikowalne \u2013 nie jeste\u015bmy w stanie \u017cadnym poleceniem Gita zmieni\u0107 istniej\u0105cego commita.<\/p>\n\n\n\n<p>Niekt\u00f3re polecenia, jak np. <code>git&nbsp;commit&nbsp;--amend<\/code>, daj\u0105 nam poczucie, \u017ce wykonali\u015bmy zmiany w commicie, ale tak naprawd\u0119 tworz\u0105 one nowe commity na wz\u00f3r ju\u017c istniej\u0105cych. W efekcie wykonania tych komend w repozytorium b\u0119d\u0105 znajdowa\u0107 si\u0119 dwa commity. Ten pierwszy nie zostanie usuni\u0119ty, ale najcz\u0119\u015bciej utracimy do niego odniesienie. Nadal b\u0119dzie istnia\u0142 w repozytorium, ale dost\u0119p do niego b\u0119dzie utrudniony, co jest do\u015b\u0107 schludnym rozwi\u0105zaniem.<\/p>\n\n\n\n<p>Otrzymujemy nowy commit w miejsce starego, kt\u00f3ry jednak nie przepada bezpowrotnie i zawsze mo\u017cemy go odzyska\u0107, je\u015bli zajdzie taka potrzeba. Efekt wykonania polecenia <code>git&nbsp;commit&nbsp;--amend <\/code>wida\u0107 na rysunku poni\u017cej. Pokazuje on, \u017ce zosta\u0142a utworzona kopia commita C2, na kt\u00f3rym wykonano operacj\u0119 <em>amend<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/01_amend-git.png\"><img decoding=\"async\" width=\"300\" height=\"90\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/01_amend-git-300x90.png\" alt=\"Efekt wykonania polecenia git\u00a0commit\u00a0--amend\" class=\"wp-image-10026\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/01_amend-git-300x90.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/01_amend-git.png 840w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Rodzaje commit\u00f3w<\/h3>\n\n\n\n<p>Wi\u0119kszo\u015b\u0107 commit\u00f3w posiada odniesienie do swoich <strong>commit\u00f3w-rodzic\u00f3w<\/strong>, bo w ten spos\u00f3b Git buduje histori\u0119 \u2013 jako ci\u0105g nast\u0119puj\u0105cych po sobie commit\u00f3w. Istniej\u0105 tak\u017ce <strong>commity bez rodzica<\/strong>. Najcz\u0119stszym takim przypadkiem jest po prostu pierwszy commit w historii repozytorium, kt\u00f3ry nie ma swojego poprzednika. Mo\u017cliwe jest r\u00f3wnie\u017c samodzielne utworzenie takiego commita, kt\u00f3ry <a href=\"https:\/\/czterytygodnie.pl\/git-orphan-branch\/\" rel=\"nofollow\" >nie posiada rodzica<\/a>.<\/p>\n\n\n\n<p>Cz\u0119sto natomiast commity miewaj\u0105 dwoje rodzic\u00f3w. S\u0105 to <strong>merge-commity<\/strong>, powsta\u0142e w wyniku \u0142\u0105czenia (ang. <em>merging<\/em>) dw\u00f3ch ga\u0142\u0119zi. W\u0142a\u015bciwie od standardowych commit\u00f3w r\u00f3\u017cni\u0105 si\u0119 jedynie posiadaniem drugiego rodzica. Poza tym plik merge-commita posiada dok\u0142adnie taki sam zestaw informacji jak zwyk\u0142y commit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wykorzystanie plik\u00f3w tree<\/h3>\n\n\n\n<p>Wiemy ju\u017c, jakie informacje wchodz\u0105 w sk\u0142ad pliku commita. Na tej podstawie mo\u017cemy stwierdzi\u0107, \u017ce jego rozmiar nie zale\u017cy od ilo\u015bci zmian w projekcie jakie zawiera. W takim razie sk\u0105d Git w\u0142a\u015bciwie wie, jakie zmiany zosta\u0142y wykonane w ramach commita?<\/p>\n\n\n\n<p>W tym celu wykorzystuje pliki <em>tree<\/em>. Ka\u017cdy commit posiada odniesienie do jednego z takich plik\u00f3w. Odniesieniem jest oczywi\u015bcie skr\u00f3t <strong>SHA-1<\/strong>, wyliczony na podstawie zawarto\u015bci pliku <em>tree<\/em>, tak jak w przypadku commita. Plik <em>tree<\/em> zawiera list\u0119 plik\u00f3w i podfolder\u00f3w, jakie znajduj\u0105 si\u0119 w konkretnym folderze projektu. Commit wskazuje na plik <em>tree, <\/em>kt\u00f3ry odpowiada zawarto\u015bci katalogu roboczego. Plik <em>tree<\/em> przyporz\u0105dkowuje ka\u017cdemu podfolderowi inny plik <em>tree<\/em>, a ka\u017cdemu plikowi plik <em>blob<\/em>.<\/p>\n\n\n\n<p>Pliki <em>blob<\/em> to kopie plik\u00f3w projektowych zrobione w momencie tworzenia commita. Pliki <em>tree<\/em> tworz\u0105 drzewiast\u0105 struktur\u0119, odpowiadaj\u0105c\u0105 strukturze katalogu roboczego. Obrazuje to poni\u017cszy rysunek &#8211; po lewej stronie pokazana jest struktura folder\u00f3w, a po prawej powi\u0105zania w plikach <em>tree<\/em> i <em>blob.<\/em><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/02_tree-blob-diagram-git-e1611576005171.png\"><img decoding=\"async\" width=\"840\" height=\"207\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/02_tree-blob-diagram-git-e1611576005171.png\" alt=\"Struktura folder\u00f3w oraz powi\u0105zania w plikach tree i blob\" class=\"wp-image-10029\"\/><\/a><\/figure><\/div>\n\n\n\n<p>Podczas tworzeniu commita, Git dodaje nowe pliki <em>tree <\/em>i <em>blob <\/em>tylko dla plik\u00f3w projektowych, kt\u00f3re uleg\u0142y zmianie. Dla pozosta\u0142ych plik\u00f3w wykorzystywane s\u0105 istniej\u0105ce ju\u017c pliki <em>tree<\/em> i <em>blob<\/em>. Git przechowuje je w postaci skompresowanej. Je\u017celi znamy skr\u00f3t interesuj\u0105cego nas pliku, to mo\u017cemy z \u0142atwo\u015bci\u0105 odczyta\u0107 jego zawarto\u015b\u0107 za pomoc\u0105 komendy:<br><code>git cat-file -p SKROT<\/code><\/p>\n\n\n\n<p>Zach\u0119cam do przetestowania jej na dowolnym repozytorium Gita. \u017beby znale\u017a\u0107 skr\u00f3t ostatniego commita, wystarczy u\u017cy\u0107 polecenia&nbsp;<code>git log -1<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Czym jest ga\u0142\u0105\u017a?<\/h2>\n\n\n\n<p>Ga\u0142\u0119zie w Gicie s\u0105 jednym z element\u00f3w, kt\u00f3re najbardziej odr\u00f3\u017cniaj\u0105 go od innych system\u00f3w kontroli wersji. Cz\u0119sto mylnie uwa\u017ca si\u0119, \u017ce ga\u0142\u0105\u017a jest zbiorem commit\u00f3w, co mo\u017cna by zobrazowa\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/03_branch-git.png\"><img decoding=\"async\" width=\"300\" height=\"290\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/03_branch-git-300x290.png\" alt=\"\" class=\"wp-image-10030\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/03_branch-git-300x290.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/03_branch-git.png 545w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Nie jest to jednak zgodne z prawd\u0105. Ga\u0142\u0105\u017a w Gicie jest tak naprawd\u0119 tylko wska\u017anikiem na pojedynczy commit. Git przechowuje ga\u0142\u0119zie w postaci plik\u00f3w, co nie powinno ju\u017c by\u0107 zaskoczeniem. W dodatku plik\u00f3w o niewielkich rozmiarach, bo zawieraj\u0105cych jedynie 40 znak\u00f3w. Brzmi znajomo, prawda? Jest to oczywi\u015bcie skr\u00f3t SHA-1 commitu. Czyni to prac\u0119 z ga\u0142\u0119ziami naprawd\u0119 wydajn\u0105. Podczas tworzenia nowej ga\u0142\u0119zi nie s\u0105 wykonywane \u017cadne wymagaj\u0105ce operacje ani nie nast\u0119puje kopiowanie jakichkolwiek plik\u00f3w, jak dzieje si\u0119 w innych systemach kontroli wersji. Git ogranicza si\u0119 do utworzenia jednego niewielkiego pliku. Prze\u0142\u0105czanie si\u0119 mi\u0119dzy ga\u0142\u0119ziami te\u017c jest szybk\u0105 operacj\u0105. Ten proces opisa\u0142em szerzej w paragrafie \u201eCo dzieje si\u0119 za kulisami\u201d.<\/p>\n\n\n\n<p>Ga\u0142\u0105\u017a, na kt\u00f3rej pracujemy, pod\u0105\u017ca za kolejnymi commitami. To znaczy, \u017ce po utworzeniu commita, Git aktualizuje wska\u017anik ga\u0142\u0119zi, tak by wskazywa\u0142 na nowy commit. By ten mechanizm by\u0142 mo\u017cliwy, istnieje specjalny wska\u017anik HEAD, kt\u00f3ry\u2026 r\u00f3wnie\u017c jest plikiem. HEAD przechowuje informacj\u0119 o tym, na jakiej obecnie ga\u0142\u0119zi si\u0119 znajdujemy. Czyli HEAD wskazuje na ga\u0142\u0105\u017a, kt\u00f3ra wskazuje na commit. Commit z kolei wskazuje na plik <em>tree<\/em>, a plik <em>tree<\/em> zawiera odniesienia do plik\u00f3w <em>blob<\/em>. Pliki <em>blob <\/em>to konkretne wersje plik\u00f3w projektowych.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prze\u0142\u0105czanie na wskazany commit<\/h3>\n\n\n\n<p>Chocia\u017c najcz\u0119\u015bciej prze\u0142\u0105czamy si\u0119 na konkretn\u0105 ga\u0142\u0105\u017a, mo\u017cliwe jest te\u017c prze\u0142\u0105czenie si\u0119 na wskazany commit. Wystarczy poda\u0107 skr\u00f3t commita, u\u017cywaj\u0105c polecenia git&nbsp;checkout&nbsp;SKROT. Wtedy plik HEAD b\u0119dzie zawiera\u0142 skr\u00f3t tego commita, a nie nazw\u0119 ga\u0142\u0119zi. Taki stan nosi nazw\u0119 <em>detached HEAD<\/em>. Git ostrzega nas jednak przed wykonywaniem tej czynno\u015bci, gdy\u017c \u0142atwo mo\u017cna utraci\u0107 wszystkie odniesienia do nowych commit\u00f3w. W tym stanie, HEAD wskazuje na konkretny commit.<\/p>\n\n\n\n<p>Je\u017celi utworzymy nowy commit, to HEAD zostanie zaktualizowany i b\u0119dzie na niego wskazywa\u0107. B\u0119dzie to jednak jedyne odniesienie do nowego commita, gdy\u017c \u017cadna ga\u0142\u0105\u017a nie b\u0119dzie na niego wskazywa\u0107. Po prze\u0142\u0105czeniu si\u0119 na inn\u0105 ga\u0142\u0105\u017a, HEAD nie b\u0119dzie ju\u017c wskazywa\u0142 nowego commita i nie b\u0119dzie istnia\u0142o do niego ju\u017c \u017cadne inne odniesienie. Git go nie usunie (a przynajmniej nie od razu), wi\u0119c ci\u0105gle mo\u017cliwe b\u0119dzie jego u\u017cycie. Niemniej, dost\u0119p do niego b\u0119dzie utrudniony.<\/p>\n\n\n\n<p>Dobrym sposobem na zrozumienie mechanizm\u00f3w pracy z ga\u0142\u0119ziami w Gicie jest wizualizowanie sobie ca\u0142ego procesu. <a href=\"https:\/\/learngitbranching.js.org\/\" rel=\"nofollow\" >Dost\u0119pny jest interaktywny kurs<\/a>, kt\u00f3ry wy\u015bwietla struktur\u0119 ga\u0142\u0119zi w postaci animowanego grafu. Wykonuj\u0105c kolejne operacje, widzimy na bie\u017c\u0105co jakie zmiany zachodz\u0105 w ga\u0142\u0119ziach. Zach\u0119cam do zapoznania si\u0119 chocia\u017c z pocz\u0105tkowymi lekcjami tego kursu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Jak zbudowany jest folder .git?<\/h2>\n\n\n\n<p>Folder .git jest baz\u0105 danych Gita. Zawiera wszystkie informacje potrzebne do pracy z repozytorium. Git przechowuje je w formie plik\u00f3w. Cz\u0119\u015b\u0107 z nich ma posta\u0107 skompresowan\u0105, by oszcz\u0119dza\u0107 miejsce na dysku.<\/p>\n\n\n\n<p>Folder .git zawiera nast\u0119puj\u0105ce elementy:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>hooks \u2013 folder zawieraj\u0105cy skrypty, uruchamiane automatycznie po wykonaniu okre\u015blonych akcji,<\/li><li>info \u2013 folder zawieraj\u0105cy plik <em>exclude<\/em> z list\u0105 ignorowanych plik\u00f3w,<\/li><li>logs \u2013 folder zawieraj\u0105cy histori\u0119 operacji na ga\u0142\u0119ziach,<\/li><li>objects \u2013 folder zawieraj\u0105cy pliki <em>tree <\/em>i <em>blob,<\/em><\/li><li>refs \u2013 folder zawieraj\u0105cy pliki ga\u0142\u0119zi i tag\u00f3w,<\/li><li>config \u2013 plik z lokaln\u0105 konfiguracj\u0105 repozytorium,<\/li><li>HEAD \u2013 plik z nazw\u0105 bie\u017c\u0105cej ga\u0142\u0119zi,<\/li><li>index \u2013 plik binarny zawieraj\u0105cy list\u0119 plik\u00f3w w przechowalni.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Folder objects<\/h3>\n\n\n\n<p>Z naszego punktu widzenia interesuj\u0105cy jest folder <em>objects<\/em>, gdzie Git przechowuje pliki <em>tree <\/em>i <em>blob<\/em>. Ciekawy jest zw\u0142aszcza spos\u00f3b w jaki Git to robi. Gdy zajrzymy do folderu <em>objects, <\/em>znajdziemy w nim w wi\u0119kszo\u015bci podfoldery, kt\u00f3rych nazwa sk\u0142ada si\u0119 z dok\u0142adnie dw\u00f3ch znak\u00f3w \u2013 liczb szesnastkowych. S\u0105 to dwa pierwsze znaki skr\u00f3t\u00f3w plik\u00f3w <em>tree <\/em>i <em>blob, <\/em>kt\u00f3re znajduj\u0105 si\u0119 w tych folderach. Jest to rozwi\u0105zanie poprawiaj\u0105ce wydajno\u015b\u0107 przy wyszukiwaniu konkretnych plik\u00f3w po ich skr\u00f3tach, gdy\u017c ogranicza liczb\u0119 plik\u00f3w jak\u0105 trzeba sprawdzi\u0107, by znale\u017a\u0107 ten w\u0142a\u015bciwy.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Podfolder pack i plik index<\/h3>\n\n\n\n<p>W folderze <em>objects <\/em>znajduje si\u0119 r\u00f3wnie\u017c podfolder o nazwie <em>pack<\/em>. Przechowuje on pliki binarne zwane <em>packfile<\/em>. \u017beby wyt\u0142umaczy\u0107 jaka jest ich rola, trzeba najpierw wyja\u015bni\u0107 w jaki spos\u00f3b Git zapisuje zmiany w projekcie.<\/p>\n\n\n\n<p>Jak wspomina\u0142em wcze\u015bniej, Git u\u017cywa w tym celu plik\u00f3w <em>blob. <\/em>S\u0105 to po prostu kopie plik\u00f3w z katalogu roboczego. Je\u017celi commit zawiera zmian\u0119 tylko w jednej linijce pliku, to Git i tak wykona kopi\u0119 ca\u0142ego tego pliku. Mo\u017ce sta\u0107 si\u0119 to problematyczne, gdy plik jest du\u017cych rozmiar\u00f3w i wprowadzane s\u0105 do niego cz\u0119sto zmiany. Wtedy kopie takiego pliku b\u0119d\u0105 niepotrzebnie zabiera\u0142y miejsce na dysku. \u017beby temu zapobiec, Git posiada mechanizm zbieraj\u0105cy takie zmiany w jednym pliku. Tym plikiem jest w\u0142a\u015bnie <em>packfile<\/em>. Przechowuje on zmiany w postaci r\u00f3\u017cnic. To znaczy, \u017ce zapisywane s\u0105 tylko te fragmenty, kt\u00f3re uleg\u0142y zmianie, a nie ca\u0142e kopie pliku. Pliki <em>blob <\/em>odpowiadaj\u0105ce tym zmianom mog\u0105 zosta\u0107 usuni\u0119te, co zwalnia miejsce na dysku.<\/p>\n\n\n\n<p>Warto jeszcze wspomnie\u0107 o pliku <em>index, <\/em>kt\u00f3ry znajduje si\u0119 bezpo\u015brednio w folderze .git. Git zapisuje zawarto\u015b\u0107 plik\u00f3w w momencie dodawania ich do <em>staging area.<\/em> W tym celu korzysta ze znanych ju\u017c plik\u00f3w <em>blob. <\/em>Zatem ka\u017cdy plik dodany do <em>staging area <\/em>ma sw\u00f3j plik <em>blob<\/em>. Plik <em>index <\/em>zawiera list\u0119 plik\u00f3w w <em>staging area<\/em> wraz z odpowiadaj\u0105cym im plikami <em>blob<\/em>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Co dzieje si\u0119 za kulisami?<\/h2>\n\n\n\n<p>Warto podsumowa\u0107 zebrane do tej pory informacje. Prze\u015bled\u017amy zmiany, jakie zachodz\u0105 w repozytorium lokalnym, podczas codziennej pracy z Gitem. B\u0119dzie to dobry spos\u00f3b na uporz\u0105dkowanie wiedzy.<\/p>\n\n\n\n<p>Zacznijmy od pustego folderu przeznaczonego na repozytorium projektu. Pierwszym krokiem jest sklonowanie repozytorium zdalnego za pomoc\u0105 komendy <code>git clone<\/code>. W efekcie w folderze lokalnym jest tworzony folder .git ze znan\u0105 ju\u017c nam zawarto\u015bci\u0105. W pliku .git\/config zostaje zapisane odniesienie do zdalnego repozytorium z domy\u015bln\u0105 nazw\u0105 <em>origin.<\/em> Dzi\u0119ki temu Git wie, jak si\u0119 z nim po\u0142\u0105czy\u0107. Plik .git\/refs\/remotes\/origin\/HEAD wskazuje domy\u015bln\u0105 ga\u0142\u0105\u017a w repozytorium zdalnym. Najcz\u0119\u015bciej jest to <em>master. <\/em>Git sprawdza jaki commit jest wskazywany przez t\u0119 ga\u0142\u0105\u017a, a nast\u0119pnie znajduje plik <em>tree<\/em>, wskazywany przez ten commit. Nast\u0119pnie umieszcza pliki i foldery w katalogu roboczym zgodnie ze wskazaniami pliku <em>tree<\/em>. To znaczy, \u017ce katalog roboczy zawiera pliki w takiej wersji, w jakiej by\u0142y podczas tworzenia commita.<\/p>\n\n\n\n<p>Kiedy tworzymy now\u0105 ga\u0142\u0105\u017a poleceniem <code>git&nbsp;branch&nbsp;nowa_galaz<\/code>, Git automatycznie tworzy nowy plik <em>.git\/refs\/heads\/nowa_galaz<\/em> zawieraj\u0105cy skr\u00f3t bie\u017c\u0105cego commita. Po prze\u0142\u0105czeniu si\u0119 na t\u0119 ga\u0142\u0105\u017a za pomoc\u0105 komendy <code>git&nbsp;checkout&nbsp;nowa_galaz<\/code>, Git aktualizuje plik HEAD. B\u0119dzie wskazywa\u0142 na now\u0105 ga\u0142\u0105\u017a, na kt\u00f3r\u0105 si\u0119 prze\u0142\u0105czyli\u015bmy.<\/p>\n\n\n\n<p>Po wprowadzeniu zmian do projektu, dodajemy wybrane pliki do <em>staging area<\/em> za pomoc\u0105 polecenia <code>git&nbsp;add.<\/code> Dla wszystkich dodanych w ten spos\u00f3b plik\u00f3w, Git tworzy ich kopie w postaci plik\u00f3w <em>blob.<\/em> Nast\u0119pnie zapisuje do pliku <em>index<\/em> list\u0119 dodanych plik\u00f3w i skr\u00f3ty odpowiadaj\u0105cych im plik\u00f3w <em>blob.<\/em> Tylko zmiany, kt\u00f3re znajd\u0105 si\u0119 w pliku <em>index <\/em>zostan\u0105 uwzgl\u0119dnione w commicie.<\/p>\n\n\n\n<p>Wykonuj\u0105c commit, korzystamy z polecenia <code>git commit<\/code>.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Git tworzy plik commit oraz pliki <em>tree<\/em> odpowiadaj\u0105ce \u015bcie\u017ckom plik\u00f3w z pliku <em>index<\/em>.<\/li><li>Pliki <em>tree <\/em>wskazuj\u0105 na pliki <em>blob<\/em>, tworz\u0105c drzewiast\u0105 struktur\u0119 odpowiadaj\u0105c\u0105 katalogowi roboczemu. Git w tym momencie nie tworzy nowych plik\u00f3w <em>blob <\/em>tylko wykorzystuje ju\u017c istniej\u0105ce, powsta\u0142e podczas dodawania plik\u00f3w do <em>staging area.<\/em><\/li><li>Nast\u0119pnie Git sprawdza na jak\u0105 ga\u0142\u0105\u017a wskazuje plik HEAD i aktualizuje j\u0105, by wskazywa\u0142a na nowo utworzony commit. Oznacza to, \u017ce modyfikowany jest plik <em>.git\/refs\/heads\/nowa_galaz<\/em>.<\/li><li>Na koniec wcielamy zmiany z naszej ga\u0142\u0119zi do ga\u0142\u0119zi <em>master<\/em>. W tym celu prze\u0142\u0105czmy si\u0119 na niego (zmienia si\u0119 plik HEAD). Wykorzystajmy komend\u0119 <code>git&nbsp;merge&nbsp;nowa_galaz<\/code>. Cz\u0119sto okazuje si\u0119, \u017ce w mi\u0119dzyczasie powsta\u0142y na nim ju\u017c jakie\u015b commity, przez co nie jest mo\u017cliwe u\u017cycie mechanizmu <em>fast-forward<\/em>. Oznacza to, \u017ce Git musi utworzy\u0107 nowy <em>merge-commit<\/em>. Powstaj\u0105 odpowiednie pliki <em>tree<\/em>, natomiast wszystkie pliki <em>blob<\/em> ju\u017c istniej\u0105.<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Jaka jest r\u00f3\u017cnica mi\u0119dzy&nbsp;<em>merge<\/em>&nbsp;a&nbsp;<em>rebase?<\/em><\/h2>\n\n\n\n<p>To pytanie cz\u0119sto pada <strong>na rozmowach kwalifikacyjnych<\/strong>, wi\u0119c warto zna\u0107 na nie odpowied\u017a. Przyjrzyjmy si\u0119, jakie mechanizmy zadzia\u0142aj\u0105, podczas wykonywania tych polece\u0144.<\/p>\n\n\n\n<p>Zadaniem obu, jest po\u0142\u0105czenie zmian z dw\u00f3ch ga\u0142\u0119zi. Robi\u0105 to jednak w inny spos\u00f3b.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Komenda rebase<\/h3>\n\n\n\n<p>Dzia\u0142anie komendy <strong><em>rebase<\/em> <\/strong>polega na skopiowaniu commit\u00f3w. Wskazujemy ga\u0142\u0105\u017a docelow\u0105:<br><code>git&nbsp;rebase&nbsp;galaz_docelowa<\/code><\/p>\n\n\n\n<p>Commity wybierane s\u0105 z ga\u0142\u0119zi, na kt\u00f3rej si\u0119 znajdujemy. Wybierane s\u0105 jedynie te commity, kt\u00f3rych nie ma na ga\u0142\u0119zi docelowej. M\u00f3wi\u0105c dok\u0142adniej, s\u0105 to commity, do kt\u00f3rych nie mo\u017cna dotrze\u0107, poruszaj\u0105c si\u0119 po kolejnych rodzicach commita wskazywanego przez ga\u0142\u0105\u017a docelow\u0105. Opr\u00f3cz kopiowania commit\u00f3w, wa\u017cn\u0105 operacj\u0105 wykonywan\u0105 podczas <em>rebase,<\/em> jest przeniesienie wska\u017anika bie\u017c\u0105cej ga\u0142\u0119zi na najnowszy skopiowany commit. Rysunki poni\u017cej przedstawiaj\u0105 sytuacj\u0119 przed i po wykonaniu polecenia:<br><code>git rebase docelowa<\/code><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/04_rebase_before-git.png\"><img decoding=\"async\" width=\"658\" height=\"326\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/04_rebase_before-git.png\" alt=\"\" class=\"wp-image-10031\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/04_rebase_before-git.png 658w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/04_rebase_before-git-300x149.png 300w\" sizes=\"(max-width: 658px) 100vw, 658px\" \/><\/a><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/03\/05_rebase-after-git.png\"><img decoding=\"async\" width=\"1024\" height=\"319\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/03\/05_rebase-after-git-1024x319.png\" alt=\"\" class=\"wp-image-20605\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/03\/05_rebase-after-git-1024x319.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/03\/05_rebase-after-git-300x93.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/03\/05_rebase-after-git-768x239.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/03\/05_rebase-after-git.png 1038w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>List\u0119 commit\u00f3w, kt\u00f3re zostan\u0105 skopiowane mo\u017cemy znale\u017a\u0107 za pomoc\u0105 polecenia <code>git log docelowa..zrodlowa<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Komenda merge<\/h3>\n\n\n\n<p>Komenda <strong><em>merge<\/em> <\/strong>tak\u017ce s\u0142u\u017cy do \u0142\u0105czenia zmian z dw\u00f3ch ga\u0142\u0119zi, ale w przeciwie\u0144stwie do <em>rebase,<\/em> nie kopiuje \u017cadnych commit\u00f3w. <em>Merge <\/em>mo\u017ce zosta\u0107 wykonany na dwa sposoby.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Pierwszy to <em>fast-forward merge.<\/em> Git wykonuje go automatycznie, ale tylko je\u015bli jedna z ga\u0142\u0119zi zawiera ju\u017c wszystkie commity z drugiej. Wtedy jedyn\u0105 wykonan\u0105 operacj\u0105 jest aktualizacja wska\u017anika bie\u017c\u0105cej ga\u0142\u0119zi, tak by wskazywa\u0142 na ten sam commit, co ga\u0142\u0105\u017a docelowa. Poni\u017cej wida\u0107 sytuacj\u0119 przed i po wykonaniu <em>merge <\/em>z u\u017cyciem <em>fast-forward<\/em>:<\/li><\/ul>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/06_merge-before-git.png\"><img decoding=\"async\" width=\"300\" height=\"63\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/06_merge-before-git-300x63.png\" alt=\"\" class=\"wp-image-10035\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/06_merge-before-git-300x63.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/06_merge-before-git.png 863w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/07_merge-after-git.png\"><img decoding=\"async\" width=\"300\" height=\"45\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/07_merge-after-git-300x45.png\" alt=\"\" class=\"wp-image-10036\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/07_merge-after-git-300x45.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/07_merge-after-git.png 862w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/figure><\/div>\n\n\n\n<ul class=\"wp-block-list\"><li>Drugi spos\u00f3b, w jaki mo\u017ce zadzia\u0142a\u0107 <em>merge<\/em>, polega na utworzeniu nowego commitu. Jest to <em>merge-commit<\/em>, kt\u00f3ry posiada dwoje rodzic\u00f3w. S\u0105 nimi commity wskazywane przez \u0142\u0105czone ze sob\u0105 ga\u0142\u0119zie. Ten spos\u00f3b jest stosowany przez Gita, je\u015bli obie ga\u0142\u0119zie zawieraj\u0105 r\u00f3\u017cne commity. Poni\u017cej wida\u0107 sytuacj\u0119 przed i po wykonaniu <em>merge <\/em>z utworzeniem <em>merge-commita.<\/em><\/li><\/ul>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/08_merge-commit-before-git.png\"><img decoding=\"async\" width=\"300\" height=\"94\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/08_merge-commit-before-git-300x94.png\" alt=\"\" class=\"wp-image-10037\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/08_merge-commit-before-git-300x94.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/08_merge-commit-before-git.png 778w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/09_merge-commit-after-git.png\"><img decoding=\"async\" width=\"300\" height=\"82\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/09_merge-commit-after-git-300x82.png\" alt=\"\" class=\"wp-image-10038\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/09_merge-commit-after-git-300x82.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/09_merge-commit-after-git.png 887w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Jaka jest r\u00f3\u017cnica mi\u0119dzy <i>fetch <\/i>a&nbsp;<em>pull?<\/em><\/h2>\n\n\n\n<p>W repozytorium lokalnym znajduj\u0105 si\u0119 specjalne ga\u0142\u0119zie zdalne, kt\u00f3rych zadaniem jest odzwierciedlanie zmian w repozytorium zdalnym. To znaczy, \u017ce b\u0119d\u0105 zawiera\u0107 te same commity, co ga\u0142\u0119zie w repozytorium zdalnym, kt\u00f3re \u015bledz\u0105. Kiedy pobieramy commity z repozytorium zdalnego, trafiaj\u0105 one do odpowiednich ga\u0142\u0119zi zdalnych. Nazwy tych ga\u0142\u0119zi zawieraj\u0105 przedrostek z nazw\u0105 repozytorium zdalnego, np. <em>origin\/master. Origin <\/em>to domy\u015blna nazwa repozytorium zdalnego, a <em>master<\/em> nazwa \u015bledzonej ga\u0142\u0119zi w tym repozytorium.<\/p>\n\n\n\n<p>Ga\u0142\u0105\u017a lokalna i jej zdalny odpowiednik nie musz\u0105 zawsze wskazywa\u0107 tego samego commitu. Je\u017celi b\u0119d\u0105c na ga\u0142\u0119zi <em>master<\/em>, utworzyli\u015bmy nowy commit, a nie wys\u0142ali\u015bmy go jeszcze do repozytorium zdalnego, to <em>origin\/master<\/em> nie b\u0119dzie wskazywa\u0142 tego commitu. Mo\u017ce te\u017c zdarzy\u0107 si\u0119 odwrotna sytuacja, gdy pobierzemy najnowsze zmiany z repozytorium zdalnego i znajd\u0105 si\u0119 one tylko na ga\u0142\u0119zi <em>origin\/master<\/em>, ale nie b\u0119dzie ich jeszcze na ga\u0142\u0119zi <em>master<\/em>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Git fetch<\/h3>\n\n\n\n<p>Taki efekt osi\u0105gniemy przy pomocy komendy <code>git fetch<\/code>. Pobiera ona commity z repozytorium zdalnego i umieszcza je w odpowiednich ga\u0142\u0119ziach zdalnych. Sytuacj\u0119 po wykonaniu <code>git fetch<\/code> przedstawia poni\u017cszy rysunek:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/10_fetch-git.png\"><img decoding=\"async\" width=\"622\" height=\"460\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/10_fetch-git.png\" alt=\"\" class=\"wp-image-10039\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/10_fetch-git.png 622w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/10_fetch-git-300x222.png 300w\" sizes=\"(max-width: 622px) 100vw, 622px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Zazwyczaj chcemy pobrane commity umie\u015bci\u0107 r\u00f3wnie\u017c w ga\u0142\u0119ziach lokalnych, aby odzwierciedli\u0107 je w katalogu roboczym. Wystarczy wykona\u0107 <em>merge<\/em> zdalnej ga\u0142\u0119zi do jej lokalnego odpowiednika:<br><code>git checkout master<\/code><br><code>git merge origin\/master<\/code><\/p>\n\n\n\n<p>Po tej operacji, lokalna ga\u0142\u0105\u017a <em>master<\/em> b\u0119dzie zawiera\u0142a zmiany ze zdalnego repozytorium:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/11_fetch-after-git.png\"><img decoding=\"async\" width=\"300\" height=\"137\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/11_fetch-after-git-300x137.png\" alt=\"\" class=\"wp-image-10040\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/11_fetch-after-git-300x137.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/01\/11_fetch-after-git.png 650w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Git pull<\/h3>\n\n\n\n<p>Pobieranie zmian w ten spos\u00f3b wymaga ka\u017cdorazowego wykonywania kilku polece\u0144. Git udost\u0119pnia jednak dodatkow\u0105 komend\u0119, kt\u00f3ra \u0142\u0105czy w sobie powy\u017csze polecenia. Komenda <code>git pull<\/code> najpierw pobiera commity ze zdalnego repozytorium i umieszcza je w ga\u0142\u0119ziach zdalnych (<em>fetch<\/em>), a nast\u0119pnie aktualizuje bie\u017c\u0105c\u0105 ga\u0142\u0105\u017a lokaln\u0105, by r\u00f3wnie\u017c zawiera\u0142a te commity (<em>merge<\/em>). Jest to u\u0142atwienie dla u\u017cytkownik\u00f3w, by mogli sprawniej pobiera\u0107 zmiany z repozytorium zdalnego.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowanie<\/h2>\n\n\n\n<p>Przyjrzeli\u015bmy si\u0119 dok\u0142adniej funkcjonalno\u015bci oferowanej przez Gita. Poznali\u015bmy sporo mechanizm\u00f3w, jakimi si\u0119 pos\u0142uguje. Dowiedzieli\u015bmy si\u0119 w jaki spos\u00f3b przechowywane s\u0105 commity i zmiany w plikach projektowych. Przyjrzeli\u015bmy si\u0119 r\u00f3wnie\u017c wykorzystywanym strukturom danych, m.in. plikom <em>tree <\/em>i <em>blob<\/em>. Poza tym zdefiniowali\u015bmy precyzyjnie, czym s\u0105 ga\u0142\u0119zie i zobaczyli\u015bmy, jak Git wykonuje na nich operacje. Wyja\u015bnili\u015bmy tak\u017ce r\u00f3\u017cnice w dzia\u0142aniu <em>merge <\/em>i <em>rebase<\/em>.<\/p>\n\n\n\n<p>Wszystko to jest wiedz\u0105, kt\u00f3ra <strong>wykracza poza podstawow\u0105 znajomo\u015b\u0107 Gita<\/strong>. Nabyte umiej\u0119tno\u015bci sprawdz\u0105 si\u0119 przy codziennej pracy i pozwol\u0105 znacznie pewniej i swobodniej pos\u0142ugiwa\u0107 si\u0119 poleceniami Gita. Pomog\u0105 r\u00f3wnie\u017c w zrozumieniu trudniejszych poj\u0119\u0107, je\u015bli zajdzie taka potrzeba. My\u015bl\u0119, \u017ce naprawd\u0119 warto mie\u0107 \u015bwiadomo\u015b\u0107, jakie operacje Git wykonuje za nas automatycznie i jakie rodzi to konsekwencje. Dzi\u0119ki temu b\u0119dziemy wiedzieli, jakie rezultaty przynios\u0105 nasze dzia\u0142ania.<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;9950&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;28&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;4.4&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;4.4\\\/5 ( votes: 28)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Jak Git dzia\u0142a za kulisami&quot;,&quot;width&quot;:&quot;122.1&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 122.1px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            4.4\/5 ( votes: 28)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Git jest narz\u0119dziem niezwykle pomocnym przy codziennej pracy w zespole. Chc\u0105c nauczy\u0107 si\u0119 jego obs\u0142ugi, si\u0119gamy po przer\u00f3\u017cne kursy. Cz\u0119sto &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/jak-git-dziala-za-kulisami\/\">Continued<\/a><\/p>\n","protected":false},"author":265,"featured_media":15240,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[452,454,455],"class_list":["post-9950","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-git","tag-merge","tag-rebase"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/03\/BlogersiiCovery-github.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/9950"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/265"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=9950"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/9950\/revisions"}],"predecessor-version":[{"id":20608,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/9950\/revisions\/20608"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/15240"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=9950"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=9950"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=9950"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}