Kurs Git - Pierwsze kroki

Tu jesteś
Java - Mapa kariery Tu jesteś 1 Java Wzorce projektowe Eclipse / IntelliJ Git 2 Maven 3 SQL
Relacyjne bazy danych niezmiennie od lat
są podstawą w budowie systemów
informatycznych. Na rynku istnieją
oczywiście również bazy typu NoSQL,
ale te zwykle są stosowane do
dedykowanych rozwiązań, jak na przykład
przechowywanie ogromnych ilości
danych w celu szybkiego przeszukiwania.
Wtajemniczeni programiści doskonalne rozumieją co kryje się pod pojęciem Git. Niemniej zawsze można o czymś zapomnieć lub stracić pewność, czy podstawy które kiedyś poznaliśmy, zostały przez nas dobrze zrozumiane. Właśnie dlatego stworzyliśmy sekcję Pierwsze kroki. Na początek proponujemy kilka słów wstępu dla początkujących, a następnie przejdziemy do najważniejszych pojęć związanych z Gitem.

Starter Git dla początkujących

Kod większości projektów informatycznych tworzony jest przez wieloosobową grupę programistów. To sprawia, że w tym samym czasie, na różnych komputerach dochodzi do zmian w plikach projektu. Bardzo często równolegle zmieniany może być ten sam plik. Mamy na przykład klasę Item, w której jeden programista dodaje metodę getName, a drugi programista usuwa metodę getAlias. Po dokonaniu zmian programiści wrzucają (innymi słowy komitują) swoje wersje klas do jednego (wspólnego) miejsca, w którym kod ten jest przechowywany. Miejsce to nazywamy repozytorium. Biorąc pod uwagę fakt, że wszystkie zmiany muszą być poprawnie zapisane, potrzebny jest nam system zarządzania takimi zmianami.

W tym celu stworzone zostały systemy kontroli wersji. Nadzorują one zmiany wykonywane przez różne osoby, śledzą kolejność tych zmian, a także zapamiętują ich historię. Przez lata najpopularniejszym rozwiązaniem w tej materii był SVN, znany również pod nazwą Apache Subversion. Jego charakterystyczną cechą było to, że posiadał on jedno centralne repozytorium, znajdujące się na jednym serwerze.

Z czasem wymagania w stosunku do tego rodzaju rozwiązań zaczęły rosnąć, co zaoowocowało popularyzacją systemu Git. Największa zaletą Gita w porównianiu do SVN-a jest to, że jest on rozproszonym repozytorium kodu źródłowego. W rzeczywistości mamy więc tutaj do czynienia z połączeniem zarówno repozytorium zdalnego jak i repozytoriów lokalnych, przechowywanych na komputerach programistów. Innymi słowy każdy programista przechowuje kopię zdalnego repozytorium na swojej maszynie, co pozwala na skuteczną pracę offline oraz na łatwe odtworzenie zdalnego repozytorium w razie jego utraty (pomijamy kwestie backupów).
System kontroli wersji
Na powyższym zdjęciu widzimy jak wygląda struktura Git-a stworzona w ramach projektu. Mamy tutaj katalog projektu, w którym Git stworzył podkatalog .git. To w nim przechowywane są faktyczne pliki i katalogi tworzące repozytorium lokalne. Oprócz tego katalog ten zawiera również przechowalnie plików (staging area). Trafiają tam pliki, które zostały stworzone przez nas w ramach projektu i które zamierzamy dodać do lokalnego repozytorium. Staging area stanowi więc swego rodzaju poczekalnię dla plików, zanim zostaną one przez nas skomitowane do repozytorium.

Na koniec możemy zauważyć, że pliki, które znajdują się w lokalnym repozytorium, mogą zostać przez nas wypchnięte (push) do repozytorium na serwerze. Proces ten oczywiście może być wykonany w przeciwnym kierunku. Możemy pociągnąć (fetch lub pull) wszystkie zmiany w kodzie, które zostały wykonane przez innych programistów. Zmieniony kod zostaje wprowadzony do naszego repozytorium lub nawet do naszego katalogu z projektem (szczegóły przedstawiamy w dalszej części rozdziału).

Git - Konsola vs interfejs graficzny

Z Git-a możemy korzystać zarówno z poziomu konsoli, jak również za pomocą programów z interfejsem graficznym. Programów tego typu jest wiele, ale my polecamy w szczególności narzędzie TortoiseGit (link na dole strony). Używamy go od lat i nigdy nas nie zawiodło.
TortoiseGit
Z drugiej strony wielu programistów pracuje dzisiaj na Mac-ach lub komputerach z Linuxem, więc mając na uwadze lepszą współpracę w ramach teamu, warto przynajmniej choć trochę znać polecenia w formie konsolowej. Git dostarcza naprawdę znaczną liczbę tych komend. Ich listę można przejrzeć uruchamiając jedną z podstawowych komend:
git help

Klonowanie czyli pobieranie projektu

Na początku pracy z projektem tworzone jest repozytorium kodu w Gicie. Wykonuje to zwykle jedna osoba w zespole. Pozostali programiści zaczynają pracę nad projektem pobierając to repozytorium na swoją lokalną maszynę. Możemy powiedzieć, że kod jest klonowany. W wyniku tej operacji w naszym systemie plików tworzony jest katalog projektu oraz dodatkowy podkatalog o nazwie .git (podobnie jak na naszym zdjęciu w paragrafie Starter dla początkujących).
git clone <adres pliku repozytorium>
Jeśli chcemy sklonować projekt z repozytorium na Githubie, znajdujący się pod adresem: https://github.com/javappa-coding/postspringboot, podajemy pełną ścieżkę do pliku (.git):
git clone https://github.com/javappa-coding/postspringboot.git
Tak jak wspomnieliśmy, w tak pobranym projekcie zawsze znajdziemy katalog .git, w którym przechowywane są pliki z obiektami Gita.
Projekt na lokalnym komputerze

Pierwszy krok - Repozytorium lokalne

Praca z plikami projektu opiera się zawsze na podobnych mechanizmach. W dużym uproszczeniu można powiedzieć, że programiści wykonują zadania na lokalnych komputerach oraz synchronizują swoje zmiany ze zmianami wykonywanymi przez innych programistów. Wszystkie możliwe scenariusze takiej pracy są realizowane za pośrednictwem komend Gita, których wykonywanie tworzy tzw. przepływ pracy, popularnie zwany jako Git Workflow.

Scenariusz 1

Zobaczmy teraz jak wygląda najprostszy, typowy scenariusz realizowany w pracy z Gitem. Polega na stworzeniu pliku i dodaniu go do lokalnego repozytorium:
  • Programista tworzy plik ItemService.java oraz uruchamia komendę, która pokazuje co teraz Git wie o projekcie:
    git status
    
    Po uruchomieniu komendy Git zwróci nam informacje o tym, że mamy jeden stworzony plik, który nie jest jeszcze przez niego śledzony.
    Untracked files:
    ...
      ItemService.java
    
    nothing added to commit but untracked files present (use "git add" to track)
    
    Oczekiwanym działaniem będzie więc dodanie tego pliku do "poczekalni" plików Gita.
  • Programista dodaje ten plik do "poczekalni" (staging area):
    git add ItemService.java
    
    Po wykonaniu komendy git status Git zwróci informację o tym, że plik jest obecnie przechowywany i że jest gotowy do wrzucenia (skomitowania) do repozytorium:
    Changes to be comitted:
    ...
      new File:     ItemService.java
    
  • Programista wrzuca (komituje) zmiany do lokalnego repozytorium ma swoim komputerze:
    git commit -m "Add ItemService"
    
    Opcja -m jest bardzo ważna. Dodajemy ją zawsze, aby wraz z komitem została zapisana informacja o tym czego dotyczy ten komit. Po wykonaniu komendy git status Git zwróci informację o tym, że nie ma obecnie żadnych zmian do skomitowania:
    ...
    nothing to commit (working directory clean)
    
    Plik znajduje się więc obecnie w lokalnym repozytorium w jego głównej gałęzi (branch) o nazwie master. W momencie komitowania zawsze wykonywana jest migawka tej operacji (snapshot). Git odnotowuje ją na osi czasu (timeline), dzięki czemu w przyszłości programista będzie mógł wrócić do wersji kodu pochodzącej dokładnie z tego czasu.

Scenariusz 2

Kolejny scenariusz polega na zmodyfikowaniu pliku i dodaniu kolejnego:
  • Programista modyfikuje plik ItemService.java oraz tworzy nowy plik ItemServiceImpl.java. Następnie uruchamia komendę, która pokazuje co teraz Git wie o projekcie:
    git status
    
    Po uruchomieniu komendy Git zwróci nam informacje o tym, że mamy jeden stworzony plik, który nie jest jeszcze przez niego śledzony, oraz że mamy jeden plik zmieniony, ale zmiana ta nie została odnotowana przez Gita:
    Changed but not updated:
    
      modified: ItemService
    
    Untracked files:
    ...
      ItemServiceImpl.java
    
    nothing changes added to commit
    
  • Programista dodaje oba zmienione pliki do "poczekalni" (staging area).
    git add --all
    
    Po wykonaniu komendy git status Git zwróci informację o tym, że oba pliki są obecnie przechowywane i że są gotowe do wrzucenia (skomitowania) do repozytorium:
    Changes to be comitted:
    ...
      new File:     ItemServiceImpl.java
      modified:     ItemService.java
    
  • Programista wrzuca (komituje) zmiany do lokalnego repozytorium ma swoim komputerze.
    git commit -m "Add new ItemServiceImpl and complete existing ItemService"
    
    Po wykonaniu komendy git status Git zwróci informację o tym, że nie ma obecnie żadnych zmian do skomitowania:
    ...
    nothing to commit (working directory clean)
    
    Podobnie jak w scenariuszu pierwszym pliki znajdują się obecnie w lokalnym repozytorium w jego głównej gałęzi (branch) o nazwie master. W momencie komitowania wykonana została migawka operacji (snapshot). Git odnotował ją na osi czasu (timeline) - na samej górze (ponad poprzednimi zmianami), dzięki czemu w przyszłości programista będzie mógł wrócić do tego miejsca w czasie.
W trakcie realizacji powyższych scenariuszy pliki zostały ostatecznie skomitowane do lokalnego repozytorium, co zostało skrupulatnie odnotowane na osi czasu. Innymi słowy stworzona została historia wykonanych komitów.

Drugi krok - Przeglądanie historii

Prawdopodobnie jedną z najczęściej używanych funkcjonalności Gita jest przeglądanie historii komitów. Używamy tej funkcjonalności najczęściej po to aby zobaczyć kiedy i jak zmieniały się pliki, które są dla nas w danym momencie ważne:
git log
Po uruchomieniu komendy, Git zwróci nam informacje o wykonanych komitach prezentując ich autora, datę oraz wiadomość podaną przez nas (opcja -m) podczas wykonywania komitu.
commit 3dhjxsa263hhas87dsdhj89sdsdsds9...
Author: Jan Kowalski <kowalski.jan@admin.javappa.com>
Date:   Mon Jan 21 11:15:22 2019

   Add new ItemServiceImpl and complete existing ItemService
    
commit 6jsd89sjksd235jkabgder25253jea1...
Author: Jan Kowalski <kowalski.jan@admin.javappa.com>
Date:   Mon Jan 21 11:00:22 2019

   Add new ItemService
Kolejność jest przedstawiana odwrotnie do wykonywanych zapisów, więc najpierw pojawiają się komity "najmłodsze", czyli te które są na szczycie osi czasu. Dodatkowo każdy komit ma swój unikalny identyfikator SHA-1 (ciąg literowo cyfrowy), którym w przyszłości możemy się posługiwać, gdy będziemy chcieli się do takiego komita odwołać.

Trzeci krok - Repozytorium zdalne

Do tej pory pokazaliśmy jak można pracować na lokalnym repozytorium. Teraz przyszedł czas na podzielenie się naszym kodem z innymi. W tym celu wykorzystamy kilka komend, które pozwolą nam na komunikację ze zdalnym serwerem.
  • Programista wrzuca na zdalne repozytorium wszystkie zmiany, które skomitował wcześniej do swojego lokalnego repozytorium.
    git push
    
  • Inny programista pobiera wrzucone zmiany ze zdalnego repozytorium. Zmiany te ściągają się do jego lokalnego repozytorium oraz do w lokalizacji, w której znajduje się jego projekt (working directory).
    git pull
    
    Jeśli zależy nam tylko na synchronizacji zmian między zdalnym i lokalnym repozytorium (bez modyfikowania working directory), wówczas używamy komendy:
    git fetch
    
    Natomiast jeśli później chcemy te zmiany wgrać do projektu z naszego lokalnego repozytorium, wówczas używamy komendy:
    git merge
    
    Wywołanie pull jest pewnego rodzaju skrótem, który odpala zarówno fetch jak i merge.
Repozytorium zdalne posiada swoją nazwę, która jest definiowana na etapie jego tworzenia. Nazwa ta może być dowolna, ale najczęściej stosowana jest nazwa origin. Tak samo jak na repozytorium lokalnym, tak samo na repozytorium zdalnym wszystkie zmiany są przechowywane w jego konkretnej gałęzi (branch). Zwyczajowo nosi ona nazwę master, jednak mogą oczywiście istnieć branche o innych nazwach.

Podsumowanie - Git Workflow

Na koniec podsumujmy wszystkie wykonane operacje. Zgodnie z prawdą, że jeden obraz jest wart więcej niż tysiąc słów obejrzyjmy omówiony przepływ danych (workflow) na poniższym obrazku:
Git workflow
Oczywiście użycie komendy git merge nie musi być zawsze bezpośrednią konsekwencją wywołania komendy git fetch. Bardzo często wykorzystujemy merge niezależnie, na przykład gdy mamy dwa branche lokalne i chcemy pobrać zmiany z jednego brancha do drugiego. Pytanie jakie należy sobie w tym momencie zadać to w jaki sposób można się przełączać między branchami. W tym celu wywołujemy komendę:
git checkout <nazwa brancha>
To wszystko co chcieliśmy Wam przekazać w zakresie wiedzy początkowej. Więcej napiszemy w niedalekiej przyszłości, kiedy to planujemy omówić szerzej temat branchy oraz przygotować opis kolejnych komend (np. komendy porównującej kod w working directory z kodem w lokalnym repozytorium). Linki
https://git-scm.com
https://tortoisegit.org
https://www.atlassian.com/git

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy:
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .