Git – podstawowe narzędzie pracy dewelopera i nie tylko. Część I. Trudne początki

Git

Trochę historii

Dawno, dawno temu, za siedmioma mainframe'ami, za siedmioma terminalami, a przed siedmioma monitorami pracowali programiści. Już w tych ciemnych wiekach, kiedy nie było jeszcze internetu, Facebooka i wielu innych wynalazków programiści, dziś zwani dumnie deweloperami, musieli współpracować razem. Chcieli osiągnąć ambitny cel, jakim było, i jest do dziś, wytworzenie działającego oprogramowania. Każde z tych przedsięwzięć borykało się z tym samym problemem – synchronizacją pracy nad kodem źródłowym. Wyobraźmy sobie, że na początku świat posiadał wtedy tylko siedmiu programistów, którzy musieli synchronizować swój kod miedzy sobą. Ilość sposobów, w jakie musieli się komunikować w trójkach, bez użycia zewnętrznych narzędzi, wynosił 7 po 3. Obliczenia te zakładają, że istnieje tylko jeden kanał komunikacji. Osoba odpowiedzialna za kontrole wersje kodu musiałaby kontrolować aż 21 kanałów przepływu informacji w zespole. Synchronizacja pracy w zespołach znacznie mniejszych, jak np. 3 osoby (wówczas mamy całe 3 analogiczne kanały komunikacji) bywa trudna. Idealna kontrola 21 kanałów wytwarzania oprogramowania jest zajęciem prawdopodobnie niemożliwym do wykonania, a na pewno wyjątkowo nudnym i wypalającym.

Innym aspektem była możliwość skasowania działającego kodu. Wyobraźmy sobie programistę, który stworzył działającą funkcję wykonującą zadany kod. Ze względu na stopień skomplikowania zajęła mu ona np. 2 dni. Miała ponad 300 linii skomplikowanego kodu. Przed podzieleniem się swoim osiągnięciem z resztą zespołu, postanowił ją jednak zrefaktorować. W tym celu rozbił ją na mniejsze funkcje, zmienił nazwy zmiennych na bardziej opisowe, skomentował trudniejsze fragmenty itp. Po ponad 2 godzinach pracy postanowił znów skompilować i uruchomić program. Kilka niewinnie wyglądających ostrzeżeń kompilatora i forma binarna była gotowa. Po wpisaniu do terminala jakże skomplikowanej instrukcji ./bardzo_wazny_program.bin, otrzymał uwielbiany przez programistów komunikat:

Segmentation fault

Niestety nasz programista po dwóch dniach pracy zapomniał skopiować pliki projektu. Czekało go więc pisanie funkcji od nowa lub debugowanie programu. W przykładzie tym złamanych zostało co najmniej kilka dobrych praktyk wytwarzania oprogramowania.

Jako że natura nie lubi próżni, a potrzeba jest matką nie tylko wynalazków, ale także programów, to dość szybko powstały systemy kontroli wersji. Wbrew powszechnemu przekonaniu, pierwsze programy, które miały w sobie pierwiastek kontrolowania wersji pliku, nie były pisane pod kod źródłowy, lecz pod aktualizacje oprogramowania dla sprzętu.

Początek systemu kontroli wersji kodu źródłowego należy datować na rok 1972. Wtedy Marc Rochkind pracujący w Bell Labs (został tam opracowany między innymi pierwszy tranzystor, pierwszy laser, język programowania C, system Unix, język programowania C++, czy pierwszy system kontroli wersji, o którym właśnie piszę 😉 ) napisał SCCS – Source Code Control System. Projekt ten istnieje do dziś i możemy go znaleźć pod adresem: http://sccs.sourceforge.net/ . SCCS stworzył całą gałąź inżynierii oprogramowania. Z czasem pojawiały się coraz lepsze (czasem też gorsze) narzędzia ułatwiające zarządzanie kodem źródłowym.

Podział systemów kontroli wersji ze względu na architekturę

Systemy kontroli wersji dzielimy na:

  1. lokalne
  2. scentralizowane
  3. rozproszone.

Lokalne systemy kontroli wersji:

Są one najpowszechniejsze wśród osób nieznających profesjonalnych narzędzi. Najprostszym lokalnym systemem kontroli wersji jest tworzenie folderów lub plików archiwów (zip, tar) z zadaną wersją plików. Podejście takie jest również często stosowane do tworzenia lokalnych kopii zapasowych. Przykładową komendą, która tworzy nam takie archiwum, może być: tar --bzip2 -cvf wazny_program.$(date +%F).tar.bz wazny_program. Używając takiego rozwiązania, łatwo jest jednak popełnić błąd. Wystarczy być w złym folderze, pomylić pliki lub je skasować. By zaradzić temu problemowi, powstały programy takie, jak SCCS. Posiadały one lokalną bazę danych z numerami wersji. Do dziś systemy tego typu są używane, a przykładem może być program rcs, który znajduje się w EuroLinuxie 5, 6 i 7. Program ten utrzymuje patche (łatki – pliki zawierające tylko różnice między wersjami pliku). By dostać plik w najnowszej wersji, system aplikuje wszystkie kolejne łatki aż do najnowszej wesji. Systemy te mają wiele wad, np. synchronizacja między użytkownikami jest stosunkowo uciążliwa. Posiadamy tutaj tzw. single point of failure – SPOF (w luźnym tłumaczeniu pojedynczy punkt awarii). Jeśli dane zostaną uszkodzone, usunięte z lokalnego systemu, to tracimy je na zawsze. Przykładem takich systemów są: lokalne foldery, lokalne pliki archiwów, sccs, rcs. Dlatego też powstały ...

… scentralizowane systemy kontroli wersji

Rozwiązania te starają się rozwiązać kłopot z koniecznością współpracy między ludźmi wytwarzającymi oprogramowanie. Istnieje w nich centralny serwer, do którego wszyscy użytkownicy się odwołują. Jest on zarządzalny, można nadawać odpowiednie uprawnienia, czytać komunikaty innych użytkowników, sprawdzać, które projekty i pliki są aktywnie rozwijane i posiada wiele innych możliwości. Jest to olbrzymia przewaga w stosunku do lokalnych systemów kontroli wersji. Przykładami takich systemów są: CVS i Subversion.

Niestety w tym wypadku posiadanie SPOF jest dużo bardziej odczuwalne. Wiele firm padło ofiarą cyberprzestępców. Posiadanie scentralizowanego systemu kontroli wersji (dodatkowo bez backupu) jest idealną okazją do skutecznego sparaliżowania całego procesu wytwarzania oprogramowania w firmie. Jeśli jednak cyberprzestępczość nam niestraszna, postarajmy się przypomnieć sobie, kiedy po raz ostatni sprawdzaliśmy „zdrowie” naszych dysków twardych (dotyczy to szczególnie dysków SSD), czy odporność naszej sieci i sprzętu na przepięcia i ile razy rzeczywiście odtwarzaliśmy naszą infrastrukturę z backupów. W przecież w skrajnym przypadku możemy bezpowrotnie stracić cały kod, jaki przez lata udało nam się wyprodukować. Dla wielu firm mogłoby to oznaczać koniec działalności. Aby temu zapobiec powstały ...

… rozproszone systemy kontroli wersji

Ta architektura, jako najbardziej dojrzała, jest dzisiaj powszechnie stosowana. Polega ona na tym, że każdy z klientów, który pobiera pliki z systemu kontroli wersji, pobiera też całą historię – jest to tzw. mirror (kopia lustrzana – czyli 1 do 1). Użytkownik, który pobiera i pracuje na plikach z repozytoriów, jest de facto ich backupem danych. Dodatkową zaletą jest fakt, iż możemy posiadać wiele zdalnych repozytoriów (serwerów z plikami). Dla rozproszonych systemów kontroli wersji nie stanowi to żadnego problemu. Przy naprawdę niewielkiej konfiguracji możemy stworzyć osobne serwery repozytoriów dla developerów, testerów, a także klientów, którymi możemy w bardzo elastyczny sposób zarządzać. Przykładami takich systemów są: Git, Merculiar, Bazaar.

Jak powstał Git?

Historia Gita jest bardzo mocno związana z historią wybitnej jednostki, jaką bez wątpienia jest Linus Torvalds. Jądro Linuksa w latach 1991–2002 zarządzane było przy pomocy archiwizacji plików i łatek (pliki patch). W roku 2002 kod źródłowy jądra został przeniesiony do własnościowego (nieotwartego, nie typu Open Source) oprogramowania BitKeeper. W 2005 roku BitKeeper zmienił zasady licencjonowania, w związku z tym developerzy Linuksa musieliby zapłacić za dalsze korzystanie z tego rozwiązania. Nie kto inny, jak sam Linus Torvalds wyszedł z inicjatywą stworzenia rozproszonego systemu kontroli wersji. Skorzystał on przy tym z doświadczenia, jakie zdobył, korzystając z BitKeepera. Jako że Git miał zarządzać bardzo rozległym kodem, jakim jest jądro Linuksa, musiał on być szybki i efektywny. Ponieważ nad Linuksem pracują tysiące developerów, system musiał być perfekcyjnie rozproszony. Wszystkie te cele udało się osiągnąć.

Jako ciekawostkę można dodać, że w tym samym czasie i w dokładnie tym samym celu powstał inny popularny (nie aż tak jak Git) system kontroli wersji Mercurial (mercury w języku angielskim to rtęć). Z linii poleceń program wywołuje się, pisząc hg – czyli symbol rtęci na tablicy Mendelejewa.

Zainstalujmy i skonfigurujmy Gita

Instalacja

Instalacja Gita w Enterprise Linuksie jest prosta. Mając zarejestrowany system, wpisujemy: sudo yum install -y git.

Ze względu na popularność Gita większość dystrybucji oferuje go od razu w repozytoriach. Powszechnie stosowaną praktyką jest włożenie Gita do grup pakietów odpowiedzialnych za wytwarzanie (development) oprogramowania.

Dla przykładu w EuroLinuxie 7.3 komenda:

sudo yum groupinfo development
# Zwróci nam
Group: Development Tools
 Group-Id: development
 Description: A basic development environment.
 Mandatory Packages:
# Wyjście wycięte
 Default Packages:
# Wyjście wycięte
# Git należy do domyślnie instalowanych pakietów.
  +git
# RCS, o którym wspominałem wcześniej, również się tu znalazł
  +rcs
# Subversion także :)
  +subversion
 Optional Packages:
# Wyjście wycięte
# hg także znajduję się w tej grupie – jednak jako pakiet opcjonalny.
 mercurial

Około 15% pakietów będących w grupie pakietów development to pakiety odpowiedzialne za systemy kontroli wersji. Fakt ten udowadnia, jak bardzo niezbędne są to narzędzia w pracy deweloperów.

Konfiguracja

Do konfiguracji Gita używamy komendy: git config. Konfiguracja Gita jest konfiguracją warstwową, tj. posiadamy kilka lokacji, które są zaczytywane. Następne warstwy nadpisują poprzednie. Warstwy, z których Git zaczytuje konfigurację, są następujące:

-- system wskazuje na /etc/gitconfig. Jak nie trudno się domyślić, ustawienie to wymaga z reguły praw roota. git config --system user.name "Imię Nazwisko"nie uda się, jeśli nie posiadamy uprawnień root (korzystając z jego konta lub przez sudo). Dostaniemy typowy komunikat: error: could not lock config file /etc/gitconfig: Permission denied. Następnie zaczytywana jest konfiguracja --global, która znajduje się w ~/.gitconfig lub ~/.config/git/config. Ostatnią lokacją, w której znajdziemy ustawienia Gita, jest samo repozytorium. W tym przypadku nie podajemy żadnego argumentu do komendy konfiguracyjnej. Konfiguracja znajduje się w .git/configw katalogu repozytorium.

By skonfigurować podstawowe informacje na temat naszego użytkownika, wykonujemy dwie komendy:

git config --global user.name "Jan Kowalski"
git config --global user.email "[email protected]"

By zobaczyć nasze ustawiania, używamy komendy:

git config --list

Przydatna jest także komenda:

git var -l

Pokazuje ona zmienne używane w danym miejscu przez Gita.
Możemy w niej na przykład zobaczyć, że domyślnym edytorem dla Gita jest GIT_EDITOR=vi.
Jeśli wolimy ładne kolorki i mamy skonfigurowane wtyczki, dobrym pomysłem może być ustawienie edytora na vim:

git config --global core.editor vim

Co dalej?

Co naturalne, w tym artykule poruszono tylko niektóre kwestie związane z Gitem. Projekt ten posiada swoją oficjalną stronę w internecie https://git-scm.com/. Można tam znaleźć między innymi samouczek (pod linkiem Try Git), który pozwala poznać podstawy Gita w ciągu około 15 - 20 minut. Czytelnik dowie się z niego, czym jest staging area, gałąź, jak dodawać zdalne repozytoria i jak pobierać oraz wypychać do nich zmiany. Jak widać, zaprzyjaźnianie się z Gitem nie wymaga od nas zbyt dużo czasu. Ten etap celowo pominąłem w artykule, gdyż skorzystanie z interaktywnego samouczka jest dużo bardziej efektywne niż przeczytanie o kilku komendach.

Osobom, które chcą dogłębniej poznać Gita, polecam książkę Pro Git. Jej elektroniczna wersja jest darmowa i naprawdę dokładnie opisuje temat. Niestety jest ona w języku angielskim. Na szczęście w ofercie polskich wydawnictw informatycznych również istnieją książki dotyczące Gita. Bardzo dobre recenzje zebrała książka "Git. Rozproszony system kontroli wersji", autorstwa Włodzimierza Gajdy. Serdecznie zapraszam do lektury.