Git – poradnik początkującego
Podstawy Gita dla początkujących użytkowników. Jeśli znasz SVN-a i zaczynasz pracę z Gitem, to właśnie coś dla Ciebie. Jeśli nie znasz SVN, to chyba nawet lepiej – unikniesz problemów z zastanawianiem się dlaczego Git działa inaczej.
Spis treści
- Kilka słów wstępu o Git
- Dlaczego Git jest lepszy od SVN?
- Pierwsze kroki z Gitem
- Zatwierdzanie zmian
- Synchronizacja z repozytorium zdalnym
- Praca z gałęziami (branch) (strona 2)
- Naprawianie błędów w obsłudze Gita, czyli tzw. undo (strona 2)
- Obsługa SVN-a przy pomocy Gita (strona 2)
- Kilka przydatnych informacji na koniec (strona 2)
Kilka słów wstępu o Git
Jak pewnie wiesz, Git to system kontroli wersji, będący wolnym oprogramowaniem o otwartym kodzie. Projektowany był z myślą o jednoczesnej pracy wielu ludzi nad jednym kodem. Dlaczego powstał? Ponieważ do rozwoju kodu jądra Linuksa wszystkie istniejące (darmowe) systemy kontroli wersji były niewystarczające. Dlatego też Linus Torvalds rozpoczął pracę nad systemem, który miał być rozproszony, szybki, miał chronić przed błędami w repozytorium i nie posiadać wad CVS-a. W ten oto sposób w roku 2005 powstał Git.
Dlaczego Git jest lepszy od SVN?
Otóż… nie jest. Zapewne wiele razy usłyszysz że jest, bo nie wymaga połączenia do sieci, jest szybszy, czy lepiej radzi sobie z merge’owaniem. To częściowo prawda, ale przy pewnych założeniach nie zauważysz tych zalet Gita.
Połączenie do sieci
Git takiego nie wymaga. Możesz mieć repozytorium lokalne i w nim commitować swoje zmiany. Ale jeśli chcesz żeby ktoś inny z nich skorzystał, to i tak musisz zrobić pusha. Faktem jest, że lokalne commity mogą pomóc jeśli np. chcesz się cofnąć do wcześniejszej wersji, a pusha robisz tylko mając skończony jakiś kawałek kodu. Ale z drugiej strony, dla tych którzy zaczynają pracę z systemem kontroli wersji, SVN będzie zdecydowanie łatwiejszy do ogarnięcia, bo jego podstawowe użycie jest zdecydowanie prostsze.
Poza tym… czy w dzisiejszych czasach dostęp do sieci jest jakimś problemem? :)
Git jest szybszy
Nie da się ukryć. Jeśli masz duże projekty, to faktycznie możesz odczuć różnicę. Przy małych jej na pewno nie zauważysz.
Merge w Git jest łatwy
I tak i nie. Ze względu na inny sposób obsługi branchy, Git sobie całkiem ładnie radzi z merge’owaniem (choć zdarzyło mi się że zrobił psikusa). Jeśli chodzi jednak o SVN-a, we wszystkich przypadkach z którymi się spotykałem z problemami przy merge’u, przyczyna wychodziła po krótkiej rozmowie – „a bo merge najlepiej robić lokalnie na plikach, tak jest najszybciej”. Jak używasz SVN-a w niewłaściwy sposób, to nie narzekaj. Od tego jest polecenie svn merge
i opcja --reintegrate
, żeby ich używać. I merge w SVN na prawdę nie boli. Jest jednak jeden trik – merge w SVN będzie bezbolesny, pod warunkiem że nie będziesz potrzebował go wykonywać pomiędzy różnymi branchami, jedynie „w kierunkach” trunk->branch i odwrotnie.
Co jest lepszego w SVN?
Na przykład możliwość ściągnięcia pojedynczego podkatalogu projektu. Git na coś takiego nie pozwala – musisz zrobić clone całego projektu.
Numerowanie rewizji też jest prostsze – to kolejne liczby. Może rzadko się przydaje, ale dla początkujących może mieć znaczenie.
Fakt, że każdy commit w SVN leci do zdalnego repozytorium też niekoniecznie jest wadą. Wielokrotnie zdarzyło mi się, że ktoś zapomniał zrobić pusha (choć commity robił), a potem zastał go urlop/L4. Jak w SVN robisz commit, to inni mogą zobaczyć Twoje zmiany od razu.
Zalety Gita
Nie twierdzę że SVN jest równie dobry, po prostu jest inny. Jakie zatem są główne zalety Gita?
- jest rozproszony, więc commit nie znajdzie się od razu w zdalnym repozytorium i zanim zrobisz pusha, tylko Ty go widzisz
- w zasadzie z lokalnej kopii projektu (o ile jest aktualna) można odzyskać całą historię zmian, jeśli np. serwer zdalny ulegnie awarii
- cały projekt z wszystkimi branchami zajmie Ci na dysku znacznie mniej, niż w przypadku SVN (dla SVN każdy branch to pełna kopia projektu głównego)
- przy dużych projektach będziesz w stanie zauważyć różnicę w szybkości działania
- nie potrzebujesz zdalnego repozytorium, żeby pracować z Gitem. Wystarczy odpalić
git init
i już masz lokalnie kontrolę wersji - zrobienie diffa z jakąś historyczną wersją projektu działa lokalnie, więc jest o wiele szybsze niż np. w SVN
Przy pewnych założeniach różnicy nie zauważysz
Jeśli wykorzystujesz repozytorium prywatnie i nikt poza Tobą tam nie zagląda, lub jeśli przy projekcie pracuje maksymalnie kilku programistów, poza tym nie potrzebujesz robić merge’y pomiędzy branchami, to z największych zalet Gita nie skorzystasz. To właśnie możliwość robienia merge z każdego miejsca w każde (im więcej ludzi pracuje w projekcie, tym więcej jest branchy które trzeba wcześniej czy później merge’ować, również pomiędzy sobą) jest największą zaletą Gita. Zatem im mniejszy projekt i im mniejsza ilość ludzi pracujących przy nim jednocześnie – tym mniej zalet będziesz widział.
Pierwsze kroki z Gitem
Jedna drobna uwaga dla tych, którzy nigdy nie korzystali z żadnego systemu kontroli wersji. Jeśli zastanawiacie się czy ma sens zapoznanie się z SVNem, a potem z Gitem, to ze swojej strony mogę powiedzieć że zdecydowanie nie. Za dużo zostaje „naleciałości” z SVNa i niektóre rzeczy ciężej będzie zrozumieć.
Tworzenie nowego repozytorium
Jeśli korzystasz np. z GitHuba (lub podobnego serwisu oferującego Gita), możesz z jego poziomu stworzyć nowe repozytorium i sklonować lokalnie na dysku przez polecenie git clone https://github.com/<twój-login>/test.git
(możesz na końcu dodać parametr wskazujący katalog, do którego zostanie sklonowany projekt, czyli git clone <adres-repo> katalog
). Teraz możesz pracować na plikach i synchronizować swoją wersję z wersją zdalną (oczywiście w obydwie strony).
Alternatywnie możesz zacząć pracować lokalnie (uruchomienie w katalogu projektu polecenia git init
). Będziesz mógł korzystać lokalnie z wersjonowania plików. Aby zsynchronizować projekt do zdalnego repozytorium, musisz założyć puste repozytorium (np. we wspomnianym GitHubie), a następnie lokalnie uruchomić polecenie git remote add test https://github.com/<twój-login>/test.git
, a następnie zsynchronizować lokalny projekt do zdalnego repozytorium przez git push origin master
.
Dodawanie, usuwanie i modyfikacja plików
Aby dodać plik, musisz go oczywiście stworzyć. Dodawanie istniejącego pliku do repozytorium wykonuje się poleceniem git add <plik>
. Aby usunąć plik istniejący w repozytorium, należy użyć polecenia git rm <plik>
. Po zmodyfikowaniu pliku nie musisz wykonywać żadnych poleceń, przy commicie wystarczy parametr aby pliki zmodyfikowane, dodane i usunięte automatycznie znalazły się w commicie.
Aby zobaczyć jakie dodane, usunięte i zmodyfikowane pliki oczekują na zatwierdzenie (commit), oraz zobaczyć jakie pliki nie zostały dodane do repozytorium lokalnie, użyj polecenia git status
, lub git status -
s. To drugie wyświetla status w wersji short, czyli znacznie skróconej. Zwykle wystarcza.
Jeśli chcesz sprawdzić, jakie zmiany zostały wykonane na plikach które chcesz zatwierdzać (commitować), użyj polecenia git diff
(jeśli chcesz zobaczyć zmiany we wszystkich plikach), lub git diff <plik>
(zmiany w jednym wybranym pliku).
Zatwierdzanie zmian
Dochodzimy do tego, co właściwie daje nam system kontroli wersji. Otóż daje nam możliwość robienia pełnych kopii zapasowych tego co masz lokalnie w projekcie (w dowolnym momencie będziesz mógł uzyskać konkretną kopię sprzed kilku minut, godzin, dni czy lat). Aby po zmianach w plikach zrobić nową kopię (można powiedzieć – rewizję aktualnej wersji Twojego projektu), należy użyć polecenia git commit -a
. Parametr -a powoduje, że pliki zmodyfikowane, dodane (przez git add) i usunięte (przez git rm) będą automatycznie uwzględnione w tej rewizji. W otwartym edytorze należy wpisać komentarz zmian, które zostały wykonane w stosunku do poprzedniej rewizji (poprzedniego wykonania commita).
Jak często robić commit?
Nie należy robić commita po każdej zmianie w pliku, ale z drugiej strony nie należy również robić raz dziennie (czy rzadziej). Najlepiej robić commity, mając spójną wersję projektu, w pełni działającą, z uwzględnionymi testami do zmienionych i dodanych funkcjonalności. W ten sposób jeśli w dowolnej chwili będziesz zmuszony powrócić do jakiejś wersji wcześniejszej, nie trzeba będzie kombinować z dodatkowymi modyfikacjami, aby projekt działał poprawnie.
Co dokładnie robi commit
Commit, tak jak wspomniałem, zapisuje pod identyfikatorem nowej rewizji aktualną wersję projektu, oczywiście z uwzględnieniem jedynie tych plików, które dodałeś do repozytorium (git add). Pozostałe pliki będą ignorowane, co zobaczysz w edytorze, wpisując komentarz do commita.
W przeciwieństwie do SVN, polecenie git commit nie wysyła nic do zdalnego repozytorium. Commit zapisuje rewizję jedynie lokalnie w katalogu Twojego projektu. Od tego momentu będziesz mógł w dowolnej chwili zobaczyć różnicę aktualnej wersji projektu (lub np. pojedynczego pliku) względem jakiejś wersji (rewizji) historycznej. Jednak nikt inny nie będzie mógł zobaczyć Twoich zmian. Co więcej, jeśli masz sklonowany projekt w innym katalogu, tam również nie zobaczysz tych zmian.
Synchronizacja z repozytorium zdalnym
Aby dokonane wcześniej zmiany które zostały „zacommitowane” (nie wiem czy ten neologizm wejdzie do języka polskiego, ale jego powszechne użycie powoduje że wszelkie zamienniki wydają się obce) trafiły do zdalnego repozytorium, należy je tam wysłać przy pomocy polecenia git push
. Jeśli chcesz wcześniej sprawdzić, jakie zmiany zostaną wysłane, możesz użyć polecenia git diff master origin/master
(zakładając, że pracujesz na gałęzi master
).
Oczywiście może się zdarzyć, że ktoś inny wysłał do zdalnego repozytorium swoje zmiany. Aby być na bieżąco z projektem, warto w miarę często (oczywiście zależnie od tego, jak często inni wykonują zmiany w projekcie) aktualizować projekt danymi ze zdalnego repozytorium. W wersji prostej (i w 99% przypadków wystarczającej) wystarczy co jakiś czas wykonać git pull
. Wersja dla zaawansowanych to git fetch
a następnie git merge
. W zasadzie opcja pull robi fetch+merge (dlatego w większości wypadków wystarczy Ci pull), jednak po tym jak zrobisz fetch (samo fetch nie zmienia Twoich plików, ściąga zmiany jedynie do podkatalogu .git), ale zanim zrobisz merge, możesz sprawdzić jakie zmiany zostały ściągnięte ze zdalnego repozytorium poleceniem git diff origin/master
. Następnie możesz wykonać git merge origin/master
(cały czas zakładam, że dalej pracujesz na stworzonej przez siebie gałęzi master). Dość dogłębnie temat pull vs fetch+merge został opisany na pewnym blogu (po angielsku).
Konflikty przy pull i merge
Oczywiście może się zdarzyć, że przy wykonaniu pull (lub po wykonaniu fetch + merge) otrzymasz komunikat Automatic merge failed; fix conflicts and then commit the result. Oznacza to, że Twoje zmiany powodują konflikt z tymi, które wykonał ktoś inny. Musisz wtedy otworzyć dany plik, wykonać ręcznie modyfikacje, a następnie wykonać git add <plik>
i najlepiej od razu git commit
. Opcją drugą jest wywołanie git mergetool
, które może ułatwić wykonanie zmian (będzie to mniej „ręczne”).
Jeśli przed wykonaniem merge wiesz jakie będą konflikty (bo np. zrobiłeś fetch a potem diff origin/master), możesz podać parametr powodujący, że konflikty będą rozwiązywane przyjmując wersję którą masz lokalnie (git merge -s ours origin/master
) lub przyjmując wersję ściągniętą ze zdalnego repozytorium (git merge -s theirs origin/master
).
Pamiętaj: fakt, że git lepiej sobie radzi z wykonywaniem merge, nie powoduje że nigdy nie będziesz miał konfliktów. Po prostu musisz się nauczyć jak sobie z nimi radzić.